1. 程式人生 > >23中設計模式類圖和原理詳解

23中設計模式類圖和原理詳解

clip_image001

意圖:

定義一個用於建立物件的介面,讓子類決定例項化哪一個類。Factory Method 使個類的例項化延遲到其子類。

適用性:

當一個類不知道它所必須建立的物件的類的時候。

當一個類希望由它的子類來指定它所建立的物件的時候。

當類將建立物件的職責委託給多個幫助子類中的某一個,並且你希望將哪一個幫助子類是代理者這一資訊區域性化的時候。

clip_image002

意圖:

提供一個建立一系列相關或相互依賴物件的介面,而無需指定它們具體的類。 
適用性:

一個系統要獨立於它的產品的建立、組合和表示時。

一個系統要由多個產品系列中的一個來配置時。

當你要強調一系列相關的產品物件的設計以便進行聯合使用時。

當你提供一個產品類庫,而只想顯示它們的介面而不是實現時。

clip_image003

意圖:

將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。

適用性:

當建立複雜物件的演算法應該獨立於該物件的組成部分以及它們的裝配方式時。

當構造過程必須允許被構造的物件有不同的表示時。

clip_image004

意圖:

用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件。

適用性:

當要例項化的類是在執行時刻指定時,例如,通過動態裝載;或者

為了避免建立一個與產品類層次平行的工廠類層次時;或者

當一個類的例項只能有幾個不同狀態組合中的一種時。建立相應數目的原型並克隆它們可能比每次用合適的狀態手工例項化該類更方便一些。

clip_image005

意圖:

保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。

適用性:

當類只能有一個例項而且客戶可以從一個眾所周知的訪問點訪問它時。

當這個唯一例項應該是通過子類化可擴充套件的,並且客戶應該無需更改程式碼就能使用一個擴充套件的例項時。

clip_image006
意圖:

將一個類的介面轉換成客戶希望的另外一個介面。Adapter 模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。 
適用性:

你想使用一個已經存在的類,而它的介面不符合你的需求。

你想建立一個可以複用的類,該類可以與其他不相關的類或不可預見的類(即那些介面可能不一定相容的類)協同工作。

(僅適用於物件Adapter )你想使用一些已經存在的子類,但是不可能對每一個都進行子類化以匹配它們的介面。物件介面卡可以適配它的父類介面。

clip_image007

意圖:

將抽象部分與它的實現部分分離,使它們都可以獨立地變化。

適用性:

你不希望在抽象和它的實現部分之間有一個固定的繫結關係。例如這種情況可能是因為,在程式執行時刻實現部分應可以被選擇或者切換。

類的抽象以及它的實現都應該可以通過生成子類的方法加以擴充。這時Bridge 模式使你可以對不同的抽象介面和實現部分進行組合,並分別對它們進行擴充。

對一個抽象的實現部分的修改應對客戶不產生影響,即客戶的程式碼不必重新編譯。

(C++)你想對客戶完全隱藏抽象的實現部分。在C++中,類的表示在類介面中是可見的。

有許多類要生成。這樣一種類層次結構說明你必須將一個物件分解成兩個部分。Rumbaugh 稱這種類層次結構為“巢狀的普化”(nested generalizations )。

你想在多個物件間共享實現(可能使用引用計數),但同時要求客戶並不知道這一點。一個簡單的例子便是Coplien 的String 類[ Cop92 ],在這個類中多個物件可以共享同一個字串表示(StringRep )。

clip_image008
意圖:

將物件組合成樹形結構以表示“部分-整體”的層次結構。C o m p o s i t e 使得使用者對單個物件和組合物件的使用具有一致性。 
適用性:

你想表示物件的部分-整體層次結構。

你希望使用者忽略組合物件與單個物件的不同,使用者將統一地使用組合結構中的所有物件。

clip_image009
意圖: 
動態地給一個物件新增一些額外的職責。就增加功能來說,Decorator 模式相比生成子類更為靈活。 
適用性:

在不影響其他物件的情況下,以動態、透明的方式給單個物件新增職責。

處理那些可以撤消的職責。

當不能採用生成子類的方法進行擴充時。一種情況是,可能有大量獨立的擴充套件,為支援每一種組合將產生大量的子類,使得子類數目呈爆炸性增長。另一種情況可能是因為類定義被隱藏,或類定義不能用於生成子類。

clip_image010

意圖:

為子系統中的一組介面提供一個一致的介面,Facade模式定義了一個高層介面,這個介面使得這一子系統更加容易使用。

適用性:

當你要為一個複雜子系統提供一個簡單介面時。子系統往往因為不斷演化而變得越來越複雜。大多數模式使用時都會產生更多更小的類。這使得子系統更具可重用性,也更容易對子系統進行定製,但這也給那些不需要定製子系統的使用者帶來一些使用上的困難。Facade可以提供一個簡單的預設檢視,這一檢視對大多數使用者來說已經足夠,而那些需要更多的可定製性的使用者可以越過facade層。

客戶程式與抽象類的實現部分之間存在著很大的依賴性。引入facade 將這個子系統與客戶以及其他的子系統分離,可以提高子系統的獨立性和可移植性。

當你需要構建一個層次結構的子系統時,使用facade模式定義子系統中每層的入口點。如果子系統之間是相互依賴的,你可以讓它們僅通過facade進行通訊,從而簡化了它們之間的依賴關係。

clip_image011

意圖:

運用共享技術有效地支援大量細粒度的物件。

適用性:

一個應用程式使用了大量的物件。

完全由於使用大量的物件,造成很大的儲存開銷。

物件的大多數狀態都可變為外部狀態。

如果刪除物件的外部狀態,那麼可以用相對較少的共享物件取代很多組物件。

應用程式不依賴於物件標識。由於Flyweight 物件可以被共享,對於概念上明顯有別的物件,標識測試將返回真值。

clip_image012

意圖:

為其他物件提供一種代理以控制對這個物件的訪問。

適用性:

在需要用比較通用和複雜的物件指標代替簡單的指標的時候,使用Proxy模式。下面是一些可以使用Proxy 模式常見情況: 
1) 遠端代理(Remote Proxy )為一個物件在不同的地址空間提供區域性代表。NEXTSTEP[Add94] 使用NXProxy 類實現了這一目的。Coplien[Cop92] 稱這種代理為“大使” (Ambassador )。 
2 )虛代理(Virtual Proxy )根據需要建立開銷很大的物件。在動機一節描述的ImageProxy 就是這樣一種代理的例子。 
3) 保護代理(Protection Proxy )控制對原始物件的訪問。保護代理用於物件應該有不同 的訪問許可權的時候。例如,在Choices 作業系統[ CIRM93]中KemelProxies為作業系統物件提供 了訪問保護。 
4 )智慧指引(Smart Reference )取代了簡單的指標,它在訪問物件時執行一些附加操作。 它的典型用途包括:

對指向實際物件的引用計數,這樣當該物件沒有引用時,可以自動釋放它(也稱為SmartPointers[Ede92 ] )。

當第一次引用一個持久物件時,將它裝入記憶體。

在訪問一個實際物件前,檢查是否已經鎖定了它,以確保其他物件不能改變它。

clip_image013

意圖:

給定一個語言,定義它的文法的一種表示,並定義一個直譯器,這個直譯器使用該表示來解釋語言中的句子。

適用性:

當有一個語言需要解釋執行, 並且你可將該語言中的句子表示為一個抽象語法樹時,可使用直譯器模式。而當存在以下情況時該模式效果最好:

該文法簡單對於複雜的文法, 文法的類層次變得龐大而無法管理。此時語法分析程式生成器這樣的工具是更好的選擇。它們無需構建抽象語法樹即可解釋表示式, 這樣可以節省空間而且還可能節省時間。

效率不是一個關鍵問題最高效的直譯器通常不是通過直接解釋語法分析樹實現的, 而是首先將它們轉換成另一種形式。例如,正則表示式通常被轉換成狀態機。但即使在這種情況下, 轉換器仍可用直譯器模式實現, 該模式仍是有用的。

clip_image014

意圖:

定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中。TemplateMethod 使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟。

適用性:

一次性實現一個演算法的不變的部分,並將可變的行為留給子類來實現。

各子類中公共的行為應被提取出來並集中到一個公共父類中以避免程式碼重複。這是Opdyke 和Johnson 所描述過的“重分解以一般化”的一個很好的例子[ OJ93 ]。首先識別現有程式碼中的不同之處,並且將不同之處分離為新的操作。最後,用一個呼叫這些新的操作的模板方法來替換這些不同的程式碼。

控制子類擴充套件。模板方法只在特定點呼叫“hook ”操作(參見效果一節),這樣就只允許在這些點進行擴充套件。

clip_image015

意圖:

使多個物件都有機會處理請求,從而避免請求的傳送者和接收者之間的耦合關係。將這些物件連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個物件處理它為止。

適用性:

有多個的物件可以處理一個請求,哪個物件處理該請求執行時刻自動確定。

你想在不明確指定接收者的情況下,向多個物件中的一個提交一個請求。

可處理一個請求的物件集合應被動態指定。

clip_image016

意圖:

將一個請求封裝為一個物件,從而使你可用不同的請求對客戶進行引數化;對請求排隊或記錄請求日誌,以及支援可撤消的操作。

適用性:

抽象出待執行的動作以引數化某物件,你可用過程語言中的回撥(call back)函式表達這種引數化機制。所謂回撥函式是指函式先在某處註冊,而它將在稍後某個需要的時候被呼叫。Command 模式是回撥機制的一個面向物件的替代品。

在不同的時刻指定、排列和執行請求。一個Command物件可以有一個與初始請求無關的生存期。如果一個請求的接收者可用一種與地址空間無關的方式表達,那麼就可將負責該請求的命令物件傳送給另一個不同的程序並在那兒實現該請求。

支援取消操作。Command的Excute 操作可在實施操作前將狀態儲存起來,在取消操作時這個狀態用來消除該操作的影響。Command 介面必須新增一個Unexecute操作,該操作取消上一次Execute呼叫的效果。執行的命令被儲存在一個歷史列表中。可通過向後和向前遍歷這一列表並分別呼叫Unexecute和Execute來實現重數不限的“取消”和“重做”。

支援修改日誌,這樣當系統崩潰時,這些修改可以被重做一遍。在Command介面中新增裝載操作和儲存操作,可以用來保持變動的一個一致的修改日誌。從崩潰中恢復的過程包括從磁碟中重新讀入記錄下來的命令並用Execute操作重新執行它們。

用構建在原語操作上的高層操作構造一個系統。這樣一種結構在支援事務( transaction)的資訊系統中很常見。一個事務封裝了對資料的一組變動。Command模式提供了對事務進行建模的方法。Command有一個公共的介面,使得你可以用同一種方式呼叫所有的事務。同時使用該模式也易於新增新事務以擴充套件系統。

clip_image017

意圖:

提供一種方法順序訪問一個聚合物件中各個元素, 而又不需暴露該物件的內部表示。

適用性:

訪問一個聚合物件的內容而無需暴露它的內部表示。

支援對聚合物件的多種遍歷。

為遍歷不同的聚合結構提供一個統一的介面(即, 支援多型迭代)。

clip_image018

意圖:

用一箇中介物件來封裝一系列的物件互動。中介者使各物件不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的互動。

適用性:

一組物件以定義良好但是複雜的方式進行通訊。產生的相互依賴關係結構混亂且難以理解。

一個物件引用其他很多物件並且直接與這些物件通訊,導致難以複用該物件。

想定製一個分佈在多個類中的行為,而又不想生成太多的子類。

clip_image019

意圖:

在不破壞封裝性的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態。這樣以後就可將該物件恢復到原先儲存的狀態。

適用性:

必須儲存一個物件在某一個時刻的(部分)狀態, 這樣以後需要時它才能恢復到先前的狀態。

如果一個用介面來讓其它物件直接得到這些狀態,將會暴露物件的實現細節並破壞物件的封裝性。

clip_image020

意圖:

定義物件間的一種一對多的依賴關係,當一個物件的狀態發生改變時, 所有依賴於它的物件都得到通知並被自動更新。

適用性:

當一個抽象模型有兩個方面, 其中一個方面依賴於另一方面。將這二者封裝在獨立的物件中以使它們可以各自獨立地改變和複用。

當對一個物件的改變需要同時改變其它物件, 而不知道具體有多少物件有待改變。

當一個物件必須通知其它物件,而它又不能假定其它物件是誰。換言之, 你不希望這些物件是緊密耦合的。

clip_image021

意圖:

允許一個物件在其內部狀態改變時改變它的行為。物件看起來似乎修改了它的類。

適用性:

一個物件的行為取決於它的狀態, 並且它必須在執行時刻根據狀態改變它的行為。

一個操作中含有龐大的多分支的條件語句,且這些分支依賴於該物件的狀態。這個狀態通常用一個或多個列舉常量表示。通常, 有多個操作包含這一相同的條件結構。State模式將每一個條件分支放入一個獨立的類中。這使得你可以根據物件自身的情況將物件的狀態作為一個物件,這一物件可以不依賴於其他物件而獨立變化。

clip_image022

意圖:

定義一系列的演算法,把它們一個個封裝起來, 並且使它們可相互替換。本模式使得演算法可獨立於使用它的客戶而變化。

適用性:

許多相關的類僅僅是行為有異。“策略”提供了一種用多個行為中的一個行為來配置一個類的方法。

需要使用一個演算法的不同變體。例如,你可能會定義一些反映不同的空間/時間權衡的演算法。當這些變體實現為一個演算法的類層次時[H087] ,可以使用策略模式。

演算法使用客戶不應該知道的資料。可使用策略模式以避免暴露覆雜的、與演算法相關的資料結構。

一個類定義了多種行為, 並且這些行為在這個類的操作中以多個條件語句的形式出現。將相關的條件分支移入它們各自的Strategy類中以代替這些條件語句。

clip_image023

意圖:

定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中。TemplateMethod 使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟。

適用性:

一次性實現一個演算法的不變的部分,並將可變的行為留給子類來實現。

各子類中公共的行為應被提取出來並集中到一個公共父類中以避免程式碼重複。這是Opdyke和Johnson所描述過的“重分解以一般化”的一個很好的例子[OJ93]。首先識別現有程式碼中的不同之處,並且將不同之處分離為新的操作。最後,用一個呼叫這些新的操作的模板方法來替換這些不同的程式碼。

控制子類擴充套件。模板方法只在特定點呼叫“hook ”操作(參見效果一節),這樣就只允許在這些點進行擴充套件。