相關閱讀 |
>>> 小城故事吳儂軟語溫婉人心的力量 >>> | 簡體 傳統 |
《C#面向對象設計模式縱橫談》學習筆記
創建型模式
1. Singleton 單件
動機:
在軟件系統中,經常有這樣一些特殊的類,必須保證它們在系統中只存在一個實例,才能確保它們的邏輯正確性、以及良好的效率。
如何繞過常規的構造器,提供一種機制來保證一個類只有一個實例?
這應該是類設計者的責任,而不是使用者的責任。
意圖:
保證一個類僅有一個實例,并提供一個該實例的全局訪問點。
——《設計模式》GoF
結構:
單線程Singleton模式的幾個要點:
·Singleton模式中的實例構造器可以設置為protected以允許子類派生。
·Singleton模式一般不要支持ICloneable接口,因為這可能會導致多個對象實例,
與Singleton模式的初衷違背.
·Singleton模式個一般不要支持序列化,因為這也有可能導致多個對象實例,同樣與Singleton模式的初衷違背.
·Singleton模式只考慮到了對象創建的管理,沒有考慮對象銷毀的管理。就支持垃圾回收的平臺和對象的開銷來講,我們一般沒有必要對其銷毀進行特殊的管理。
·不能應對多線程環境:在多線程環境下,使用Singleton模式仍然有可能得到Singleton類的多個實例對象。
Singleton模式擴展:
·將一個實例擴展到n個實例,例如對象池的實現。
·將new構造器的調用轉移到其他類中,例如多個類協同工作環境中,某個局部環境只需要擁有某個類的一個實例。
·理解和擴展Singleton模式的核心是“如何控制用戶使用new對一個類的實例構造器的任意調用”。
Class Singleton
{
public static readonly Singleton Instance = new Singleton();
private Singleton() { }
}
2.Abstract Factory 抽象工廠
動機(Motivation):在軟件系統中,經常面臨著“一系列相互依賴的對象”的創建工作;同時,由于需求的變化,往往存在更多系列對象的創建工作。
如何應對這種變化?如何繞過常規的對象創建方法(new),提供一種“封裝機制”來避免客戶程序和這種“多系列具體對象創建工作”的緊耦合?
意圖(Intent):提供一個接口,讓該接口負責創建一系列“相關或者相互依賴的對象”,無需指定它們具體的類。 ——《設計模式》GoF
結構(Structure):
模式的幾個要點:
·如果沒有應對“多系列對象構建”的需求變化,則沒有必要使用Abstract Factory模式,這時候使用簡單的靜態工廠完全可以。
·“系列對象”指的是這些對象之間有相互依賴、或作用的關系,例如游戲開發場景中的“道路”與“房屋”的依賴,“道路”與“地道”的依賴。
·Abstract Factory模式主要在于應對“新系列”的需求變動。其缺點在于難以應對“新對象”的需求變動。
·Abstract Factory模式經常和Factory Method模式共同組合來應對“對象創建”的需求變化。
3.Builder 生成器模式
動機:在軟件系統中,有時候面臨著“一個復雜對象”的創建工作,其通常由各個部分的子對象用一定的算法構成;由于需求的變化,這個復雜對象的各個部分經常面臨著劇烈的變化,但是將它們組合在一起的算法卻相對穩定。
如何應對這種變化?如何提供一種“封裝機制”來隔離出“復雜對象的各個部分”的變化,從而保持系統中的“穩定構建算法”不隨著需求改變而改變?
意圖:將一個復雜的構建與其表示相分離,使得同樣的構建過程可以創建不同的表示。——《設計模式》GoF
結構:
協作(Collaborations):
模式的幾個要點:
·Builder模式主要用于“分步驟構建一個復雜的對象”。在這其中“分步驟”是一個穩定的算法,而復雜對象的各個部分則經常變化。
·變化點在哪里,封裝哪里——Builder模式主要在于應對“復雜對象各個部分”的頻繁需求變動。其缺點在于難以應對“分步驟構建算法”的需求變動。
·Abstract Factory模式解決“系列對象”的需求變化,Builder模式解決“對象部分”的需求變化。Builder模式通常和Composite模式組合使用。
4.Factory Method 工廠方法模式
動機:在軟件系統中,經常面臨著“某個對象”的創建工作;由于需求的變化,這個對象經常面臨著劇烈的變化,但是它卻擁有比較穩定的接口。
如何應對這種變化?如何提供一種“封裝機制”來隔離出“這個易變對象”的變化,從而保持系統中“其他依賴該對象的對象”不隨著需求改變而改變?
意圖:定義一個用于創建對象的接口,讓子類決定實例化哪一個類。Factory Method使得一個類的實例化延遲到子類。 ——《設計模式》GoF
結構:
模式的幾個要點:
·Factory Method模式主要用于隔離類對象的使用者和具體類型之間的耦合關系。面對一個經常變化的具體類型,緊耦合關系會導致軟件的脆弱。
·Factory Method模式通過面向對象的手法,將所要創建的具體對象工作延遲到子類,從而實現一種擴展(而非更改)的策略,較好地解決了這種緊耦合關系。
·Factory Method模式解決“單個對象”的需求變化,Abstract Factory模式解決“系列對象”的需求變化,Builder模式解決“對象部分”的需求變化。
5.Prototype 原型模式
動機:
在軟件系統中,經常面臨著“某些結構復雜的對象”的創建工作;由于需求的變化,這些對象經常面臨著劇烈的變化,但是它們卻擁有比較穩定一致的接口。
如何應對這種變化?如何向“客戶程序(使用這些對象的程序)”隔離出“這些易變對象”,從而使得“依賴這些易變對象的客戶程序”不隨著需求改變而改變?
意圖:
使用原型實例指定創建對象的種類,然后通過拷貝這些原型來創建新的對象。
——《設計模式》GoF
結構:
模式的幾個要點:
·Prototype模式同樣用于隔離類對象的使用者和具體類型(易變類)之間的耦合關系,它同樣要求這些“易變類”擁有“穩定的接口”。
·Prototype模式對于“如何創建易變類的實體對象”采用“原型克隆”的方法來做,它使得我們可以非常靈活地動態創建“擁有某些穩定接口”的新對象——所需工作僅僅是注冊一個新類的對象(即原型),然后在任何需要的地方不斷地Clone。
·Prototype模式中的Clone方法可以利用.NET中的Object類的MemberwiseClone()方法或者序列化來實現深拷貝。
有關創建性模式的討論
·Singleton模式解決的是實體對象個數的問題。除了Singleton之外,其他創建型模式解決的都是new所帶來的耦合關系。
·Factory Method, Abstract Factory, Builder都需要一個額外的工廠類來負責實例化“易變對象”,而Prototype則是通過原型(一個特殊的工廠類)來克隆“易變對象”。
·如果遇到“易變類”,起初的設計通常從Factory Method開始,當遇到更多的復雜變化時,再考慮重構為其他三種工廠模式(Abstract Factory, Builder, Prototype)。
結構型模式
6. Adapter 適配器模式
動機:
在軟件系統中,由于應用環境的變化,常常需要將“一些現存的對象”放在新的環境中應用,但是新環境要求的接口是這些現存對象所不滿足的。
如何應對這種“遷移的變化”?如何既能利用現有對象的良好實現,同時又能滿足新的應用環境所要求的接口?
意圖:
將一個類的接口轉換成客戶希望的另一個接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作。 ——《設計模式》GoF
結構:
對象適配器
類適配器
Adapter模式的幾個要點
·Adapter模式主要應用于“希望復用一些現存的類,但是接口又與復用環境要求不一致的情況”,在遺留代碼復用、類庫遷移等方面非常有用。
·GoF定義了兩種Adapter模式的實現結構:對象適配器和類適配器。但類適配器采用“多繼承”的實現,帶來了不良的高耦合,所以一般不推薦使用。對象適配器采用“對象組合”的方式,更符合松耦合精神。
·Adapter模式可以實現的非常靈活,不必拘泥于GoF中定義的兩種結構。例如,完全可以將Adapter模式中的“現存對象”作為新的接口方法參數,來達到適配的目的。
·Adapter模式本身要求我們盡可能地使用“面向接口的編程”風格,這樣才能在后期很方便地適配。
7. Bridge 橋接模式
動機:
如何應對這種“多維度的變化”?如何利用面向對象技術來使得Tank類型可以輕松地沿著“平臺”和“型號”兩個方向變化,而不引入額外的復雜度?
意圖:
將抽象部分與實現部分分離,使它們都可以獨立地變化。 ——《設計模式》GoF
結構:
Bridge模式的幾個要點:
·Bridge模式使用“對象間的組合關系”解耦了抽象和實現之間固有的綁定關系,使得抽象(Tank的型號)和實現(不同的平臺)可以沿著各自的維度來變化。
·所謂抽象和實現沿著各自緯度的變化,即“子類化”它們,比如不同的Tank型號子類,和不同的平臺子類)。得到各個子類之后,便可以任意組合它們,從而獲得不同平臺上的不同型號。
·Bridge模式有時候類似于多繼承方案,但是多繼承方案往往違背單一職責原則(即一個類只有一個變化的原因),復用性比較差。Bridge模式是比多繼承方案更好的解決方法。
·Bridge模式的應用一般在“兩個非常強的變化維度”,有時候即使有兩個變化的維度,但是某個方向的變化維度并不劇烈——換方之兩個變化不會導致縱橫交錯的結果,并不一定要使用Bridge模式。
8. Composite 組合模式
動機:
客戶代碼過多地依賴于對象容器復雜的內部實現結構,對象容器內部實現結構(而非抽象接口)的變化將引起客戶代碼的頻繁變化,帶來了代碼的維護性、擴展性等弊端。
如何將“客戶代碼與復雜的對象容器結構”解耦?讓對象容器自己來實現自身的復雜結構,從而使得客戶代碼就像處理簡單對象一樣來處理復雜的對象容器?
意圖:
將對象組合成樹形結構以表示“部分—整體”的層次結構。Composite使得用戶對單個對象和組合對象的使用具有一致性。 ——《設計模式》GoF
結構:
Composite模式的幾個要點
·Composite模式采用樹形結構來實現普遍存在的對象容器,從而將“一對多”的關系轉化為“一對一”的關系,使得客戶代碼可以一致地處理對象和對象容器,無需關心處理的是單個的對象,還是組合的對象容器。
·將“客戶代碼與復雜的對象容器結構”解耦是Composite模式的核心思想,解耦之后,客戶代碼將與純粹的抽象接口——而非對象容器的復內部實現結構——發生依賴關系,從而更能“應對變化”。
·Composite模式中,是將“Add和Remove等和對象容器相關的方法”定義在“表示抽象對象的Component類”中,還是將其定義在“表示對象容器的Composite類”中,是一個關乎“透明性”和“安全性”的兩難問題,需要仔細權衡。這里有可能違背面向對象的“單一職責原則”,但是對于這種特殊結構,這又是必須付出的代價。ASP.NET控件的實現在這方面為我們提供了一個很好的示范。
·Composite模式在具體實現中,可以讓父對象中的子對象反向追溯;如果風對象有頻敏的遍歷需求,可使用緩存技巧來改善效率。
9. Decorator 裝飾模式
動機:
我們“過度地使用了繼承來擴展對象的功能”,由于繼承為類型引入的靜態特質,使得這種擴展方式缺乏靈活性;并且隨著子類的增多(擴展功能的增多),各種子類的組合(擴展功能的組合)會導致更多子類的膨脹(多繼承)。
如何使“對象功能的擴展”能夠根據需要來動態地實現?同時避免“擴展功能的增多”帶來的子類膨脹問題?從而使得任何“功能擴展變化”所導致的影響將為最低?
意圖:
動態地給一個對象增加一些額外的職責。就增加功能而言,Decorator模式比生成子類更為靈活。 ——《設計模式》GoF
結構:
Decorator模式的幾個要點:
·通過采用組合、而非繼承的手法,Decorator模式實現了在運行時動態地擴展對象功能的能力,而且可以根據需要擴展多個功能。避免了單獨使用繼承帶來的“靈活性差”和“多子類衍生問題”。
·Component類在Decorator模式中充當抽象接口的角色,不應該去實現具體的行為。而且Decorator類對于Component類應該透明——換言之Component類無需知道Decorator類,Decorator類是從外部來擴展Component類的功能。
· Decorator類在接口上表現為is-a Component的繼承關系,即Decorator類繼承了Component類所具有的接口。但在實現上又表現為has-a Component的組合關系,即Decorator類又使用了另外一個Component類。我們可以使用一個或者多個Decorator對象來“裝飾”一個Component對象,且裝飾后的對象仍然是一個Component對象。
·Decorator模式并非解決“多子類衍生的多繼承”問題,Decorator模式應用的要點在于解決“主體類在多個方向上的擴展功能”——是為“裝飾”的含義。
10. Facade 外觀模式
動機:
組合的客戶和組件中各種復雜的子系統有了過多的耦合,隨著外部客戶程序和各子系統的演化,這種過多的耦合面臨很多變化的挑戰。
如何簡化外部客戶程序和系統間的交互接口?如何將外部客戶程序的演化和內部子系統的變化之間的依賴相互解耦?
意圖:
為子系統中的一組接口提供一個一致的界面,Façade模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。 ——《設計模式》GoF
結構:
Façade模式的幾個要點:
·從客戶程序的角度來看,Façade模式不僅簡化了整個組件系統的接口,同時對于組件內部與外部客戶程序來說,從某種程度上也達到了一種“解耦”的效果——內部子系統的任何變化不會影響到Façade接口的變化。
·Façade設計模式更注重從架構的層次去看整個系統,而不是單個類的層次。Façade很多時候更是一種架構設計模式。
·注意區分Façade模式、Adapter模式、Bridge模式與Decorator模式。Façade模式注重簡化接口,Adapter模式注重轉換接口,Bridge模式注重分離接口(抽象)與其實現,Decorator模式注重穩定接口的前提下為對象擴展功能。
11. Flyweight 享元模式
動機:
采用純粹對象方案的問題在于大量細粒度的對象會很快充斥在系統中,從而帶來很高的運行時代價——主要指內在需求方面的代價。
如何在避免大量細粒度對象問題的同時,讓外部客戶程序仍然能夠透明地使用面向對象的方式來進行操作?
意圖:
運用共享技術有效地支持大量細粒度的對象。 ——《設計模式》GoF
結構:
Flyweight模式的幾個要點:
·面向對象很好地解決了抽象性的問題,但是作為一個運行在機器中的程序實體,我們需要考慮對象的代價問題。Flyweight設計模式主要解決面向對象的代價問題,一般不觸主面向對象的抽象性問題。
·Flyweight采用對象共享的做法來降低系統中對象的個數,從而降低細粒度對象給系統帶來的內存壓力。在具體實現方面,要注意對象狀態的處理。
·對象的數量太大從而導致對象內存開銷加大——什么樣的數量才算大?
這需要我們仔細的根據具體應用情況進行評估,而不能憑空臆斷。
12. Proxy 代理模式
動機:
在面向對象系統中,有些對象由于某種原因(比如對象創建的開銷很大,或者某些操作需要安全控制,或者需要進程外的訪問等),直接訪問會給使用、或者系統結構帶來很多麻煩。
如何在不失去透明操作對象的同時來管理/控制這些對象特有的復雜性?增加一層間接層是軟件開發中常見的解決方式。
意圖:
為其他對象提供一種代理以控制對這個對象的訪問。 ——《設計模式》GoF
結構:
Proxy模式的幾個要點:
·“增加一層間接層”是軟件系統中對許多復雜問題的一種常見解決方法。在面向對象系統中,直接使用某些對象會帶來很多問題,作為間接層的proxy對象便是解決這一問題的常用手段。
·具體proxy設計模式的實現方法、實現粒度都相差很大,有些可能對單個對象做細粒度的控制,如copy-on-write技術,有些可能對組件模塊提供抽象代理層,在架構層次對對象做proxy。
·Proxy并不一定要求保持接口的一致性,只要能夠實現間接控制,有時候損及一些透明性是可以接受的。
行為型模式
13. Template Method模板方法
動機:
在軟件構建過程中,對于某一項任務,它常常有穩定的整體操作結構,但各個子步驟卻有很多改變的需求,或者由于固有的原因(比如框架與應用之間的關系)而無法和任務的整體結構同時實現。
如何在確定穩定操作結構的前提下,來靈活應對各個子步驟的變化或者晚期實現需求?
意圖:
定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。Template Method使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。
——《設計模式》GoF
結構:
Template Method模式的幾個要點:
·Template Method模式是一種非常基礎性的設計模式,在面向對象系統中有著大量的應用。它用最簡潔的機制(虛函數的多態性)為很多應用程序框架提供了靈活的擴展點,是代碼復用方面的基本實現結構。
·除了可以靈活應對子步驟的變化外,“不要調用我,讓我來調用你”的反向控制結構是Template Method的典型應用。
·在具體實現方面,被Template Method調用的虛方法可以具有實現,也可以沒有任何實現(抽象方法、純虛方法),但一般推薦它們設置為protected方法。
14. Command命令
動機:
在軟件構建過程中,“行為請求者”與“行為實現者”通常呈現一種“緊耦合”。但在某些場合——比如需要對行為進行“記錄、撤銷/重做(undo/redo)、事務”等處理,這種無法抵御變化的緊耦合是不合適的。
在這種情況下,如何將“行為請求者”與“行為實現者”解耦?將一組行為抽象為對象,可以實現二者之間的松耦合。
意圖:
將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數化;對請求排隊或記錄請求日志,以及支持可撤銷的操作。 ——《設計模式》GoF
結構:
Command模式的幾個要點:
·Command模式的根本目的在于將“行為請求者”與“行為實現者”解耦,在面向對象語言中,常見的實現手段是“將行為抽象為對象”。
· 實現Command接口的具體命令對象ConcreteCommand有時候根據需要可能會保存一些額外的狀態信息。
·Command模式與C#中的Delegate有些類似。但兩者定義行為接口的規范有所區別:Command以面向對象中的“接口-實現”來定義行為接口規范,更嚴格,更符合抽象原則;Delegate以函數簽名來定義行為接口規范,更靈活,但抽象能力比較弱。
15. Interpreter解釋器
動機:
在軟件構建過程中,如果某一特定領域的問題比較復雜,類似的模式不斷重復出現,如果使用普通的編程方式來實現將面臨非常頻繁的變化。
在這種情況下,將特定領域的問題表達為某種語法規則下的句子,然后構建一個解釋器來解釋這樣的句子,從而達到解決問題的目的。
意圖:
給定一個語言,定義它的文法的一種表示,并定義一種解釋器,這個解釋器使用該表示來解釋語言中的句子。 ——《設計模式》GoF
結構:
Interpreter模式的幾個要點:
·Interpreter模式的應用場合是Interpreter模式應用中的難點,只有滿足“業務規則頻繁變化,且類似的模式不斷重復出現,并且容易抽象為語法規則的問題”才適合使用Interpreter模式。
·使用Interpreter模式來表示文法規則,從而可以使用面向對象技巧來方便地“擴展”文法。
·Interpreter模式比較適合簡潔的文法表示,對于復雜的文法表示,Interpreter模式會產生比較大的類層次結構,需要求助于語法分析生成器這樣的標準工具。
16. Mediator中介者
動機:
在軟件構建過程中,經常會出現多個對象互相關聯交互的情況,對象之間常常會維持一種復雜的引用關系,如果遇到一些需求的更改,這種直接的引用關系將面臨不斷的變化。
在這種情況下,我們可使用一個“中介對象”來管理對象間的關聯關系,避免相互交互的對象之間的緊耦合引用關系,從而更好地抵御變化。
意圖:
用一個中介對象來封裝一系列的對象交互。中介者使各對象不需要顯式的相互引用,從而使其耦合松散,而且可以獨立地改變它們之間的交互。 ——《設計模式》GoF
結構:
Mediator模式的幾個要點:
·將多個對象間復雜的關聯關系解耦,Mediator模式將多個對象間的控制邏輯進行集中管理,變“多個對象互相關聯”為“多個對象和一個中介者關聯”,簡化了系統的維護,抵御了可能的變化。
·隨著控制邏輯的復雜化,Mediator具體對象的實現可能相當復雜。這時候可以對Mediator對象進行分解處理。
·Façade模式是解耦系統外到系統內(單向)的對象關聯關系;Mediator模式是解耦系統內各個對象之間(雙向)的關聯關系。
17. Iterator迭代器
動機:
在軟件構建過程中,集合對象內部結構常常變化各異。但對于這些集合對象,我們希望在不暴露其內部結構的同時,可以讓外部客戶代碼透明地訪問其中包含的元素;同時這種“透明遍歷”也為“同一種算法在多種集合對象上進行操作”提供了可能。
使用面向對象技術將這種遍歷機制抽象為“迭代器對象”為“應對變化中的集合對象”提供了一種優雅的方式。
意圖:
提供一種方法順序訪問一個聚合對象中的各個元素,而又不暴露該對象的內部表示。
——《設計模式》GoF
結構:
Iterator模式的幾個要點:
·迭代抽象:訪問一個聚合對象的內容而無需暴露它的內部表示。
·迭代多態:為遍歷不同的集合結構提供一個統一的接口,從而支持同樣的算法在不同的集合結構上進行操作。
·迭代器的健壯性考慮:遍歷的同時更改迭代器所在的集合結構,會導致問題。
18. Observer 觀察者模式
動機:
在軟件構建過程中,我們需要為某些對象建立一種“通知依賴關系”——一個對象(目標對象)的狀態發生改變,所有的依賴對象(觀察者對象)都將得到通知。如果這樣的依賴關系過于緊密,將使軟件不能很好地抵御變化。
使用面向對象技術,可以將這種依賴關系弱化,并形成一種穩定的依賴關系。從而實現軟件體系結構的松耦合。
意圖:
定義對象間的一種一對多的依賴關系,以便當一個對象的狀態發生改變時,所有依賴于它的對象都得到通知并自動更新。 ——《設計模式》GoF
結構:
Observer模式的幾個要點:
·使用面向對象的抽象,Observer模式使得我們可以獨立地改變目標與觀察者,從而使二者之間的依賴關系達到松耦合。
·目標發送通知時,無需指定觀察者,通知(可以攜帶通知信息作為參數)會自動傳播。觀察者自己決定是否需要訂閱通知,目標對象對此一無所知。
·在C#的event中,委托充當了抽象的Observer接口,而提供事件的對象充當了目標對象。委托是比抽象Observer接口更為松耦合的設計。
19. Chain of Responsibility模式
動機:
在軟件構建過程中,一個請求可能被多個對象處理,但是每個請求在運行時只能有一個接受者,如果顯式指定,將必不可少地帶來請求發送者與接受者的緊耦合。
如何使請求的發送者不需要指定具體的接受者?讓請求的接受者自己在運行時決定來處理請求,從而使兩者解耦。
意圖:
使多個對象有機會處理請求,從而避免請求的發送者和接受收者之間的耦合關系。將這些對象連成一條鏈,并沿著這條鏈傳遞請求,直到有一個對象處理它為止。
——《設計模式》GoF
結構:
Chain of Responsibility模式的幾個要點:
·Chain of Responsibility模式的應用場合在于“一個請求可能有多個接受者,但是最后具正的接受者只有一個”,只有這時候請求發送者與接受者的耦合才有可能出現“變化脆弱”的癥狀,職責鏈的目的就是將二者解耦,從而更好地應對變化。
·應用了Chain of Responsibility模式后,對象的職責分派將更具靈活性。我們可以在運行時動態添加/修改請求的處理職責。
·如果請求傳遞到職責鏈的末尾仍得不到處理,應該有一個合理的缺省機制。這也是每一個接受對象的責任,而不是發出請求的對象的責任。
20. Memento備忘錄模式
動機:
在軟件構建過程中,某些對象的狀態在轉換過程中,可能由于某種需要,要求程序能夠回溯到對象之前處于某個點時的狀態。如果使用一些公有接口來讓其他對象得到對象的狀態,便會暴露對象的細節實現。
如何實現對象狀態的良好保存與恢復?但同時又不會因此而破壞對象本身的封閉性。
意圖(Intent)
在不破壞封閉性的前提下,捕獲一個對象的內部狀態,并在該對象之外保存這個狀態。這樣以后就可以將該對象恢復到原先保存的狀態。 ——《設計模式》GoF
結構(Structure)
Memento模式的幾個要點:
·備忘錄(Memento)存儲原發器(Originator)對象的內部狀態,在需要時恢復原發器狀態。Memento模式適用于“由原發器管理,卻又必須存儲在原發器之外的信息”
·在實現Memento模式中,要防止原發器以外的對象訪問備忘錄對象。備忘錄對象有兩個接口,一個為原發器使用的寬接口;一個為其他對象使用的窄接口。
·在實現Memento模式時,要考慮拷貝對象狀態的效率問題,如果對象開銷比較大,可以采用某種增量式改變來改進Memento模式。
21. State 狀態模式
動機:
在軟件構建過程中,某些對象的狀態如果改變,其行為也會隨之而發生變化,比如文檔處于只讀狀態,其支持的行為和讀寫狀態支持的行為就可能完全不同。
如何在運行時根據對象的狀態來透明地更改對象的行為?而不會為對象操作和狀態轉化之間引入緊耦合?
意圖(Intent)
允許一個對象在其內部狀態改變時改變它的行為。從而使對象看起來似乎修改了其行為。
——《設計模式》GoF
結構(Structure)
State模式的幾個要點
·State模式將所有與一個特定狀態相關的行為都放入一個State的子類對象中,在對象狀態切換時,切換相應的對象;但同時維持State的接口,這樣實現了具體操作與狀態轉換之間的解耦。
·為不同的狀態引入不同的對象使得狀態轉換變得更加明確,而且可以保證不會出現狀態不一致的情況,因為轉換是原子性的——要么徹底轉換過來,要么不轉換。
·如果State對象沒有實例變量,那么各個上下文可以共享同一個State對象,從而節省對象開銷。
22. Strategy 策略模式
動機(Motivation):
在軟件構建過程中,某些對象使用的算法可能多種多樣,經常改變,如果將這些算法都編碼到對象中,將會使對象變得異常復雜;而且有時候支持不使用的算法也是一個性能負担。
如何在運行時根據需要透明地更改對象的算法?將算法與對象本身解耦,從而避免上述問題?
意圖(Intent)
定義一系列算法,把它們一個個封閉起來,并且使它們可互相替換。該模式使得算法可獨立于使用它的客戶而變化。 ——《設計模式》GoF
結構(Structure)
Strategy模式的幾個要點
·Strategy及其子類為組件提供了一系列可重用的算法,從而可以使得類型在運行時方便地根據需要在各個算法之間進行切換。所謂封裝算法,支持算法的變化。
·Strategy模式提供了用條件判斷語句以外的另一種選擇,消除條件判斷語句,就是在解耦合。含有許多條件判斷語句的代碼通常都需要Strategy模式。
·與State類似,如果Strategy對象沒有實例變量,那么各個上下文可以共享同一個Strategy對象,從而節省對象開銷。
. Visitor 訪問者模式
動機(Motivation):
在軟件構建過程中,由于需求的改變,某些類層次結構中常常需要增加新的行為(方法),如果直接在基類中做這樣的更改,將會給子類帶來很繁重的變更負担,甚至破壞原有設計。
如何在不更改類層次結構的前提下,在運行時根據需要透明地為類層次結構上的各個類動態添加新的操作,從而避免上述問題?
意圖(Intent)
表示一個作用于某對象結構中的各元素的操作。它可以在不改變各元素的類的前提下定義作用于這些元素的新的操作。 ——《設計模式》GoF
結構(Structure)
Vistor模式的幾個要點:
·Vistor模式通過所謂雙重分發(double dispatch)來實現在不更改Element類層次結構的前提下,在運行時透明地為類層次結構上的各個類動態添加新的操作。
·所謂雙重分發即Visitor模式中間包括了兩個多態分發(注意其中的多態機制):第一個為accetp方法的多態辨析;第二個為visit方法的多態辨析。
·Visitor模式的最大缺點在于擴展類層次結構(增添新的Element子類),會導致Visistor類的改變。因此Visitor模式適用于“Element類層次結構穩定,而其中的操作卻經常面臨頻繁改動”。
. 設計模式總結
創建型模式
·Singleton模式解決的是實體對象個數的問題。除了Singleton之外,其他創建型模式解決的都是new所帶來的耦合關系。
·Factory Method, Abstract Factory, Builder都需要一個額外的工廠類來負責實例“易變對象”,而Prototype則是通過原型(一個特殊的工廠類)來克隆“易變對象”。
·如果遇到“易變類”,起初的設計通常從Factory Method開始,當遇到更多的復雜變化時,再考慮重構為其他三種工廠模式(Abstract Factory, Builder, Prototype)。
結構型模式
·Adapter模式注重轉換接口,將不吻合的接口適配對接
·Bridge模式注重分離接口與其實現,支持多維度變化
·Composite模式注重統一接口,將“一對多”的關系轉化為“一對一”的關系
·Decorator模式注重穩定接口,在此前提下為對象擴展功能
·Façade模式注重簡化接口,簡化組件系統與外部客戶程序的依賴關系
·Flyweight模式注重保留接口,在內部使用共享技術對對象存儲進行優化
·Proxy模式注重假借接口,增加間接層來實現靈活控制
行為型模式(1)
·Template Method模式封裝算法,支持算法的變化
·State模式注重封裝與狀態相關的行為,支持狀態的變化
·Memento模式注重封裝對象狀態變化,支持狀態保存/恢復
·Mediator模式注重封裝對象間的交互,支持對象交互的變化
行為型模式(2)
·Chain Of Responsibility模式注重封裝對象責任,支持責任的變化
·Command模式注重將請求封裝為對象,支持請求的變化
·Iterator模式注重封裝集合對象內部結構,支持集合的變化
·Interpreter模式注重封裝特定領域變化,支持領域問題的頻繁變化
·Observer模式注重封裝對象通知,支持通信對象的變化
·Visitor模式注重封裝對象操作變化,支持在運行時為類層次結構動態添加新的操作。
設計模式應用總結
·設計模式建立在對系統變化點的基礎上進行,哪里有變化點,哪里應用設計模式。
·設計模式應該以演化的方式來獲得,系統的變化點往往是經過不斷演化才能準確定位。
·不能為了模式而模式,設計模式是一種軟件設計的軟力量,而非規范標準。不應夸大設計模式的作用。
小樂 2014-06-23 11:46:36
稱謂:
内容: