Java设计模式——GoF设计模式
一、GoF設(shè)計模式簡介
目錄
一、GoF設(shè)計模式簡介
1.GoF 的 23 種模式一覽表
2.GoF 的 23 種設(shè)計模式的簡要說明
3.抽象工廠模式
4.建造者模式
5.工廠方法模式
6.原型模式
7.單例模式
8.適配器模式
9.橋接模式
10.組合模式
11.裝飾模式
12.外觀模式
13.享元模式
14.代理模式
15.職責(zé)鏈模式
16.命令模式
17.解釋器模式
18.迭代器模式
19.中介者模式
20.備忘錄模式
21.觀察者模式
22.狀態(tài)模式
23.策略模式
24.模板方法模式
25.訪問者模式
本節(jié)內(nèi)容參考《Java設(shè)計模式》@劉偉 編著,清華大學(xué)出版社出版,設(shè)計模式詳解請參考原書。
1.GoF 的 23 種模式一覽表
| 類模式 | 工廠方法模式 | (類)適配器模式 | 解釋器模式 模板方法模式 |
| 對象模式 | 抽象工廠模式 建造者模式 原型模式 單例模式 | (對象)適配器模式 橋接模式 組合模式 裝飾模式 外觀模式 享元模式 代理模式 | 職責(zé)鏈模式 命令模式 迭代器模式 中介者模式 備忘錄模式 觀察者模式 狀態(tài)模式 策略模式 訪問者模式 |
2.GoF 的 23 種設(shè)計模式的簡要說明
| 創(chuàng)建型模式 Creational Patterns | 抽象工廠模式 Abstract Factory Pattern | 提供一個創(chuàng)建一系列相關(guān)或相互依賴對象的接口,而無須指定它們具體的類 | ★★★★☆ | ★★★★★ |
| 建造者模式 Builder Pattern | 將一個復(fù)雜對象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示 | ★★★★☆ | ★★☆☆☆ | |
| 工廠方法模式 Factory Method Pattern | 定義一個用于創(chuàng)建對象的接口,但是讓子類決定將哪一個類實例化。工廠方法模式讓一個類的實例化延遲到其子類 | ★★☆☆☆ | ★★★★★ | |
| 原型模式 Prototype Pattern | 使用原型實例指定待創(chuàng)建對象的類型,并且通過復(fù)制這個原型來創(chuàng)建新的對象 | ★★★☆☆ | ★★★☆☆ | |
| 單例模式 Singleton Pattern | 確保一個類只有一個實例,并提供一個全局訪問點來訪問這個唯一實例 | ★☆☆☆☆ | ★★★★☆ | |
| 結(jié)構(gòu)型模式 Structural Patterns | 適配器模式 Adapter Pattern | 將一個類的接口轉(zhuǎn)換成客戶希望的另一個接口。適配器模式讓那些接口不兼容的類可以一起工作 | ★★☆☆☆ | ★★★★☆ |
| 橋接模式 Bridge Pattern | 將抽象部分它的實現(xiàn)部分解耦,使得兩者都能夠獨立變化 | ★★★☆☆ | ★★★☆☆ | |
| 組合模式 Composite Pattern | 組合多個對象形成樹形結(jié)構(gòu)以表示焗油部分-整體關(guān)系的層次結(jié)構(gòu)。組合模式讓客戶端可以統(tǒng)一對待單個對象和組合對象 | ★★★☆☆ | ★★★★☆ | |
| 裝飾模式 Decorator Pattern | 動態(tài)地給一個對象增加一些額外的職責(zé)。就擴展功能而言,裝飾模式提供了一種比使用子類更加靈活的替代方案 | ★★★☆☆ | ★★★☆☆ | |
| 外觀模式 Facade Pattern | 為子系統(tǒng)中的一組接口提供一個統(tǒng)一的入口。外觀模式定義了一個高層接口,這個接口使得這一子系統(tǒng)更加容易使用 | ★☆☆☆☆ | ★★★★★ | |
| 享元模式 Flyweight Pattern | 運用共享技術(shù)有效地支持大量細粒度對象的復(fù)用 | ★★★★☆ | ★☆☆☆☆ | |
| 代理模式 Proxy Pattern | 給某一個對象提供一個代理或占位符,并由代理對象來控制對原來對象的訪問 | ★★★☆☆ | ★★★★☆ | |
| 行為型模式 Behavioral Patterns | 職責(zé)鏈模式 Chain of Responsibility Pattern | 避免將一個請求的發(fā)送者與接收者耦合在一起,讓多個對象都有機會處理請求。將接收請求的對象連接成一條鏈,并且沿著這條鏈傳遞請求,直到有一個對象能夠處理它為止 | ★★★☆☆ | ★★☆☆☆ |
| 命令模式 Command Pattern | 將一個請求封裝為一個對象,從而可用不同的請求對客戶進行參數(shù)化,對請求排隊或者記錄請求日志,以及支持可撤銷的操作 | ★★★☆☆ | ★★★★☆ | |
| 解釋器模式 Interpreter Pattern | 給定一個語言,定義它的文法的一種表示,并定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子 | ★★★★★ | ★☆☆☆☆ | |
| 迭代器模式 Iterator Pattern | 提供一種方法順序訪問一個聚合對象中的各個元素,而又不用暴露該對象的內(nèi)部表示 | ★★★☆☆ | ★★★★★ | |
| 中介者模式 Mediator Pattern | 定義一個對象來封裝一系列對象的交互。中介者模式使各對象之間不需要顯示地相互引用,從而使其耦合松散,而且可以獨立地改變它們之間的交互 | ★★★☆☆ | ★★☆☆☆ | |
| 備忘錄模式 Memento Pattern | 在不破壞封裝的前提下捕獲一個對象的內(nèi)部狀態(tài),并在該對象之外保存這個狀態(tài),這樣可以在以后將對象恢復(fù)到原先保存的狀態(tài) | ★★☆☆☆ | ★★☆☆☆ | |
| 觀察者模式 Observer Pattern | 定義對象之間的一種一對多依賴關(guān)系,使得每當(dāng)一個對象狀態(tài)發(fā)生改變時其相關(guān)依賴對象皆得到通知并被自動更新 | ★★★☆☆ | ★★★★★ | |
| 狀態(tài)模式 State Pattern | 允許一個對象在其內(nèi)部狀態(tài)改變時改變它的行為。對象看起來似乎修改了它的類 | ★★★☆☆ | ★★★☆☆ | |
| 策略模式 Strategy Pattern | 定義一系列算法,將每一個算法封裝起來,并讓它們可以相互替換。策略模式讓算法可以獨立于使用它的客戶而變化 | ★☆☆☆☆ | ★★★★☆ | |
| 模板方法模式 Template Method Pattern | 定義一個操作中算法的框架,而將一些步驟延遲到子類中。模板方法模式使得子類可以不改變一個算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟 | ★★☆☆☆ | ★★★☆☆ | |
| 訪問者模式 Visitor Pattern | 表示一個作用于某對象結(jié)構(gòu)中的各個元素的操作。訪問者模式可以在不改變各元素的類的前提下定義作用于這些元素的新操作 | ★★★★☆ | ★☆☆☆☆ |
3.抽象工廠模式
???????抽象工廠模式是工廠方法模式的進一步延伸,由于它提供了功能更為強大的工廠類并且具備較好的可擴展性,在軟件開發(fā)中得以廣泛應(yīng)用,尤其是在一些框架和API類庫的設(shè)計中,例如在Java語言的AWT(抽象窗口工具包)中就使用了抽象工廠模式,它使用抽象工廠模式來實現(xiàn)在不同的操作系統(tǒng)中應(yīng)用程序呈現(xiàn)與所在操作系統(tǒng)一致的外觀界面。抽象工廠模式也是在軟件開發(fā)中最常用的設(shè)計模式之一。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 抽象工廠(AbstractFactory) | 它聲明了一組用于創(chuàng)建一族產(chǎn)品的方法,每一個方法對應(yīng)一種產(chǎn)品。 |
| 具體工廠(ConcreteFactory) | 它實現(xiàn)了在抽象工廠中聲明的創(chuàng)建產(chǎn)品的方法,生成一組具體產(chǎn)品,這些產(chǎn)品構(gòu)成了一個產(chǎn)品族,每一個產(chǎn)品都位于某個產(chǎn)品等級結(jié)構(gòu)中。 |
| 抽象產(chǎn)品(AbstractProduct) | 它為每種產(chǎn)品聲明接口,在抽象產(chǎn)品中聲明了產(chǎn)品所具有的業(yè)務(wù)方法。 |
| 具體產(chǎn)品(ConcreteProduct) | 它定義具體工廠生產(chǎn)的具體產(chǎn)品對象,實現(xiàn)抽象產(chǎn)品接口中聲明的業(yè)務(wù)方法。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 抽象工廠模式隔離了具體類的生成,使得客戶端并不需要知道什么被創(chuàng)建。由于這種隔離,更換一個具體工廠就變得相對容易,所有的具體工廠都實現(xiàn)了抽象工廠中定義的那些公共接口,因此只需要改變具體工廠的實例就可以在某種程度上改變整個軟件系統(tǒng)的行為。 |
| 當(dāng)一個產(chǎn)品族中的多個對象被設(shè)計成一起工作時,它能夠保證客戶端始終只使用同一個產(chǎn)品族中的對象。 | |
| 增加新的產(chǎn)品族很方便,無須修改已有系統(tǒng),符合開閉原則。 | |
| 缺點 | 增加新的產(chǎn)品等級結(jié)構(gòu)麻煩,需要對原有系統(tǒng)進行較大的修改,甚至需要修改抽象層代碼,這顯然會帶來較大的不變,違背了開閉原則。 |
| 適用環(huán)境 | 一個系統(tǒng)不應(yīng)該依賴于產(chǎn)品類實例如何被創(chuàng)建、組合和表達的細節(jié),這對于所有類型的工廠模式都是很重要的,用戶無須關(guān)心對象的創(chuàng)建過程,將對象的創(chuàng)建和適用解耦。 |
| 系統(tǒng)中有多于一個的產(chǎn)品族,而每次只使用其中某一產(chǎn)品族。可以通過配置文件等方式來使用戶能夠動態(tài)改變產(chǎn)品族,也可以很方便地增加新的產(chǎn)品族。 | |
| 屬于同一個產(chǎn)品族的產(chǎn)品將在一起使用,這一約束必須在系統(tǒng)的設(shè)計中體現(xiàn)出來。同一個產(chǎn)品族中的產(chǎn)品可以是沒有任何關(guān)系的對象,但是它們都具有一些共同的約束,如同一操作系統(tǒng)下的按鈕和文本框,按鈕與文本框之間沒有直接關(guān)系,但它們都屬于某一操作系統(tǒng)的,此時具有一個共同的約束條件,及操作系統(tǒng)的類型。 | |
| 產(chǎn)品等級結(jié)構(gòu)穩(wěn)定,在設(shè)計完成之后不會向系統(tǒng)中增加新的產(chǎn)品等級結(jié)構(gòu)或者刪除已有的產(chǎn)品等級結(jié)構(gòu)。 |
4.建造者模式
? ? ? ?建造者模式的核心在于如何一步步構(gòu)建一個包含多個組成部件的完整對象,使用相同的構(gòu)建過程構(gòu)建不同的產(chǎn)品。在軟件開發(fā)中,如果需要創(chuàng)建復(fù)雜對象并希望系統(tǒng)具備很好的靈活性和擴展性可以考慮使用建造者模式。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 抽象建造 (Builder) | 它為創(chuàng)建一個產(chǎn)品對象的各個部件指定抽象接口,在該接口中一般聲明兩類方法,一類方法是 buildPartX(),它們用于創(chuàng)建復(fù)雜對象的各個部件;另一類方法是 getResult(),它們用于返回復(fù)雜對象。Builder既可以是抽象類,也可以是接口。 |
| 具體建造者 (ConcreteBuilder) | 它實現(xiàn)了Builder接口,實現(xiàn)各個部件的具體構(gòu)造和裝配方法,定義并明確所創(chuàng)建的復(fù)雜對象,還可以提供一個方法返回創(chuàng)建好的復(fù)雜產(chǎn)品對象(該方法也可以由抽象建造者實現(xiàn))。 |
| 產(chǎn)品 (Product) | 它是被構(gòu)建的復(fù)雜對象,包含多個組成部件,具體建造者創(chuàng)建該產(chǎn)品的內(nèi)部表示并定義它的裝配過程。 |
| 指揮者 (Director) | 指揮者又稱為導(dǎo)演類,他負責(zé)安排復(fù)雜對象的建造次序,指揮者與抽象建造者之間存在關(guān)聯(lián)關(guān)系,可以在其 construct() 建造方法中調(diào)用建造者對象的部件構(gòu)造與裝配方法,完成復(fù)雜對象的建造。客戶端一般只需要與指揮者進行交互,在客戶端確定具體建造者的類型,并實例化具體建造者對象(也可以通過配置文件和反射機制實現(xiàn)),然后通過指揮者類的構(gòu)造函數(shù)或者 Setter 方法將該對象傳入指揮者類中。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 在建造者模式中,客戶端不必知道產(chǎn)品內(nèi)部組成的細節(jié),將產(chǎn)品本身與產(chǎn)品的創(chuàng)建過程解耦,使得相同的創(chuàng)建過程可以創(chuàng)建不同的產(chǎn)品對象。 |
| 每一個具體建造者都相對獨立,而與其他的具體建造者無關(guān),因此可以很方便地替換具體建造者或增加新的具體建造者,用戶使用不同的具體建造者即可得到不同的產(chǎn)品對象。由于指揮者類針對抽象建造者編程,增加新的具體建造者無須修改原有類庫的代碼,系統(tǒng)擴展方便,符合開閉原則。 | |
| 可以更加精細地控制產(chǎn)品的創(chuàng)建過程。將復(fù)雜產(chǎn)品的創(chuàng)建步驟分解在不同的方法中,使得創(chuàng)建過程更加清晰,也更方便使用程序來控制創(chuàng)建過程。 | |
| 缺點 | 建造者模式所創(chuàng)建的產(chǎn)品一般具有較多的共同點,其組成部分相似,如果產(chǎn)品之間的差異性很大,例如很多組成部分都不相同,不適合使用建造者模式,因此其使用范圍受到一定的限制。 |
| 如果產(chǎn)品的內(nèi)部變化復(fù)雜,可能會導(dǎo)致需要定義很多具體建造者類來實現(xiàn)這種變化,導(dǎo)致系統(tǒng)變得很龐大,增加系統(tǒng)的理解難度和運行成本。 | |
| 適用環(huán)境 | 需要生成的產(chǎn)品對象有復(fù)雜的內(nèi)部結(jié)構(gòu),這些產(chǎn)品對象通常包含多個成員變量。 |
| 需要生成的產(chǎn)品對象的屬性相互依賴,需要指定其生成順序。 | |
| 對象的創(chuàng)建過程獨立于創(chuàng)建該對象的類。在建造者模式中通過引入指揮者類將創(chuàng)建過程封裝在指揮者類中,而不在建造者類和客戶類中。 | |
| 隔離復(fù)雜對象的創(chuàng)建和使用,并使得相同的創(chuàng)建過程可以創(chuàng)建不同的產(chǎn)品。 |
5.工廠方法模式
? ? ? ?工廠方法模式是簡單工廠模式的延伸,它繼承了簡單工廠模式的優(yōu)點,同時還彌補了簡單工廠模式的不足。工廠方法模式是使用頻率最高的設(shè)計模式之一,很多開源框架和API類庫的核心模式。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 抽象工廠(Factory) | 在抽象工廠類中聲明了工廠方法,用于返回一個產(chǎn)品。抽象工廠是工廠方法模式的核心,所有創(chuàng)建對象的工廠類都必須實現(xiàn)該接口。 |
| 具體工廠(ConcreteFactory) | 它是抽象工廠類的子類,實現(xiàn)了在抽象工廠中聲明的工廠方法,并可由客戶端調(diào)用,返回一個具體產(chǎn)品類的實例。 |
| 抽象產(chǎn)品(Product) | 它是定義產(chǎn)品的接口,是工廠方法模式所創(chuàng)建對象的超類型,也就是產(chǎn)品對象的公共父類。 |
| 具體產(chǎn)品(ConcreteProduct) | 它實現(xiàn)了抽象產(chǎn)品接口,某種類型的具體產(chǎn)品由專門的具體工廠創(chuàng)建,具體工廠和具體產(chǎn)品之間一一對應(yīng)。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 在工廠方法模式中,工廠方法用來創(chuàng)建客戶所需要的產(chǎn)品,同時還向客戶隱藏了哪種具體產(chǎn)品類將被實例化這一細節(jié),用戶只需要關(guān)心所需產(chǎn)品對應(yīng)的工廠,無須關(guān)心創(chuàng)建細節(jié),甚至無須知道具體產(chǎn)品類的類名。 |
| 基于工廠角色和產(chǎn)品角色的多態(tài)性設(shè)計是工廠方法模式的關(guān)鍵。它能夠讓工廠自主確定創(chuàng)建何種產(chǎn)品對象,而如何創(chuàng)建這個對象的細節(jié)完全封裝在具體工廠內(nèi)部。工廠方法模式之所以又被稱為多態(tài)工廠模式,正是因為所有的具體工廠類都具有同一抽象父類。 | |
| 使用工廠方法模式的另一個優(yōu)點是在系統(tǒng)中加入新產(chǎn)品時無須修改抽象工廠和抽象產(chǎn)品提供的接口,無須修改客戶端,也無須修改其他的具體工廠和具體產(chǎn)品,而只要添加一個具體工廠和具體產(chǎn)品即可,這樣系統(tǒng)的可擴展性也就變得非常好,完全符合開閉原則。 | |
| 缺點 | 在添加新產(chǎn)品時需要編寫新的具體產(chǎn)品類,而且還要提供與之對應(yīng)的具體工廠類,系統(tǒng)中類的個數(shù)將成對增加,在一定程度上增加了系統(tǒng)的復(fù)雜度,有更多的類需要編譯和運行,會給系統(tǒng)帶來一些額外的開銷。 |
| 由于考慮到系統(tǒng)的可擴展性,需要引入抽象層,在客戶端代碼中均使用抽象層進行定義,增加了系統(tǒng)的抽象性和理解難度。 | |
| 適用環(huán)境 | 客戶端不知道它所需要的的對象的類。在工廠方法模式中,客戶端不需要知道具體產(chǎn)品類的類名,只需要知道所對應(yīng)的工廠即可,具體產(chǎn)品對象由具體工廠類創(chuàng)建,可將具體工廠類的類名存儲在配置文件或數(shù)據(jù)庫中。 |
| 抽象工廠類通過其子類來指定創(chuàng)建哪個對象。在工廠方法模式中,對于抽象工廠類只需要提供一個創(chuàng)建產(chǎn)品的接口,而由其子類來確定具體要創(chuàng)建的對象,利用面向?qū)ο蟮亩鄳B(tài)性和里氏代換原則,在程序運行時子類對象將覆蓋父類對象,從而使得系統(tǒng)更容易擴展。 |
6.原型模式
???????原型模式作為一種快速創(chuàng)建大量相同或相似對象的方式,在軟件開發(fā)中的應(yīng)用較為廣泛,很多軟件提供的復(fù)制(Ctrl + C)和粘貼(Ctrl + V)操作就是原型模式的典型應(yīng)用。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 抽象原型類 (Prototype) | 它是聲明克隆方法的接口,是所有具體原型類的公共父類,它可以是抽象類也可以是接口,甚至還可以是具體實現(xiàn)類。 |
| 客戶類 (Client) | 在客戶類中,讓一個原型對象克隆自身從而創(chuàng)建一個新的對象,只需要直接實例化或通過工廠方法等方式創(chuàng)建一個原型對象,再通過調(diào)用該對象的克隆方法即可得到多個相同的對象。由于客戶類針對抽象原型類 Prototype 編程,因此用戶可以根據(jù)需要選擇具體原型類,系統(tǒng)具有較好的可擴展性,增加或更換具體原型類都很方便。 |
| 具體原型類 (ConcretePrototype) | 它實現(xiàn)在抽象原型類中聲明的克隆方法,在克隆方法中返回自己的一個克隆對象。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 當(dāng)創(chuàng)建新的對象實例較為復(fù)雜時,使用原型模式可以簡化對象的創(chuàng)建過程,通過復(fù)制一個已有實例可以提高新實例的創(chuàng)建效率。 |
| 擴展性較好,由于在原型模式中提供了抽象原型類,在客戶端可以針對抽象原類進行編程,而將具體原型類寫在配置文件中,增加或減少產(chǎn)品類對原有系統(tǒng)沒有任何影響。 | |
| 原型模式提供了簡化的創(chuàng)建結(jié)構(gòu),工廠方法模式常常需要有一個與產(chǎn)品類等級結(jié)構(gòu)相同的工廠等級結(jié)構(gòu),而原型模式就不需要這樣,原型模式中產(chǎn)品的復(fù)制是通過封裝在原型類中的克隆方法實現(xiàn)的,無須專門的工廠類來創(chuàng)建產(chǎn)品。 | |
| 可以使用深克隆的方式保存對象的狀態(tài),使用原型模式將對象復(fù)制一份并將其狀態(tài)保存起來,以便在需要的時候使用(例如恢復(fù)到某一歷史狀態(tài)),可輔助實現(xiàn)撤銷操作。 | |
| 缺點 | 需要為每一個類配備一個克隆方法,而且該克隆方法位于一個類的內(nèi)部,當(dāng)對已有的類進行改造時需要修改源代碼,違背了開閉原則。 |
| 在實現(xiàn)深克隆時需要編寫較為復(fù)雜的代碼,而且當(dāng)對象之間存在多重的嵌套引用時,為了實現(xiàn)深克隆,每一層對象對應(yīng)的類都必須支持深克隆,實現(xiàn)起來可能會比較麻煩。 | |
| 適用環(huán)境 | 創(chuàng)建新對象成本較大(例如初始化需要占用較長的時間,占用太多的CPU資源或網(wǎng)絡(luò)資源),新對象可以通過復(fù)制已有對象來獲得,如果是相似對象,則可以對其成員變量稍作修改。 |
| 系統(tǒng)要保存對象的狀態(tài),而對象的狀態(tài)變化很小。 | |
| 需要避免使用分層次的工廠類來創(chuàng)建分層次的對象,并且類的實例對象只有一個或很少的幾個組合狀態(tài),通過復(fù)制原型對象得到新實例可能比使用構(gòu)造函數(shù)創(chuàng)建一個新實例更加方便。 |
7.單例模式
???????單例模式作為一種目標(biāo)明確、結(jié)構(gòu)簡單、理解容易的設(shè)計模式,在軟件開發(fā)中的使用頻率相當(dāng)高,在很多應(yīng)用軟件和框架中都得以廣泛應(yīng)用。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 單例 (Singleton) | 在單例類的內(nèi)部創(chuàng)建它的唯一實例,并通過靜態(tài)方法 getInstance() 讓客戶端可以使用它的唯一實例;為了防止在外部對單例類實例化,將其構(gòu)造函數(shù)的可見性設(shè)為? private;在單例類內(nèi)部定義了一個 Singleton 類型的靜態(tài)對象作為供外部共享訪問的唯一實例。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 單例模式提供了對唯一實例的受控訪問。因為單例類封裝了它的唯一實例,所以它可以嚴格控制客戶怎樣以及何時訪問它。 |
| 由于在系統(tǒng)內(nèi)存中只存在一個對象,因此可以節(jié)約系統(tǒng)資源,對于一些需要頻繁創(chuàng)建和銷毀的對象,單例模式無疑可以提高系統(tǒng)的性能。 | |
| 允許可變數(shù)目的實例。基于單例模式可以進行擴展,使用與控制單例對象相似的方法來獲得指定個數(shù)的實例對象,既節(jié)省系統(tǒng)資源,又解決了由于單例對象共享過多有損性能的問題。(注:自行提供指定數(shù)目實例對象的類可稱為多例類。) | |
| 缺點 | 由于單例模式中沒有抽象層,因此單例類的擴展有很大的困難。 |
| 單例類的職責(zé)過重,在一定程度上違背了單一職責(zé)原則。因為單例類既提供了業(yè)務(wù)方法,又提供了創(chuàng)建對象的方法(工廠方法),將對象的創(chuàng)建和對象本身的功能耦合在一起。 | |
| 現(xiàn)在很多面向?qū)ο笳Z言(如 Java、C#)的運行環(huán)境都提供了自動來及回收技術(shù),因此如果實例化的共享對象長時間不被利用,系統(tǒng)會認為它是垃圾,會自動銷毀并回收資源,下次利用時又將重新實例化,這將導(dǎo)致共享的單例對象狀態(tài)的丟失。 | |
| 適用環(huán)境 | 系統(tǒng)只需要一個實例對象,例如系統(tǒng)要求提供一個唯一的序列號生成器或資源管理器,或者因為資源消耗太大而只允許創(chuàng)建一個對象。 |
| 客戶調(diào)用類的單個實例只允許適用一個公共訪問點,除了該公共訪問點,不能通過其他途徑訪問該實例。 |
8.適配器模式
???????適配器模式將現(xiàn)有接口轉(zhuǎn)化為客戶類所期望的接口,實現(xiàn)了對現(xiàn)有類的復(fù)用,它是一種使用頻率非常高的設(shè)計模式,在軟件開發(fā)中得到了廣泛的應(yīng)用。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 目標(biāo)抽象類 (Target) | 目標(biāo)抽象類定義客戶所需的接口,可以是一個抽象類或者接口,也可以是具體類。在類適配器中,由于 Java 語言不支持多重繼承,他只能是接口。 |
| 適配器類 (Adapter) | 它可以調(diào)用另一個接口,作為一個轉(zhuǎn)換器,對 Adaptee 和 Target 進行適配。適配器 Adapter是適配器模式的核心,在類適配器中,它通過實現(xiàn)關(guān)聯(lián)一個 Adaptee 對象使二者產(chǎn)生聯(lián)系。 |
| 適配者類 (Adaptee) | 適配者即被適配的角色,它定義了一個已經(jīng)存在的接口,這個接口需要適配。適配者類一般是一個具體類,包含了客戶希望使用的業(yè)務(wù)方法,在某些情況下甚至沒有適配者類的源代碼。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點通用 | 將目標(biāo)類和適配者類解耦,通過引入一個適配器類來重用現(xiàn)有的適配者類,無須修改原有構(gòu)。 |
| 增加了類的頭名性和復(fù)用性,將具體的業(yè)務(wù)實現(xiàn)過程封裝在適配者類中,對于客戶端類而言是透明的,而且提高了適配者的復(fù)用性,同一個適配者類可以在多個不同的系統(tǒng)中復(fù)用。 | |
| 靈活性和擴展性都非常好,通過使用配置文件可以很方便的更換適配器,也可以在不修改原有代碼的基礎(chǔ)上增加新的適配器類,完全符合開閉原則。 | |
| 優(yōu)點 類適配器 | 由于適配器類是適配者類的子類,因此可以在適配器類中置換一些適配者的方法,使得適配器的靈活性更強。 |
| 優(yōu)點 對象適配器 | 一個對象適配器可以把多個不同的適配者適配到同一個目標(biāo)。 |
| 可以適配一個適配者的子類,由于適配器和適配者之間是關(guān)聯(lián)關(guān)系,根據(jù)里氏代換原則,適配者的子類也可通過該適配器進行適配。 | |
| 缺點 類適配器 | 對于 Java、C# 等不支持多重類繼承的語言,一次最多只能適配一個適配者類,不能同時適配多個適配者。 |
| 適配者類不能為最終類,例如在 Java 中不能為 final 類。 | |
| 在 Java、C# 等語言中,類適配器模式中的目標(biāo)抽象類只能為接口,不能為類,其使用有一定的局限性。 | |
| 缺點 對象適配器 | 與類適配器模式相比,在該模式下要在適配器中置換適配者類的某些方法比較麻煩。如果一定要置換掉適配器類的一個或多個方法,可以先做一個適配者類的子類,將適配者類的方法置換掉,然后再把適配者類的子類當(dāng)成真正的適配者進行適配,實現(xiàn)過程較為復(fù)雜。 |
| 適用環(huán)境 | 系統(tǒng)需要使用一些現(xiàn)有的類,而這些類的接口(例如方法名)不符合系統(tǒng)的需要,甚至沒有這些類的源代碼。 |
| 想創(chuàng)建一個可以重復(fù)使用的類,用于和一些彼此之間沒有太大關(guān)聯(lián)的類(包括一些可能在將來引進的類)一起工作。 |
9.橋接模式
???????橋接模式是設(shè)計 Java 虛擬機和實現(xiàn) JDBC 等驅(qū)動程序的核心模式之一,應(yīng)用較為廣泛。在軟件開發(fā)中如果一個類或一個系統(tǒng)有多個變化維度都可以嘗試使用橋接模式對其進行設(shè)計。橋接模式為多維度變化的系統(tǒng)提供了一套完整的解決方案,并且降低了系統(tǒng)的復(fù)雜度。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 抽象類 (Abstraction) | 它是用于定義抽象類的接口,通常是抽象類而不是接口,其中定義了一個 Implementor(實現(xiàn)類接口)類型的對象并可以維護該對象,它與? Implementor 之間具有關(guān)聯(lián)關(guān)系,它既可以包含抽象業(yè)務(wù)方法,也可以包含具體業(yè)務(wù)方法。 |
| 擴充抽象類 (RefinedAbstraction) | 它擴充由 Abstraction 定義的接口,通常情況下它不再是抽象類而是具體類,它實現(xiàn)了在 Abstraction 中聲明的抽象業(yè)務(wù)方法,在 RefinedAbstraction 中可以調(diào)用在?Implementor 中定義的業(yè)務(wù)方法。 |
| 實現(xiàn)類接口 (Implementor) | 它是定義實現(xiàn)類的接口,這個接口不一定要與 Abstraction 的接口完全一致,事實上這兩個接口可以完全不同。一般而言,Implementor?接口僅提供基本操作,而 Abstraction 定義的接口可能會做更多復(fù)雜的操作。Implementor 接口對這些基本操作進行了聲明,而具體實現(xiàn)交給其子類。通過關(guān)聯(lián)關(guān)系,在?Abstraction 中不僅擁有自己的方法,還可以調(diào)用到?Implementor 中定義的方法,使用關(guān)聯(lián)關(guān)系來代替繼承關(guān)系。 |
| 具體實現(xiàn)類 (ConcreteImplementor) | 它具體實現(xiàn)了?Implementor 接口,在不同的?ConcreteImplementor 中提供基本操作的不同實現(xiàn),在程序運行時?ConcreteImplementor 對象將替換其父類對象,提供給抽象類具體的業(yè)務(wù)操作方法。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 分離抽象接口及其實現(xiàn)部分。橋接模式使用“對象間的關(guān)聯(lián)關(guān)系”解耦了抽象和實現(xiàn)之間固有的綁定關(guān)系,使得抽象和實現(xiàn)可以沿著各自的維度來變化。所謂抽象和實現(xiàn)沿著各自維度變化,也就是說抽象和實現(xiàn)不在同一繼承層次結(jié)構(gòu)中,而是“子類化”它們,使它們各自具有自己的子類,以便任意組合子類,從而獲得多維度組合對象。 |
| 在很多情況下,橋接模式可以取代多層繼承方案,多層繼承方案違背了單一職責(zé)原則,復(fù)用性較差,并且類的個數(shù)非常多,橋接模式是比多層繼承方案更好的解決方法,它極大地覺少了子類的個數(shù)。 | |
| 橋接模式提高了系統(tǒng)的可擴展性,在兩個變化維度中任意擴展一個維度都不需要修改原有系統(tǒng),符合開閉原則。 | |
| 缺點 | 橋接模式的使用會增加系統(tǒng)的理解與設(shè)計難度,由于關(guān)聯(lián)關(guān)系建立在抽象層,要求開發(fā)者一開始就針對抽象層進行設(shè)計與編程。 |
| 橋接模式要求正確地識別出系統(tǒng)中的兩個獨立變化的維度,因此其使用范圍具有一定的局限性,如何正確識別兩個獨立維度也需要一定的經(jīng)驗積累。 | |
| 適用環(huán)境 | 如果一個系統(tǒng)需要在抽象化和具體化之間增加更多的靈活性,避免在兩個層次之間建立靜態(tài)的繼承關(guān)系,通過橋接模式可以使它們在抽象層建立一個關(guān)聯(lián)關(guān)系。 |
| 抽象部分和實現(xiàn)部分可以用繼承的方式獨立擴展而互不影響,在程序運行時可以動態(tài)地將一個抽象化子類的對象和一個實現(xiàn)化子類的對象進行組合,即系統(tǒng)需要對抽象化角色和實現(xiàn)化角色進行動態(tài)耦合。 | |
| 一個類存在兩個(或多個)獨立變化的維度,且這兩個(或多個)維度都需要獨立進行擴展。 | |
| 對于那些不希望使用繼承或因為多層繼承導(dǎo)致系統(tǒng)類的個數(shù)急劇增加的系統(tǒng),橋接模式尤為適用。 |
10.組合模式
???????組合模式使用面向?qū)ο蟮乃枷雭韺崿F(xiàn)樹形結(jié)構(gòu)的構(gòu)建與處理,描述了如何將容器對象和葉子對象進行遞歸組合,實現(xiàn)簡單、靈活性好。由于在軟件開發(fā)中存在大量的樹形結(jié)構(gòu),因此組合模式是一種使用頻率較高的結(jié)構(gòu)型設(shè)計模式。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 抽象構(gòu)件 (Component) | 它可以是接口或抽象類,為葉子構(gòu)件和容器構(gòu)件對象聲明接口,在該角色中可以包含所有子類共有行為的聲明和實現(xiàn)。在抽象構(gòu)件中定義了訪問及管理它們的子構(gòu)件的方法,如增加子構(gòu)件、刪除子構(gòu)件,獲取子構(gòu)件等。 |
| 葉子構(gòu)件 (Leaf) | 它在組合結(jié)構(gòu)中表示葉子節(jié)點對象,葉子節(jié)點沒有子節(jié)點,它實現(xiàn)了在抽象構(gòu)件中定義的行為。對于那些訪問及管理子構(gòu)件的方法,可以通過拋出異常、提示錯誤等方式進行處理。 |
| 容器構(gòu)件 (Composite) | 它在組合結(jié)構(gòu)中表示容器節(jié)點對象,容器節(jié)點包含子節(jié)點,其子節(jié)點可以是葉子結(jié)點,也可以是容器節(jié)點,它提供一個集合用于存儲子節(jié)點,實現(xiàn)了在抽象構(gòu)件中定義的行為,包括那些訪問及管理子構(gòu)件的方法,在其業(yè)務(wù)方法中可以遞歸調(diào)用其子結(jié)點的業(yè)務(wù)方法。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 可以清楚地定義分層次的復(fù)雜對象,表示對象的全部或部分層次,它讓客戶端忽略了層次的差異,方便對整個層次結(jié)構(gòu)進行控制。 |
| 客戶端可以一致地使用一個組合結(jié)構(gòu)或其中單個對象,不必關(guān)心處理的是單個對象還是整個組合結(jié)構(gòu),簡化了客戶端代碼。 | |
| 在組合模式中增加新的容器構(gòu)件和葉子構(gòu)件都很方便,無須對現(xiàn)有類庫進行任何修改,符合開閉原則。 | |
| 為樹形結(jié)構(gòu)的面向?qū)ο髮崿F(xiàn)提供了一種靈活的解決方案,通過葉子對象和容器對象的遞歸組合可以形成復(fù)雜的樹形結(jié)構(gòu),但對樹形結(jié)構(gòu)的控制卻非常簡單。 | |
| 缺點 | 在增加新構(gòu)件時很難對容器中的構(gòu)件類型進行限制。有時候希望一個容器中只能有某些特定類型的對象,例如在某個文件夾中只能包含文本文件,在使用組合模式時不能依賴類型系統(tǒng)來施加這些約束,因為它們都來自于相同的抽象層,在這種情況下必須通過在運行時進行類型檢查來實現(xiàn),這個實現(xiàn)過程較為復(fù)雜。 |
| 適用環(huán)境 | 在具有整體和部分的層次結(jié)構(gòu)中希望通過一種方式忽略整體與部分的差異,客戶端可以一致地對待它們。 |
| 在一個使用面向?qū)ο笳Z言開發(fā)的系統(tǒng)中需要處理一個樹形結(jié)構(gòu)。 | |
| 在一個系統(tǒng)中能夠分離出葉子對象和容器對象,而且它們的類型不固定,需要增加一些新的類型。 |
11.裝飾模式
???????裝飾模式降低了系統(tǒng)的耦合度,可以動態(tài)增加或刪除對象的職責(zé),并使需要裝飾的具體構(gòu)件類和具體裝飾類可以獨立變化,以便增加新的具體構(gòu)件類和具體裝飾類。使用裝飾模式將大大減少子類的個數(shù),讓系統(tǒng)擴展起來比較方便,而且更容易維護,是取代繼承復(fù)用的有效方式之一。在軟件開發(fā)中裝飾模式的應(yīng)用較為廣泛,例如在 Java I/O 中的輸入流和輸出流的設(shè)計、javax.swing 包中一些圖形界面構(gòu)件功能的增強等地方都運用了裝飾模式。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 抽象構(gòu)件 (Component) | 它是具體構(gòu)件和抽象裝飾類的共同父類,聲明了在具體構(gòu)件中實現(xiàn)業(yè)務(wù)的方法,它的引入可以使客戶端以一致的方式處理未被裝飾的對象以及裝飾之后的對象,實現(xiàn)客戶端的透明操作。 |
| 具體構(gòu)建 (ConcreteComponent) | 它是抽象構(gòu)件類的子類,用于定義具體的構(gòu)件對象,實現(xiàn)了在抽象構(gòu)件中聲明的方法,裝飾類可以給它增加額外的職責(zé)(方法)。 |
| 抽象裝飾類 (Decorator) | 它也是抽象構(gòu)件類的子類,用于給具體構(gòu)件增加職責(zé),但是具體職責(zé)在其子類中實現(xiàn)。它維護一個指向抽象構(gòu)件對象的引用,通過該引用可以調(diào)用裝飾之前的構(gòu)件對象的方法,并通過其子類擴展該方法,以達到裝飾的目的。 |
| 具體裝飾類 (ConcreteDecorator) | 它是抽象裝飾類的子類,負責(zé)向構(gòu)件添加新的職責(zé)。每一個具體裝飾類都定義了一些新的行為,他可以調(diào)用在抽象裝飾中定義的方法,并可以增加新地方法用于擴充對象的行為。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 對于擴展一個對象的功能,裝飾模式比繼承更加靈活,不會導(dǎo)致類的個數(shù)急劇增加。 |
| 可以通過一種動態(tài)地方式來擴展一個對象的功能,通過配置文件可以在運行時選擇不同的具體裝飾類,從而實現(xiàn)不同的行為。 | |
| 可以對一個對象進行多次裝飾,通過使用不同的具體裝飾類以及這些裝飾類的排列組合可以創(chuàng)造出很多不同行為的組合,得到功能更加強大的對象。 | |
| 具體構(gòu)件類與具體裝飾類可以獨立變化,用戶可以根據(jù)需要增加新的具體構(gòu)建類和具體裝飾類,原有的類庫代碼無須改變,符合開閉原則。 | |
| 缺點 | 在使用裝飾模式進行系統(tǒng)設(shè)計時將產(chǎn)生很多小對象,這些對象的區(qū)別在于它們之間相互連接的方式有所不同,而不是它們的類或者屬性值有所不同,大量小對象的產(chǎn)生勢必會占用更多的系統(tǒng)資源,在一定程度上影響了程序的性能。 |
| 裝飾模式提供了一種比繼承更加靈活、機動的解決方案,但同時也意味著比繼承更加易于出錯,排錯也更困難,對于多次裝飾的對象,在調(diào)試是尋找錯誤可能需要逐級排查,較為繁瑣。 | |
| 適用環(huán)境 | 在不影響其他對象的情況下以動態(tài)、透明的方式給單個對象添加職責(zé)。 |
| 當(dāng)不能采用繼承的方式對系統(tǒng)進行擴展或采用繼承不利于系統(tǒng)擴展和維護時可以使用裝飾模式。不能采用繼承的情況主要有兩類:第一類是系統(tǒng)中存在大量獨立的擴展,為支持每一種擴展或者擴展之間的組合將產(chǎn)生大量的子類,使得子類數(shù)目呈爆炸性增長;第二類是因為類已定義為不能被繼承(例如在 Java 語言中使用final 關(guān)鍵字修飾的類)。 |
12.外觀模式
???????外觀模式是一種使用頻率非常高的設(shè)計模式,它通過引入一個外觀角色來簡化客戶端與子系統(tǒng)之間的交互,為復(fù)雜的子系統(tǒng)調(diào)用提供一個統(tǒng)一的入口,使子系統(tǒng)與客戶端的耦合度降低,且客戶端調(diào)用非常方便。外觀模式并不給系統(tǒng)增加任何新功能,它僅僅是簡化調(diào)用接口。在幾乎所有的軟件中都能夠找到外觀模式的應(yīng)用,例如絕大多數(shù) B/S 系統(tǒng)都有一個首頁或者導(dǎo)航頁面,大部分 C/S 系統(tǒng)都提供了菜單或者工具欄,在這里首頁和導(dǎo)航頁面就是 B/S 系統(tǒng)的外觀角色,而菜單和工具欄就是 C/S 系統(tǒng)的外觀角色,通過它們用戶可以快速訪問子系統(tǒng),降低了系統(tǒng)的復(fù)雜程度。所涉及與多個業(yè)務(wù)對象交互的場景都可以考慮使用外觀模式進行重構(gòu),例如 Java EE 中的 Session 外觀模式。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 外觀角色 (Facade) | 在客戶端可以調(diào)用它的方法,在外觀角色中可以知道相關(guān)的(一個或者多個)子系統(tǒng)的功能和責(zé)任;在正常情況下,它將所有從客戶端發(fā)來的請求委派到相應(yīng)的子系統(tǒng),傳遞給相應(yīng)的子系統(tǒng)對象處理。 |
| 子系統(tǒng)角色 (SubSystem) | 在軟件系統(tǒng)中可以有一個或者多個子系統(tǒng)角色,每一個子系統(tǒng)可以不是一個單獨的類,而是一個類的集合,它實現(xiàn)子系統(tǒng)的功能;每一個子系統(tǒng)都可以被客戶端直接調(diào)用,或者被外觀角色調(diào)用,它處理由外觀類傳過來的請求;子系統(tǒng)并不知道外觀的存在,對于子系統(tǒng)而言,外觀角色僅僅是另外一個客戶端而已。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 它對于客戶端屏蔽了子系統(tǒng)組件,減少了客戶端所需處理的對象數(shù)目,并使子系統(tǒng)使用起來更加容易。通過引入外觀模式,客戶端代碼將變得很簡單,與之關(guān)聯(lián)的對象也很少。 |
| 它實現(xiàn)了子系統(tǒng)與客戶端之間的松耦合關(guān)系,這使得子系統(tǒng)的變化不會影響到調(diào)用它的客戶端,只需要調(diào)整外觀類即可。 | |
| 一個子系統(tǒng)的修改對其他子系統(tǒng)沒有任何影響,而且子系統(tǒng)內(nèi)部變化也不會影響到外觀對象。 | |
| 缺點 | 不能很好地限制客戶端直接使用子系統(tǒng)類,如果客戶端訪問子系統(tǒng)類做太多的限制則減少了可變性和靈活性。 |
| 如果設(shè)計不當(dāng),增加新的子系統(tǒng)可能需要修改外觀類的源代碼,違背了開閉原則。 | |
| 適用環(huán)境 | 當(dāng)要為訪問一系列復(fù)雜的子系統(tǒng)提供一個簡單入口時可以使用外觀模式。 |
| 客戶端程序與多個子系統(tǒng)之間存在很大的依賴性。引入外觀類可以將子系統(tǒng)與客戶端解耦,從而提高子系統(tǒng)的獨立性和可移植性。 | |
| 在層次化結(jié)構(gòu)中可以使用外觀模式定義系統(tǒng)中每一層的入口,層與層之間不直接產(chǎn)生聯(lián)系,而通過外觀類建立聯(lián)系,降低層之間的耦合度。 |
13.享元模式
???????當(dāng)系統(tǒng)中存在大量相同或者相似的對象時享元模式是一種較好的解決方案,它通過共享技術(shù)實現(xiàn)相同或相似的細粒度對象的復(fù)用,從而節(jié)約了內(nèi)存空間,提高了系統(tǒng)性能。相比其他結(jié)構(gòu)型設(shè)計模式,享元模式的使用頻率并不算太高,但是作為一種以“節(jié)約內(nèi)存,提高性能”為出發(fā)點的設(shè)計模式,它在軟件開發(fā)中還是得到了一定程度的應(yīng)用。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 抽象享元類 (Flyweight) | 抽象享元類通常是一個接口或抽象類,在抽象享元類中聲明了具體享元類公共的方法,這些方法可以向外界提供享元對象的內(nèi)部數(shù)據(jù)(內(nèi)部狀態(tài)),同時也可以通過這些方法來設(shè)置外部數(shù)據(jù)(外部狀態(tài))。 |
| 具體享元類 (ConcreteFlyweight) | 具體享元類實現(xiàn)了抽象享元類,其實例稱為享元對象;在具體享元類中為內(nèi)部狀態(tài)提供了存儲空間。通常可以結(jié)合單例模式來設(shè)計具體享元類,為每一個具體享元類提供唯一的享元對象。 |
| 非共享具體享元類 (UnsharedConcreteFlyweight) | 并不是所有的抽象享元類的子類都需要被共享,不能被共享的子類可設(shè)計為非共享具體享元類;當(dāng)需要一個非共享具體享元類的對象時可以直接通過實例化創(chuàng)建。 |
| 享元工廠類 (FlyweightFactory) | 享元工廠類用于創(chuàng)建并管理享元對象,它針對抽象享元類編程,將各種類型的具體享元對象存儲在一個享元池中,享元池一般設(shè)計為一個存儲“鍵值對”的集合(也可以是其他類型的集合),可以結(jié)合工廠模式進行設(shè)計;當(dāng)用戶請求一個具體享元對象時,享元工廠提供一個存儲在享元池中已創(chuàng)建的實例或者創(chuàng)建一個新的實例(如果不存在),返回新創(chuàng)建的實例并將其存儲在享元池中。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 享元模式可以減少內(nèi)存中對象的數(shù)量,使得相同或者相似對象再內(nèi)存中只保存一份。從而可以節(jié)約系統(tǒng)資源,提高系統(tǒng)性能。 |
| 享元模式的外部狀態(tài)相對孤立,而且不會影響其內(nèi)部狀態(tài),從而使享元對象可以在不同的環(huán)境中被共享。 | |
| 缺點 | 享元模式使系統(tǒng)變得復(fù)雜,需要分離出內(nèi)部狀態(tài)和外部狀態(tài),這使得程序的邏輯復(fù)雜化。 |
| 為了使對象可以共享,享元模式需要將享元對象的部分狀態(tài)外部化,而讀取外部狀態(tài)將使運行時間變長。 | |
| 適用環(huán)境 | 一個系統(tǒng)有大量相同或者相似的對象,造成內(nèi)存的大量耗費。 |
| 對象的大部分狀態(tài)都可以外部化,可以將這些外部狀態(tài)傳入對象中。 | |
| 在使用享元模式時需要維護一個存儲享元對象的享元池,而這需要耗費一定的系統(tǒng)資源,因此應(yīng)當(dāng)在需要多次重復(fù)使用享元對象時才使用享元模式。 |
14.代理模式
???????代理模式是常用的結(jié)構(gòu)型設(shè)計模式之一,它為對象的間接訪問提供了一個解決方案,可以對對象的訪問進行控制。代理模式的類型較多,其中遠程代理、虛擬代理、保護代理等在軟件開發(fā)中的應(yīng)用非常廣泛。在 Java RMI、EJB、Web Service、Spring AOP 等技術(shù)和框架中都使用了代理模式。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 抽象主題角色 (Subject) | 它聲明了真實主題和代理主題的共同接口,這樣一來在任何使用真實主題的地方都可以使用代理主題,客戶端通常需要針對抽象主題角色進行編程。 |
| 代理主題角色 (Proxy) | 它包含了對真實主題的引用,從而可以在任何時候操作真實主題對象;在代理主題角色中提供了一個與真實主題角色相同的接口,以便在任何時候都可以替代真實主題;代理主題角色還可以控制對真實主題的使用,負責(zé)在需要的時候創(chuàng)建和刪除真實主題對象,并對真實主題對象的使用加以約束。通常,在代理主題角色中客戶端在調(diào)用所引用的真實主題操作之前或之后還需要執(zhí)行其他操作,而不僅僅是單純調(diào)用真實主題對象中的操作。 |
| 真實主題角色 (RealSubject) | 它定義了代理角色所代表的真實對象,在真實主題角色中實現(xiàn)了真實的業(yè)務(wù)操作,客戶端可以通過代理主題角色間接調(diào)用真實主題角色中定義的操作。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 能夠協(xié)調(diào)調(diào)用者和被調(diào)用者,在一定程度上降低了系統(tǒng)的耦合度。 |
| 客戶端可以針對抽象主題角色進行編程,增加和更換代理類無須修改源代碼,符合開閉原則,系統(tǒng)具體較好的靈活性和可擴展性。 | |
| 優(yōu)點 遠程代理 | 遠程代理為位于兩個不同地址空間的對象的訪問提供了一種實現(xiàn)機制,可以將一些消耗資源較多的對象和操作移至性能更好的計算機上,提高了系統(tǒng)的整體運行效率。 |
| 優(yōu)點 虛擬代理 | 虛擬代理通過一個消耗資源較少的對象來代表一個消耗資源較多的對象,可以在一定程度上節(jié)省系統(tǒng)的運行開銷。 |
| 優(yōu)點 緩沖代理 | 緩沖代理為某一個操作的結(jié)果提供臨時的緩存存儲空間,以便在后續(xù)使用中能夠共享這些結(jié)果,優(yōu)化系統(tǒng)性能,縮短執(zhí)行時間。 |
| 優(yōu)點 保護代理 | 保護代理可以控制對一個對象的訪問權(quán)限,為不同用戶提供不同級別的使用權(quán)限。 |
| 缺點 | 由于在客戶端和真實主題之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢,例如保護代理。 |
| 實現(xiàn)代理模式需要額外的工作,而且有些代理模式的實現(xiàn)過程較為復(fù)雜,例如遠程代理。 | |
| 適用環(huán)境 | 當(dāng)客戶端對象需要訪問遠程主機中的對象時可以使用遠程代理。 |
| 當(dāng)需要用一個消耗資源較少的對象來表示一個消耗資源較多的對象,從而降低系統(tǒng)開銷、縮短運行時間時可以使用虛擬代理,例如一個對象需要很長時間才能完成加載時。 | |
| 當(dāng)需要為某一個被頻繁訪問的操作結(jié)果提供一個臨時存儲空間,以供多個客戶端共享訪問這些結(jié)果時可以使用緩沖代理。通過使用緩沖代理,系統(tǒng)無須再客戶端每一次訪問時都重新執(zhí)行操作,只需要直接從臨時緩沖區(qū)獲取操作結(jié)果即可。 | |
| 當(dāng)需要控制對一個對象的訪問為不同用戶提供不同級別的訪問權(quán)限時可以使用保護代理。 | |
| 當(dāng)需要為一個對象的訪問(引用)提供一些額外的操作時可以使用智能引用代理。 |
15.職責(zé)鏈模式
? ? ? ?職責(zé)鏈模式通過建立一條鏈來組織請求的處理者,請求將沿著鏈進行傳遞,請求發(fā)送者無須知道請求在何時、何處以及如何被處理,實現(xiàn)了請求發(fā)送者與處理者的解耦。在軟件開發(fā)中,如果遇到有多個對象可以處理同一請求可以應(yīng)用職責(zé)鏈模式,例如在 Web 應(yīng)用開發(fā)中創(chuàng)建多個過濾器(Filter)鏈來對請求數(shù)據(jù)進行過濾,在工作流系統(tǒng)中實現(xiàn)公文的分級審批等,使用職責(zé)鏈模式可以較好地解決此類問題。Java 語言中的異常處理(Exception Handlers)機制也是職責(zé)鏈模式的典型應(yīng)用之一,不同的catch 子句可以處理不同類型的異常,這些 catch 子句構(gòu)成了一條處理異常對象的職責(zé)鏈。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 抽象處理者 (Handler) | 它定義了一個處理請求的接口,一般設(shè)計為抽象類,由于不同的具體處理者處理請求的方式不同,因此在其中定義了抽象請求處理方法。每一個處理者的下家還是一個處理者,故在抽象處理者中定義了一個抽象處理者類型的對象作為其對下家的引用,通過該引用處理者可以連成一條鏈。 |
| 具體處理著 (ConcreteHandler) | 它是抽象處理者的子類可以處理用戶請求,在具體處理者類中實現(xiàn)了抽象處理者中定義的抽象請求處理方法,在處理請求之前需要進行判斷,看是否有相應(yīng)的處理權(quán)限,如果可以處理請求就處理它,否則將請求轉(zhuǎn)發(fā)給后繼者;在具體處理者中可以訪問鏈中的下一個對象,以便請求轉(zhuǎn)發(fā)。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 職責(zé)鏈模式使得一個對象無須知道其他哪一個對象處理其請求,對象僅需知道該請求會被處理即可,接收者和發(fā)送者都沒有對方的明確信息,并且鏈中的對象不需要知道鏈的結(jié)構(gòu),由客戶端負責(zé)鏈的創(chuàng)建,降低了系統(tǒng)的耦合度。 |
| 請求處理對象僅需維持一個指向其后繼者的引用,而不需要維持它對所有的候選處理者的引用,可簡化對象之間的相互連接。 | |
| 在給對象分配職責(zé)時,職責(zé)鏈可以帶來更多的靈活性,可以通過在運行時對該鏈進行動態(tài)地增加或修改來增加或改變處理一個請求的職責(zé)。 | |
| 在系統(tǒng)中增加一個新的具體請求處理者時無須修改原有系統(tǒng)的代碼,只需要在客戶端重新建鏈即可,從這一點來看是符合開閉原則的。 | |
| 缺點 | 由于一個請求沒有明確的接收者,那么就不能保證它一定會被處理,該請求可能一直到鏈的末端都得不到處理;一個請求也可能因職責(zé)鏈沒有被正確配置而得不到處理。 |
| 對于比較長的職責(zé)鏈,請求的處理可能涉及多個處理對象,系統(tǒng)性能將受到一定的影響,而且在進行代碼調(diào)試時不太方便。 | |
| 如果建鏈不當(dāng),可能會造成循環(huán)調(diào)用,將導(dǎo)致系統(tǒng)陷入死循環(huán)。 | |
| 適用環(huán)境 | 有多個對象可以處理同一個請求,具體哪個對象處理該請求待運行時刻再確定,客戶端只需將請求提交到鏈上,而無須關(guān)心請求的處理對象是誰以及它是如何處理的。 |
| 在不明確指定接收者的情況下向多個對象中的一個提交一個請求。 | |
| 可動態(tài)指定一組對象處理請求,客戶端可以動態(tài)創(chuàng)建職責(zé)鏈來處理請求,還可以改變鏈中處理者之間的先后次序。 |
16.命令模式
???????命令模式是一種使用頻率非常高的設(shè)計模式,它可以將請求發(fā)送者與接收者解耦,請求發(fā)送者通過命令對象來間接引用請求接收者,使得系統(tǒng)具有更好的靈活性和可擴展性。在基于 GUI 的軟件開發(fā)(無論是電腦桌面應(yīng)用還是手機移動應(yīng)用)中,命令模式都得到了廣泛的應(yīng)用。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 抽象命令類 (Command) | 抽象命令類一般是一個抽象類或接口,在其中聲明了用于執(zhí)行請求的 execute() 等方法,通過這些方法可以調(diào)用請求接收者的相關(guān)操作。 |
| 具體命令類 (ConcreteCommand) | 具體命令類是抽象命令類的子類,實現(xiàn)了在抽象命令類中聲明的方法,它對應(yīng)具體的接收者對象,將接收者對象的動作綁定其中。具體命令類在實現(xiàn) execute() 方法時將調(diào)用接收者對象的相關(guān)操作(Action)。 |
| 調(diào)用者 (Invoker) | 調(diào)用者即請求發(fā)送者,它通過命令對象來執(zhí)行請求。一個調(diào)用者并不需要在設(shè)計時確定其接收者,因此它只與抽象命令類之間存在關(guān)聯(lián)關(guān)系。在程序運行時可以將一個具體命令對象注入其中,再調(diào)用具體命令對象的 execute() 方法,從而實現(xiàn)間接調(diào)用請求接收者的相關(guān)操作。 |
| 接收者 (Receiver) | 接收者執(zhí)行與請求相關(guān)的操作,具體實現(xiàn)對請求的業(yè)務(wù)處理。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 降低系統(tǒng)的耦合度。由于請求者與接收者之間不存在直接引用,因此請求者與接收者之間實現(xiàn)完全解耦,相同的請求者可以對應(yīng)不同的接收者,同樣相同的接收者也可以供不同的請求者使用,兩者之間具有良好的獨立性。 |
| 新的命令可以很容易地加入到系統(tǒng)中。由于增加新的具體命令類不會影響到其他類,因此增加新的具體命令類很容易,無須修改原有系統(tǒng)源代碼,甚至客戶類代碼,滿足開閉原則的要求。 | |
| 可以比較容易地設(shè)計一個命令隊列或宏命令(組合命令)。 | |
| 為請求的撤銷(Undo)和恢復(fù)(Redo)操作提供了一種設(shè)計和實現(xiàn)方案。 | |
| 缺點 | 使用命令模式可能會導(dǎo)致某些系統(tǒng)有過多的具體命令類,因為針對每一個對請求接收者的調(diào)用操作都需要設(shè)計一個具體命令類,所以在某些系統(tǒng)中可能需要提供大量的具體命令類,者將影響命令模式的使用。 |
| 適用環(huán)境 | 系統(tǒng)需要將請求調(diào)用者和請求接收者解耦,使得調(diào)用者和接收者不直接交互。請求調(diào)用者無須知道接收者的存在,也無須知道接收者是誰,接收者也無須關(guān)心何時被調(diào)用。 |
| 系統(tǒng)需要在不同的時間指定請求、將請求排隊和執(zhí)行請求。一個命令對象和請求的初始調(diào)用者可以有不同的生命期,換而言之,最初的請求發(fā)出者可能已經(jīng)不存在,而命令對象本身仍然是活動的,可以通過該命令對象去調(diào)用請求接收者,并且無須關(guān)心請求調(diào)用者的存在性,可以通過請求日志文件等機制來具體實現(xiàn)。 | |
| 系統(tǒng)需要支持命令的撤銷(Undo)操作和恢復(fù)(Redo)操作。 | |
| 系統(tǒng)需要將一組操作組合在一起形成宏命令。 |
17.解釋器模式
???????解釋器模式為自定義語言的設(shè)計和實現(xiàn)提供了一種解決方案,用于定義一組文法規(guī)則并通過這組文法規(guī)則來解釋語言中的句子。雖然解釋器模式的使用頻率不是特別高,但是它在正則表達式、XML 文檔解釋等領(lǐng)域還是得到了廣泛使用。與解釋器模式類似,目前還誕生了很多基于抽象語法樹的源代碼處理工具,例如 Eclipse 中的 Eclipse AST,它可以使用于表示和處理 Java 語言的語法結(jié)構(gòu),用戶可以通過擴展其功能創(chuàng)建自己的文法規(guī)則,實現(xiàn)對源代碼的分析。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 抽象表達式 (AbstractExpression) | 在抽象表達式中聲明了抽象的解釋操作,它是所有終結(jié)符表達式和非終結(jié)符表達式的公共父類。 |
| 終結(jié)符表達式 (TerminalExpression) | 終結(jié)符表達式是抽象表達式的子類,它實現(xiàn)了與文法中的終結(jié)符相關(guān)聯(lián)的解釋操作,在句子中的每一個終結(jié)符否是該類的一個實例。通常在一個解釋器模式中只有少數(shù)幾個終結(jié)符表達式類,它們的實例可以通過非終結(jié)符表達式組成較為復(fù)雜的句子。 |
| 非終結(jié)符表達式 (NonterminalExpression) | 非終結(jié)符表達式也是抽象表達式的子類,它實現(xiàn)了文法中非終結(jié)符的解釋操作,由于在非終結(jié)符表達式中可以包含終結(jié)符表達式,也可以繼續(xù)包含非終結(jié)符表達式,因此其解釋操作一般通過遞歸的方式完成。 |
| 環(huán)境類 (Context) | 環(huán)境類又稱為上下文類,它用于存儲解釋器之外一些全局信息,通常它臨時存儲了需要解釋的語句。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 易于改變和擴展文法。由于在解釋器模式中使用類表示語言的文法規(guī)則,因此可以通過繼承等機制來改變或擴展文法。 |
| 每一條文法規(guī)則都可以表示為一個類,因此可以方便的實現(xiàn)一個簡單的語言。 | |
| 實現(xiàn)文法較為容易。在抽象語法樹種每一個表達式節(jié)點類的實現(xiàn)方式都是相似的,這些累的代碼編寫不會特別復(fù)雜,還可以通過一些工具自動生成節(jié)點類代碼。 | |
| 增加新的解釋器表達式較為方便。如果用戶要增加新的解釋表達式只需要對應(yīng)增加一個新的終結(jié)符表達式或非終結(jié)符表達式類,原有表達式類代碼無須修改,符合開閉原則。 | |
| 缺點 | 對于復(fù)雜文法難以維護。在解釋器模式中每一條規(guī)則至少需要定義一個類,因此如果一個語言包含太多文法規(guī)則,類的個數(shù)將會急劇增加,導(dǎo)致系統(tǒng)難以管理和維護,此時可以考慮使用語法分析程序等方式來取代解釋器模式。 |
| 執(zhí)行效率較低。由于在解釋器模式中使用了大量的循環(huán)和遞歸調(diào)用,因此在解釋較為復(fù)雜的句子時其速度很慢,而且代碼的調(diào)式過程也比較麻煩。 | |
| 適用環(huán)境 | 可以將一個需要解釋執(zhí)行的語言中的句子表示為一顆抽象語法樹。 |
| 一些重復(fù)出現(xiàn)的問題可以用一種簡單的語言進行表達。 | |
| 一個語言的文法較為簡單。對于復(fù)雜的文法,解釋器模式中的文法類層次結(jié)構(gòu)將變得很龐大而無法管理,此時最好使用語法分析程序生成器。 | |
| 執(zhí)行效率不是關(guān)鍵問題。高效的解釋器通常不是通過直接解釋抽象語法樹來實現(xiàn)的,而是需要將它們轉(zhuǎn)換成其他形式,使用解釋器模式的執(zhí)行效率并不高。 |
18.迭代器模式
???????迭代器模式是一種使用頻率非常高的設(shè)計模式,通過引入迭代器可以將數(shù)據(jù)的便利功能從聚合對象中分離出來,聚合對象只負責(zé)存儲數(shù)據(jù),而遍歷數(shù)據(jù)由迭代器來完成。由于很多編程語言的類庫都已經(jīng)實現(xiàn)了迭代器模式,因此在實際開發(fā)中只需要直接使用 Java、C# 等語言已定義好的迭代器即可,迭代器已經(jīng)成為操作聚合對象的基本工具之一。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 抽象迭代器 (Iterator) | 它定義了訪問和遍歷元素的接口,聲明了用于遍歷數(shù)據(jù)元素的方法,例如用于獲取第一個元素的 first() 方法、用于訪問下一個元素的 next() 方法、用于判斷是否還有下一個元素的 hasNext() 方法、用于獲取當(dāng)前元素的 currentItem() 方法等,在具體迭代器中將實現(xiàn)這些方法。 |
| 具體迭代器 (ConcreteIterator) | 它實現(xiàn)了抽象迭代器接口,完成對聚合對象的遍歷,同時在具體迭代器中通過游標(biāo)來記錄在聚合對象中所處的當(dāng)前位置,在具體實現(xiàn)時游標(biāo)通常是一個表示位置的非負整數(shù)。 |
| 抽象聚合類 (Aggregate) | 它用于存儲和管理元素對象,聲明一個 createIterator() 方法用于創(chuàng)建一個迭代器對象,充當(dāng)抽象迭代器工廠角色。 |
| 具體聚合類 (ConcreteAggregate) | 它是抽象聚合類的子類,實現(xiàn)了在抽象聚合類中聲明的 createIterator() 方法,該方法返回一個與該具體聚合類對應(yīng)的具體迭代器?ConcreteIterator 實例。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 迭代器模式支持以不同的方式遍歷一個聚合對象,在同一聚合對象上可以定義多種遍歷方式。在迭代器模式中只需要用一個不同的迭代器來替換原有迭代器即可改變遍歷算法,也可以自己定義迭代器的子類以支持新的遍歷方式。 |
| 迭代器模式簡化了聚合類。由于引入了迭代器,在原有的聚合對象中不需要再自行提供數(shù)據(jù)遍歷等方法,這樣可以簡化聚合類的設(shè)計。 | |
| 在迭代器模式中引入了抽象層,增加新的聚合類和迭代器類都很方便,無須修改源代碼,滿足開閉原則。 | |
| 缺點 | 由于迭代器模式將存儲數(shù)據(jù)和遍歷數(shù)據(jù)的職責(zé)分離,在增加新的聚合類時需要對應(yīng)增加新的迭代器類,類的個數(shù)成對增加,這在一定程度上增加了系統(tǒng)的復(fù)雜性。 |
| 抽象迭代器的設(shè)計難度較大,需要充分考慮到系統(tǒng)將來的擴展。在自定義迭代器時創(chuàng)建一個考慮全面的抽象迭代器并不是一件很容易的事情。 | |
| 適用環(huán)境 | 訪問一個聚合對象的內(nèi)容而無須暴露它的內(nèi)部表示。將聚合對象的訪問與內(nèi)部數(shù)據(jù)的存儲分離,使得訪問聚合對象時無須了解其內(nèi)部實現(xiàn)細節(jié)。 |
| 需要為一個聚合對象提供多種遍歷方式。 | |
| 為遍歷不同的聚合結(jié)構(gòu)提供一個統(tǒng)一的接口,在該接口的實現(xiàn)類中為不同的聚合結(jié)構(gòu)提供不同的遍歷方式,而客戶端可以一致性地操作該接口。 |
19.中介者模式
???????中介者模式將一個網(wǎng)狀的系統(tǒng)結(jié)構(gòu)變成一個以中介者對象為中心的星形結(jié)構(gòu),在這個星形結(jié)構(gòu)中使用中介者對象與其他對象的一對多關(guān)系來取代原有對象之間的多對多關(guān)系。中介者模式在事件驅(qū)動類軟件中的應(yīng)用較為廣泛,特別是基于 GUI(Graphical User Interface,圖形用戶界面)的應(yīng)用軟件。此外,在類與類之間存在錯綜復(fù)雜的關(guān)聯(lián)關(guān)系的系統(tǒng)中,中介者模式也得到了較好的應(yīng)用。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 抽象中介者 (Mediator) | 它定義了一個接口,該接口用于與各同事對象之間進行通信。 |
| 具體中介者 (ConcreteMediator) | 它是抽象中介者的子類,通過協(xié)調(diào)各個同事對象來實現(xiàn)協(xié)作行為,他維持了對各個同事對象的引用。 |
| 抽象同事類 (Colleague) | 它定義了各個同事類公有的方法,并聲明了一些抽象方法供子類實現(xiàn),同時它維持了一個對抽象中介者類的引用,其子類可以通過該引用與中介者通信。 |
| 具體同事類 (ConcreteColleague) | 它是抽象同事類的子類,每一個同事對象在需要和其他同事對象通信時先與中介者通信,通過中介者間接完成與其他同事類的通信;在具體同事類中實現(xiàn)了在抽象同事勒種聲明的抽象方法。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 中介者模式簡化了對象之間的交互,它用中介者和同事的一對多交互替代了原來同事之間的多對多交互,一對多關(guān)系更容易理解、維護和擴展,將原本難以理解的網(wǎng)狀結(jié)構(gòu)轉(zhuǎn)換成相對簡單的星形結(jié)構(gòu)。 |
| 可將各同事對象解耦。中介者有利于各同事之間的松耦合,可以獨立地改變和復(fù)用每一個同事和中介者,增加新的中介者類和新的同事類都比較方便,更好地符合開閉原則。 | |
| 可以減少子類生成,中介者將原本分布于多個對象間的行為集中在一起,改變這些行為只需生成新的中介者子類即可,這使得各個同事類可以被重用,無須直接對同事類進行擴展。 | |
| 缺點 | 在具體中介者類中包含了大量同事之間的交互細節(jié),可能會導(dǎo)致具體中介者類非常復(fù)雜,使得系統(tǒng)難以維護。 |
| 適用環(huán)境 | 系統(tǒng)中對象之間存在復(fù)雜的引用關(guān)系,系統(tǒng)結(jié)構(gòu)混亂且難以理解。 |
| 一個對象由于引用了其他很多對象并且直接和這些對象通信,導(dǎo)致難以復(fù)用該對象。 | |
| 想通過一個中間類來封裝多個類中的行為,而又不想生成太多的子類,此時可以通過引入中介者類來實現(xiàn),在中介者中定義對象交互的公共行為,如果需要改變行為則可以增加新的具體中介者類。 |
20.備忘錄模式
???????備忘錄模式在很多軟件的使用過程中普遍存在,但是在應(yīng)用軟件開發(fā)中它的使用頻率并不太高,因為現(xiàn)在很多基于窗體和瀏覽器的應(yīng)用軟件并沒有提供撤銷操作。如果需要為軟件提供撤銷功能,備忘錄模式無疑是一種很好的解決方案。在一些字處理軟件,圖像編輯軟件、數(shù)據(jù)庫管理系統(tǒng)等軟件中備忘錄模式都得到了很好的應(yīng)用。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 原發(fā)器 (Originator) | 原發(fā)器是一個普通類,他通過創(chuàng)建一個備忘錄來存儲當(dāng)前內(nèi)部狀態(tài),也可以使用備忘錄來恢復(fù)其內(nèi)部狀態(tài),一般將系統(tǒng)中需要保存內(nèi)部狀態(tài)的類設(shè)計為原發(fā)器。 |
| 備忘錄 (Memento) | 備忘錄用于存儲原發(fā)器的內(nèi)部狀態(tài),根據(jù)原發(fā)器來決定保存哪些內(nèi)部狀態(tài)。備忘錄的設(shè)計一般可以參考原發(fā)器的設(shè)計,根據(jù)實際需要確定備忘錄類中的屬性。需要注意的是,除了原發(fā)器本身與負責(zé)人類之外,備忘錄對象不能直接供其他類使用,原發(fā)器的設(shè)計在不同的編程語言中實現(xiàn)機制會有所不同。 |
| 負責(zé)人 (Caretaker) | 負責(zé)人又稱管理者,他負責(zé)保存?zhèn)渫?#xff0c;但是不能對備忘錄的內(nèi)容進行操作或檢查。在負責(zé)人類中可以存儲一個或多個備忘錄對象,它只負責(zé)存儲對象,而不能修改對象,也無須知道對象的實現(xiàn)細節(jié)。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 提供了一種狀態(tài)恢復(fù)的實現(xiàn)機制,使得用戶可以方便地回到一個特定的歷史步驟,當(dāng)新的狀態(tài)無效或者存在問題時可以使用暫時存儲起來的備忘錄將狀態(tài)復(fù)原。 |
| 備忘錄實現(xiàn)了對信息的封裝,一個備忘錄對象是一種原發(fā)器對象狀態(tài)的表示,不會被其他代碼所改動。備忘錄保存了原發(fā)器的狀態(tài),采用列表、堆棧等集合來存儲備忘錄對象可以實現(xiàn)多次撤銷操作。 | |
| 缺點 | 資源消耗過大,如果需要保存的原發(fā)器類的成員變量太多,就不可避免地需要占用大量的存儲空間,每保存一次對象的狀態(tài)都需要消耗一定的系統(tǒng)資源。 |
| 適用環(huán)境 | 保存一個對象在某一個時刻的全部狀態(tài)或部分狀態(tài),這樣以后需要時它能夠恢復(fù)到先前的狀態(tài),實現(xiàn)撤銷操作。 |
| 防止外界對象破壞一個對象歷史狀態(tài)的封裝性,避免將對象歷史狀態(tài)的實現(xiàn)細節(jié)暴露給外界對象。 |
21.觀察者模式
???????觀察者模式是一種使用頻率非常高的設(shè)計模式,無論是移動應(yīng)用、Web 應(yīng)用或者桌面應(yīng)用,觀察者模式幾乎無處不在,它為實現(xiàn)對象之間的聯(lián)動提供了一套完整的解決方案,凡是涉及一對一或者一對多的對象交互場景都可以使用觀察者模式。觀察者模式廣泛應(yīng)用于各種編程語言的 GUI 事件處理的實現(xiàn),在基于事件的 XML 解析技術(shù)(例如 SAX2)以及 Web 事件處理中也都使用了觀察者模式。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 目標(biāo) (Subject) | 目標(biāo)又稱為主題,它是指被觀察的對象。在目標(biāo)中定義了一個觀察者集合,一個觀察目標(biāo)可以接受任意數(shù)量的觀察者來觀察,它提供了一系列方法來在增加和刪除觀察者對象,同時它定義了通知方法 notify() 。目標(biāo)類可以是接口,也可以是抽象類或具體類。 |
| 具體目標(biāo) (ConcreteSubject) | 具體目標(biāo)是目標(biāo)類的子類,它通常包含有經(jīng)常發(fā)生改變的數(shù)據(jù),當(dāng)它的狀態(tài)發(fā)生改變時將向它的各個觀察者發(fā)出通知;同時它還實現(xiàn)了在目標(biāo)類中定義的抽象業(yè)務(wù)邏輯方法(如果有)。如果無須擴展目標(biāo)類,則具體目標(biāo)類可以省略。 |
| 觀察者 (Observer) | 察者將對觀察目標(biāo)的改變做出反應(yīng),觀察者一般定義為接口,該接口聲明了更新數(shù)據(jù)的方法 update() ,因此又稱為抽象觀察者。 |
| 具體觀察者 (ConcreteObserver) | 在具體觀察者中維護一個指向具體目標(biāo)對象的引用,它存儲具體觀察者的有關(guān)狀態(tài),這些狀態(tài)需要和具體目標(biāo)的狀態(tài)保持一致;它實現(xiàn)了在抽象觀察者?Observer 中定義的 update() 方法。通常實現(xiàn)時可以調(diào)用具體目標(biāo)類? attach() 方法將自己添加到目標(biāo)類的集合中或通過 detach() 方法將自己從目標(biāo)類的集合中刪除。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | |
| 在觀察目標(biāo)和觀察者之間建立一個抽象的耦合。觀察目標(biāo)只需要維持一個抽象觀察者的集合,無須了解其具體觀察者。由于觀察目標(biāo)和觀察者沒有緊密地耦合在一起,因此它們可以屬于不同的抽象化層次。 | |
| 支持廣播通信,觀察目標(biāo)會向所有已注冊的觀察者對象發(fā)送通知,簡化了一對多系統(tǒng)設(shè)計的難度。 | |
| 符合開閉原則,增加新的具體觀察者無須修改原有系統(tǒng)代碼,在具體觀察者與觀察目標(biāo)之間不存在關(guān)聯(lián)關(guān)系的情況下增加新的觀察目標(biāo)也很方便。 | |
| 缺點 | 如果一個觀察目標(biāo)對象有很多直接和間接觀察者,將所有的觀察者都通知到會花費很多時間。 |
| 如果在觀察者和觀察目標(biāo)之間存在循環(huán)依賴,觀察目標(biāo)會觸發(fā)它們之間進行循環(huán)調(diào)用,可能導(dǎo)致系統(tǒng)崩潰。 | |
| 觀察者模式?jīng)]有相應(yīng)的機制讓觀察者知道所觀察的目標(biāo)對象是怎么發(fā)生變化的,而僅僅只是知道觀察目標(biāo)發(fā)生了變化。 | |
| 適用環(huán)境 | 一個抽象模型有兩個方面,其中一個方面依賴于另一個方面,將這兩個方面封裝在獨立地對象中使它們可以各自獨立地改變和復(fù)用。 |
| 一個對象的改變將導(dǎo)致一個或多個其他對象也發(fā)生改變,而并不知道具體有多少對象將發(fā)生改變,也不知道這些對象是誰。 | |
| 需要在系統(tǒng)中創(chuàng)建一個觸發(fā)鏈,A 對象的行為將影響B(tài)對象,B 對象的行為將影響 C 對象......,可以使用觀察者模式創(chuàng)建一種鏈式觸發(fā)機制。 |
22.狀態(tài)模式
???????狀態(tài)模式將一個對象在不同狀態(tài)下的不同行為封裝在一個個狀態(tài)類中,通過設(shè)置不同的狀態(tài)對象可以讓環(huán)境對象擁有不同的行為,而狀態(tài)轉(zhuǎn)換的細節(jié)對于客戶端而言是透明的,方便了客戶端的使用。在實際開發(fā)中,狀態(tài)模式具有較高的使用頻率,在工作流、游戲等軟件中狀態(tài)模式都得到了廣泛的應(yīng)用,例如公文狀態(tài)的轉(zhuǎn)換、游戲中角色的升級等。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 環(huán)境類 (Context) | 環(huán)境類又稱為上下文類,它是擁有多種狀態(tài)的對象。由于環(huán)境類的狀態(tài)存在多樣性且在不同狀態(tài)下對象的行為有所不同,因此將狀態(tài)獨立出去形成單獨的狀態(tài)類。在環(huán)境類中維護了一個抽象狀態(tài)類 State 的實例,這個實例定義當(dāng)前狀態(tài),在具體實現(xiàn)時它是一個 State 子類的對象。 |
| 抽象狀態(tài)類 (State) | 它用于定義一個接口以封裝與環(huán)境類的一個特定狀態(tài)相關(guān)的行為,在抽象狀態(tài)類中聲明了各種不同狀態(tài)對應(yīng)的方法,而在其子類中實現(xiàn)了這些方法,由于不同狀態(tài)下對象的行為可能不同,因此在不同的子類中方法的實現(xiàn)可能存在不同,相同的方法可以卸載抽象狀態(tài)類中。 |
| 具體狀態(tài)類 (ConcreteState) | 它是抽象狀態(tài)類的子類,每一個子類實現(xiàn)一個與環(huán)境類的一個狀態(tài)相關(guān)的行為,每一個具體狀態(tài)類對應(yīng)環(huán)境的一個具體狀態(tài),不同的具體狀態(tài)類的行為有所不同。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 狀態(tài)模式封裝了狀態(tài)的轉(zhuǎn)換與規(guī)則,在狀態(tài)模式中可以將狀態(tài)的轉(zhuǎn)換代碼封裝在環(huán)境類或者具體狀態(tài)類中,可以對狀態(tài)轉(zhuǎn)換代碼進行集中管理,而不是分散坐在一個個業(yè)務(wù)方法中。 |
| 狀態(tài)模式將所有與某個狀態(tài)有關(guān)的行為放到一個類中,只需要注入一個不同的狀態(tài)對象即可使環(huán)境對象擁有不同的行為。 | |
| 狀態(tài)模式允許狀態(tài)轉(zhuǎn)換邏輯與狀態(tài)對象合成一體,而不是提供一個巨大的條件語句塊,狀態(tài)模式可以避免使用龐大的條件語句將業(yè)務(wù)方法和狀態(tài)轉(zhuǎn)換代碼交織在一起。 | |
| 狀態(tài)模式可以讓多個環(huán)境對象共享一個狀態(tài)對象,從而減少系統(tǒng)中對象的個數(shù)。 | |
| 缺點 | 狀態(tài)模式會增加系統(tǒng)中類和對象的個數(shù),導(dǎo)致系統(tǒng)運行開銷增大。 |
| 狀態(tài)模式的結(jié)構(gòu)與實現(xiàn)都較為復(fù)雜,如果使用不當(dāng)將導(dǎo)致程序結(jié)構(gòu)和代碼的混亂,增加系統(tǒng)設(shè)計的難度。 | |
| 狀態(tài)模式對開閉原則的支持并不太好,增加新的狀態(tài)類需要修改那些負責(zé)狀態(tài)轉(zhuǎn)換的源代碼,否則無法轉(zhuǎn)換到新增的狀態(tài);而且修改某個狀態(tài)類的行為也需要修改對應(yīng)類的源代碼。 | |
| 適用環(huán)境 | 對象的行為依賴于它的狀態(tài)(例如某些屬性值),狀態(tài)的改變將導(dǎo)致行為的變化。 |
| 在代碼中包含大量與對象狀態(tài)有關(guān)的條件語句,這些條件語句的出現(xiàn)會導(dǎo)致代碼的可維護性和靈活性變差,不能方便地增加和刪除狀態(tài),并且導(dǎo)致客戶類與類庫之間的耦合增強。 |
23.策略模式
???????策略模式用于算法的自由切換和擴展,它是應(yīng)用較為廣泛的設(shè)計模式之一。策略模式對應(yīng)于解決某一個問題的一個算法族,允許用戶來解決某一問題,同時可以方便地更換算法或者增加新的算法,只要涉及算法的封裝、復(fù)用和切換都可以考慮使用策略模式。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 環(huán)境類 (Context) | 環(huán)境類是使用算法的角色,它在解決某個問題(即實現(xiàn)某個功能)時可以采用多種策略。在環(huán)境類中維持一個對抽象策略類的引用實例,用于定義所采用的策略。 |
| 抽象策略類 (Strategy) | 抽象策略類為所支持的算法聲明了抽象方法,是所有策略類的父類,它可以是抽象類或具體類,也可以是接口。環(huán)境類通過抽象策略類中聲明的方法在運行時調(diào)用具體策略類中實現(xiàn)的算法。 |
| 具體策略類 (ConcreteStrategy) | 具體策略類實現(xiàn)了在抽象策略類中聲明的算法,在運行時具體策略類將覆蓋在環(huán)境類中定義的抽象策略類對象,使用一種具體的算法實現(xiàn)某個業(yè)務(wù)功能。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 策略模式提供了對開閉原則的完美支持,用戶可以在不修改原有系統(tǒng)的基礎(chǔ)上選擇算法或行為,也可以靈活地增加新的算法或行為。 |
| 策略模式提供了管理相關(guān)的算法族的辦法。策略模式的等級結(jié)構(gòu)定義了一個算法或行為族,恰當(dāng)?shù)厥褂美^承可以把公共的代碼移到抽象策略類中,從而避免重復(fù)的代碼。 | |
| 策略模式提供了一種可以替換繼承關(guān)系的辦法,如果不使用策略模式,那么使用算法的環(huán)境類就可能會有一些子類,每一個子類提供一種不同的算法。但是這樣一來算法的使用就和算法本身混在一起,不符合單一職責(zé)原則,決定使用哪一種算法的邏輯和該算法本身混合在一起,從而不可能再獨立演化;而使用繼承無法實現(xiàn)算法或行為在程序運行時的動態(tài)切換。 | |
| 使用策略模式可以避免多重條件選擇語句。多重條件選擇語句不易維護,他把采取哪一種算法或行為的邏輯與算法或行為本身的實現(xiàn)邏輯混合在一起,將它們?nèi)坑簿幋a在一個龐大的多重條件選擇語句中,比直接繼承環(huán)境類的辦法還要原始和落后。 | |
| 策略模式提供了一種算法的復(fù)用機制,由于將算法單獨提取出來封裝在策略模式類中,因此不同的環(huán)境類可以方便地復(fù)用這些策略類。 | |
| 缺點 | 客戶端必須知道所有的策略類,并自行決定使用哪一個策略類,這就意味著客戶端必須理解這些算法的區(qū)別,以便適當(dāng)時選擇恰當(dāng)?shù)乃惴āQ而言之,策略模式只適用于客戶端知道所有算法或行為的情況。 |
| 策略模式將造成系統(tǒng)產(chǎn)生很多具體策略類,任何細小的變化都將導(dǎo)致系統(tǒng)要增加一個新的具體策略類。 | |
| 無法同時在客戶端使用多個策略類,也就是說,在使用策略模式時客戶端每次只能使用一個策略類,不支持使用一個策略類完成部分功能后再使用另一個策略類完成剩余功能的情況。 | |
| 適用環(huán)境 | 一個系統(tǒng)需要動態(tài)地在幾種算法中選擇一種,那么可以將這些算法封裝到一個個的具體算法類中,而這些具體算法類都是一個抽象算法類的子類。換而言之,這些具體算法類均有統(tǒng)一的接口,根據(jù)里氏代換原則和面向?qū)ο蟮亩鄳B(tài)性,客戶端可以選擇使用任何一個具體算法類,并只需要維持一個數(shù)據(jù)類型是抽象算法類的對象。 |
| 一個對象有很多行為,如果不用恰當(dāng)?shù)啬J?#xff0c;這些行為則只好使用多重條件選擇語句來實現(xiàn)。此時使用策略模式把這些行為轉(zhuǎn)移到相應(yīng)的具體策略類里面,就可以避免使用難以維護的多重條件選擇語句。 | |
| 不希望客戶端知道復(fù)雜的、與算法相關(guān)的數(shù)據(jù)結(jié)構(gòu),在具體策略類中封裝算法與相關(guān)的數(shù)據(jù)結(jié)構(gòu),可以提高算法的保密性與安全性。 |
24.模板方法模式
???????模板方法模式是基于繼承的代碼復(fù)用技術(shù),它體現(xiàn)了面向?qū)ο蟮闹T多重要思想,是一種使用較為頻繁的設(shè)計模式。模板方法模式廣泛應(yīng)用于框架設(shè)計(例如 Spring、JUnit 等)中,以確保通過父類來控制處理流程的邏輯程序(例如框架的初始化、測試流程的設(shè)置等)。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 抽象類 (AbstractClass) | 在抽象類中定義了一系列基本操作(Primitive Operations),這些基本操作可以是具體的,也可以是抽象的,每一個基本操作對應(yīng)算法的一個步驟,在其子類中可以重定義或?qū)崿F(xiàn)這些步驟。同時在抽象類中實現(xiàn)了一個模板方法(Template Method),用于定義一個算法的框架,模板方法不僅可以調(diào)用在抽象類中實現(xiàn)的基本方法,也可以調(diào)用在抽象類的子類中實現(xiàn)的基本方法,還可以調(diào)用其他對象中的方法。 |
| 具體子類 (ConcreteClass) | 它是抽象類的子類,用于實現(xiàn)在父類中聲明的抽象基本操作以完成子類特定算法的步驟,也可以覆蓋在父類中已實現(xiàn)的具體基本操作。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 在父類中形式化地定義一個算法,而由它的子類來實現(xiàn)細節(jié)的處理,在子類實現(xiàn)詳細的處理算法時并不會改變算法中步驟的執(zhí)行次序。 |
| 模板方法模式是一種代碼復(fù)用技術(shù),在類庫設(shè)計中尤為重要,它提取了類庫中的公共行為,將公共行為放在父類中,而通過其子類實現(xiàn)不同的行為,它鼓勵用戶恰當(dāng)?shù)厥褂美^承來實現(xiàn)代碼復(fù)用。 | |
| 模板方法模式可以實現(xiàn)一種反向控制結(jié)構(gòu),通過子類覆蓋父類的鉤子方法來決定某一特定步驟是否需要執(zhí)行。 | |
| 在模板方法模式中可以通過子類來覆蓋父類的基本方法,不同的子類可以提供基本方法的不同實現(xiàn),更換和增加新的子類很方便,符合單一職責(zé)原則和開閉原則。 | |
| 缺點 | 在模板方法模式中需要為每一個基本方法的不同實現(xiàn)提供一個子類,如果父類中可變的基本方法太多,將會導(dǎo)致類的個數(shù)增加,系統(tǒng)更加龐大,設(shè)計也更加抽象,此時可結(jié)合橋接模式進行設(shè)計。 |
| 適用環(huán)境 | 對一些復(fù)雜的算法進行分割,將其算法中固定不變的部分設(shè)計為模板方法和父類具體方法,而一些可以改變的細節(jié)由其子類來實現(xiàn)。即一次性實現(xiàn)一個算法的不變部分,并將可變的行為留給子類來實現(xiàn)。 |
| 各子類中公共的行為應(yīng)被提取出來并集中到一個公共父類中以避免代碼重復(fù)。 | |
| 需要通過子類來決定父類算法中的某個步驟是否執(zhí)行,實現(xiàn)子類對父類的方向控制。 |
25.訪問者模式
???????由于訪問者模式的使用條件較為苛刻,本身結(jié)構(gòu)也較為復(fù)雜,因此在實際應(yīng)用中使用頻率不是也別高。當(dāng)系統(tǒng)中存在一個較為復(fù)雜的對象結(jié)構(gòu),且不同訪問者對其所采取的操作也不相同時,可以考慮使用訪問者模式進行設(shè)計。在 XML 文檔解析、編譯器的設(shè)計、復(fù)雜集合對象的處理等領(lǐng)域中訪問者模式得到了一定的應(yīng)用。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?組成對象
| 抽象訪問者 (Visitor) | 抽象訪問者為對象結(jié)構(gòu)中的每一個具體元素類聲明一個訪問操作,從這個操作的名稱或參數(shù)類型可以清楚地知道需要訪問的具體元素的類型,具體訪問者需要實現(xiàn)這些操作方法,定義對這些元素的訪問操作。 |
| 具體訪問者 (ConcreteVisitor) | 具體訪問者實現(xiàn)了每個由抽象訪問者聲明的操作,每一個操作用于訪問對象結(jié)構(gòu)中一種類型的元素。 |
| 抽象元素 (Element) | 抽象元素一般是抽象類或者接口,它聲明了一個 accept()方法,用于接受訪問者的訪問操作,該方法通常以一個抽象訪問者作為參數(shù)。 |
| 具體元素 (ConcreteElement) | 具體元素實現(xiàn)了 accept() 方法,在 accept() 方法中調(diào)用訪問者的訪問方法以便完成對一個元素的操作。 |
| 對象結(jié)構(gòu) (ObjectStructure) | 對象結(jié)構(gòu)是一個元素的集合,它用于存放元素對象,并且提供了遍歷其內(nèi)部原生的方法。對象結(jié)構(gòu)可以結(jié)合組合模式來實現(xiàn),也可以是一個簡單的集合對象。 |
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?場景分析
| 優(yōu)點 | 在訪問者模式中增加新的訪問操作很方便。使用訪問者模式,增加新的訪問操作就意味著增加一個新的具體訪問者類,實現(xiàn)簡單,無須修改源代碼,符合開閉原則。 |
| 訪問者模式將有關(guān)元素對象的訪問行為集中到一個訪問者對象中,而不是分散在一個個的元素類中。類的職責(zé)更加清晰,有利于對象結(jié)構(gòu)中元素對象的復(fù)用,相同的對象結(jié)構(gòu)可以供多個不同的訪問者訪問。 | |
| 訪問者模式讓用戶能夠在不修改現(xiàn)有元素類層次結(jié)構(gòu)的情況下定義作用于該層次結(jié)構(gòu)的操作。 | |
| 缺點 | 在訪問者模式中增加新的元素類很困難。在訪問者模式中,每增加一個新的元素類都意味著要在抽象訪問者角色中增加一個新的抽象操作,并在每一個具體訪問者類中增加相應(yīng)的具體操作,這違背了開閉原則的要求。 |
| 訪問者面膜是破壞了對象的封裝性。訪問者模式要求訪問者對象訪問并調(diào)用每一個元素對象的操作,這意味著元素對象有時候必須暴露一些自己的內(nèi)部操作和內(nèi)部狀態(tài),否則無法供訪問者訪問。 | |
| 適用環(huán)境 | 一個對象結(jié)構(gòu)包含多個類型的對象,希望對這些對象實施一些依賴其具體類型的操作。在訪問者中針對每一種具體的類型都提供了一個訪問操作,不同類型的對象可以有不同的訪問操作。 |
| 需要對一個對象結(jié)構(gòu)中的對象進行很多不同的并且不相關(guān)的操作,而需要避免讓這些操作“污染”這些對象的類,也不希望在增加新操作時修改這些類。訪問者模式使得用戶可以將相關(guān)的訪問操作集中起來定義在訪問者類中,對象結(jié)構(gòu)可以被多個不同的訪問者類所使用的,將對象本身與對象的訪問操作分離。 | |
| 對象結(jié)構(gòu)中對象對應(yīng)的類很少改變,但經(jīng)常需要在此對象結(jié)構(gòu)上定義新的操作。 |
總結(jié)
以上是生活随笔為你收集整理的Java设计模式——GoF设计模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html的排版标题的是,HTML 5结构
- 下一篇: javascript --- type