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