圖解 23 種設計模式
點擊下方“IT牧場”,選擇“設為星標”

一、單一職責原則
二、開放-封閉原則
軟件實體應該可以擴展,但不可修改。該原則是面向?qū)ο笤O計的核心所在,遵循這個原則可以帶來面向?qū)ο蠹夹g所聲稱的可維護、可擴展、可復用、靈活性好。
設計人員必須對于他設計的模塊應該對哪種變化封閉做出選擇,必須先猜測出最有可能發(fā)生的變化種類,然后構造抽象來隔離那些變化。最初編寫程序時假設變化不會發(fā)生,當變化發(fā)生時,就創(chuàng)建抽象來隔離以后發(fā)生的同類變化,拒絕不成熟的抽象。
三、里氏代換原則
子類型必須能夠替換掉它們的父類型。由于子類型的可替換性才使得使用父類類型的模塊在無需修改的情況下就可以擴展。
四、依賴倒轉(zhuǎn)原則
高層模塊不應該依賴低層模塊,兩個都應該依賴抽象;抽象不應該依賴細節(jié),細節(jié)應該依賴抽象。
要針對接口編程,不要針對實現(xiàn)編程。該原則可以說是面向?qū)ο笤O計的標志,編寫時考慮的是如何對抽象編程而不是針對細節(jié)編程,即程序中所有的依賴關系都是終止于抽象類或者接口。
五、迪迷特原則(最少知識原則)
如果兩個類不必彼此直接通信,那么這兩個類就不應當發(fā)生直接的相互作用;如果其中一個類需要調(diào)用另一個類的某一個方法的話,可以通過第三者轉(zhuǎn)發(fā)這個調(diào)用。
該原則其根本思想,是強調(diào)了類之間的松耦合;類之間的耦合越弱,越利于復用,一個處在弱耦合的類被修改,不會對有關系的類造成波及。在類的結構設計上,每一個類都應當盡量降低成員的訪問權限。
六、合成/聚合復用原則
盡量使用合成/聚合,盡量不要使用類繼承。
聚合表示一種弱的“擁有”關系,體現(xiàn)的是A對象可以包含B對象,但B對象不是A對象的一部分;合成則是一種強的“擁有”關系,體現(xiàn)了嚴格的部分和整體的關系,部分和整體的生命周期一樣。
優(yōu)先使用對象的合成/聚合將有助于你保持每個類被封裝,并被擊中在單個任務上,這樣類和類繼承層次會保持較小規(guī)模,并且不太可能增長為不可控制的龐然大物。
七、UML例圖
‘+’表示public,‘-’表示private,‘#’表示protected;
接口頂端有《interface》顯示,只有兩行;同時另一個表示方法為棒棒糖表示法;聚合表示一種弱的’擁有’關系,體現(xiàn)的是A對象可以包含B對象,但B對象不是A對象的一部分;
合成是一種強的’擁有’關系,體現(xiàn)了嚴格的部分和整體的關系,部分和整體的生命周期一樣;

在UML類圖中,常見的有以下幾種關系: 泛化(Generalization), ?實現(xiàn)(Realization),關聯(lián)(Association),聚合(Aggregation),組合(Composition),依賴(Dependency)
1. 泛化(Generalization)
【泛化關系】:是一種繼承關系,表示一般與特殊的關系,它指定了子類如何特化父類的所有特征和行為。例如:老虎是動物的一種,即有老虎的特性也有動物的共性。
【箭頭指向】:帶三角箭頭的實線,箭頭指向父類

2. 實現(xiàn)(Realization)
【實現(xiàn)關系】:是一種類與接口的關系,表示類是接口所有特征和行為的實現(xiàn).
【箭頭指向】:帶三角箭頭的虛線,箭頭指向接口

3. 關聯(lián)(Association)
【關聯(lián)關系】:是一種擁有的關系,它使一個類知道另一個類的屬性和方法;如:老師與學生,丈夫與妻子關聯(lián)可以是雙向的,也可以是單向的。雙向的關聯(lián)可以有兩個箭頭或者沒有箭頭,單向的關聯(lián)有一個箭頭。
【代碼體現(xiàn)】:成員變量
【箭頭及指向】:帶普通箭頭的實心線,指向被擁有者

上圖中,老師與學生是雙向關聯(lián),老師有多名學生,學生也可能有多名老師。但學生與某課程間的關系為單向關聯(lián),一名學生可能要上多門課程,課程是個抽象的東西他不擁有學生。
下圖為自身關聯(lián):

4. 聚合(Aggregation)
【聚合關系】:是整體與部分的關系,且部分可以離開整體而單獨存在。如車和輪胎是整體和部分的關系,輪胎離開車仍然可以存在。
聚合關系是關聯(lián)關系的一種,是強的關聯(lián)關系;關聯(lián)和聚合在語法上無法區(qū)分,必須考察具體的邏輯關系。
【代碼體現(xiàn)】:成員變量
【箭頭及指向】:帶空心菱形的實心線,菱形指向整體

5. 組合(Composition)
【組合關系】:是整體與部分的關系,但部分不能離開整體而單獨存在。如公司和部門是整體和部分的關系,沒有公司就不存在部門。
組合關系是關聯(lián)關系的一種,是比聚合關系還要強的關系,它要求普通的聚合關系中代表整體的對象負責代表部分的對象的生命周期。
【代碼體現(xiàn)】:成員變量
【箭頭及指向】:帶實心菱形的實線,菱形指向整體

6. 依賴(Dependency)
【依賴關系】:是一種使用的關系,即一個類的實現(xiàn)需要另一個類的協(xié)助,所以要盡量不使用雙向的互相依賴.
【代碼表現(xiàn)】:局部變量、方法的參數(shù)或者對靜態(tài)方法的調(diào)用
【箭頭及指向】:帶箭頭的虛線,指向被使用者

各種關系的強弱順序:
泛化 = 實現(xiàn) > 組合 > 聚合 > 關聯(lián) > 依賴
下面這張UML圖,比較形象地展示了各種類圖關系:

1. Factory Method(工廠方法)

意圖:
定義一個用于創(chuàng)建對象的接口,讓子類決定實例化哪一個類。Factory Method 使一個類的實例化延遲到其子類。
適用性:
當一個類不知道它所必須創(chuàng)建的對象的類的時候。當一個類希望由它的子類來指定它所創(chuàng)建的對象的時候。
當類將創(chuàng)建對象的職責委托給多個幫助子類中的某一個,并且你希望將哪一個幫助子類是代理者這一信息局部化的時候。
2. Abstract Factory(抽象工廠)

意圖:
提供一個創(chuàng)建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。
適用性:
一個系統(tǒng)要獨立于它的產(chǎn)品的創(chuàng)建、組合和表示時。一個系統(tǒng)要由多個產(chǎn)品系列中的一個來配置時。
當你要強調(diào)一系列相關的產(chǎn)品對象的設計以便進行聯(lián)合使用時。當你提供一個產(chǎn)品類庫,而只想顯示它們的接口而不是實現(xiàn)時。
3. Builder(建造者)

意圖:
將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創(chuàng)建不同的表示。
適用性:
當創(chuàng)建復雜對象的算法應該獨立于該對象的組成部分以及它們的裝配方式時。當構造過程必須允許被構造的對象有不同的表示時。
4. Prototype(原型)

意圖:
用原型實例指定創(chuàng)建對象的種類,并且通過拷貝這些原型創(chuàng)建新的對象。
適用性:
當要實例化的類是在運行時刻指定時,例如,通過動態(tài)裝載;或者為了避免創(chuàng)建一個與產(chǎn)品類層次平行的工廠類層次時;或者當一個類的實例只能有幾個不同狀態(tài)組合中的一種時。建立相應數(shù)目的原型并克隆它們可能比每次用合適的狀態(tài)手工實例化該類更方便一些。
5. Singleton(單例)

意圖:
保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
適用性:
當類只能有一個實例而且客戶可以從一個眾所周知的訪問點訪問它時。當這個唯一實例應該是通過子類化可擴展的,并且客戶應該無需更改代碼就能使用一個擴展的實例時
6. Adapter Class/Object(適配器)

意圖:
將一個類的接口轉(zhuǎn)換成客戶希望的另外一個接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作。
適用性:
你想使用一個已經(jīng)存在的類,而它的接口不符合你的需求。
你想創(chuàng)建一個可以復用的類,該類可以與其他不相關的類或不可預見的類(即那些接口可能不一定兼容的類)協(xié)同工作。
(僅適用于對象Adapter )你想使用一些已經(jīng)存在的子類,但是不可能對每一個都進行子類化以匹配它們的接口。對象適配器可以適配它的父類接口。
7. Bridge(橋接)

意圖:
將抽象部分與它的實現(xiàn)部分分離,使它們都可以獨立地變化。
適用性:
你不希望在抽象和它的實現(xiàn)部分之間有一個固定的綁定關系。例如這種情況可能是因為,在程序運行時刻實現(xiàn)部分應可以被選擇或者切換。
類的抽象以及它的實現(xiàn)都應該可以通過生成子類的方法加以擴充。這時Bridge 模式使你可以對不同的抽象接口和實現(xiàn)部分進行組合,并分別對它們進行擴充。
對一個抽象的實現(xiàn)部分的修改應對客戶不產(chǎn)生影響,即客戶的代碼不必重新編譯。
(C++)你想對客戶完全隱藏抽象的實現(xiàn)部分。在C++中,類的表示在類接口中是可見的。
有許多類要生成。這樣一種類層次結構說明你必須將一個對象分解成兩個部分。Rumbaugh 稱這種類層次結構為“嵌套的普化”(nested generalizations )。
你想在多個對象間共享實現(xiàn)(可能使用引用計數(shù)),但同時要求客戶并不知道這一點。一個簡單的例子便是Coplien 的String 類[ Cop92 ],在這個類中多個對象可以共享同一個字符串表示(StringRep )。
8. Composite(組合)

意圖:
將對象組合成樹形結構以表示“部分-整體”的層次結構。C o m p o s i t e 使得用戶對單個對象和組合對象的使用具有一致性。
適用性:
你想表示對象的部分-整體層次結構。你希望用戶忽略組合對象與單個對象的不同,用戶將統(tǒng)一地使用組合結構中的所有對象。
9. Decorator(裝飾)

意圖:
動態(tài)地給一個對象添加一些額外的職責。就增加功能來說,Decorator 模式相比生成子類更為靈活。
適用性:
在不影響其他對象的情況下,以動態(tài)、透明的方式給單個對象添加職責。處理那些可以撤消的職責。
當不能采用生成子類的方法進行擴充時。一種情況是,可能有大量獨立的擴展,為支持每一種組合將產(chǎn)生大量的子類,使得子類數(shù)目呈爆炸性增長。另一種情況可能是因為類定義被隱藏,或類定義不能用于生成子類。
10. Facade(外觀)

意圖:
為子系統(tǒng)中的一組接口提供一個一致的界面,F(xiàn)acade模式定義了一個高層接口,這個接口使得這一子系統(tǒng)更加容易使用。
適用性:
當你要為一個復雜子系統(tǒng)提供一個簡單接口時。子系統(tǒng)往往因為不斷演化而變得越來越復雜。大多數(shù)模式使用時都會產(chǎn)生更多更小的類。這使得子系統(tǒng)更具可重用性,也更容易對子系統(tǒng)進行定制,但這也給那些不需要定制子系統(tǒng)的用戶帶來一些使用上的困難。Facade 可以提供一個簡單的缺省視圖,這一視圖對大多數(shù)用戶來說已經(jīng)足夠,而那些需要更多的可定制性的用戶可以越過facade層。
客戶程序與抽象類的實現(xiàn)部分之間存在著很大的依賴性。引入facade 將這個子系統(tǒng)與客戶以及其他的子系統(tǒng)分離,可以提高子系統(tǒng)的獨立性和可移植性。
當你需要構建一個層次結構的子系統(tǒng)時,使用facade模式定義子系統(tǒng)中每層的入口點。如果子系統(tǒng)之間是相互依賴的,你可以讓它們僅通過facade進行通訊,從而簡化了它們之間的依賴關系。
11. Flyweight(享元)

意圖:
運用共享技術有效地支持大量細粒度的對象。
適用性:
一個應用程序使用了大量的對象。完全由于使用大量的對象,造成很大的存儲開銷。對象的大多數(shù)狀態(tài)都可變?yōu)橥獠繝顟B(tài)。
如果刪除對象的外部狀態(tài),那么可以用相對較少的共享對象取代很多組對象。應用程序不依賴于對象標識。由于Flyweight 對象可以被共享,對于概念上明顯有別的對象,標識測試將返回真值。
12. Proxy(代理)

意圖:
為其他對象提供一種代理以控制對這個對象的訪問。
適用性:
在需要用比較通用和復雜的對象指針代替簡單的指針的時候,使用Proxy模式。下面是一 些可以使用Proxy 模式常見情況:
遠程代理(Remote Proxy )為一個對象在不同的地址空間提供局部代表。NEXTSTEP[Add94] 使用NXProxy 類實現(xiàn)了這一目的。Coplien[Cop92] 稱這種代理為“大使” (Ambassador )。
虛代理(Virtual Proxy )根據(jù)需要創(chuàng)建開銷很大的對象。在動機一節(jié)描述的ImageProxy 就是這樣一種代理的例子。
保護代理(Protection Proxy )控制對原始對象的訪問。保護代理用于對象應該有不同 的訪問權限的時候。例如,在Choices 操作系統(tǒng)[ CIRM93]中KemelProxies為操作系統(tǒng)對象提供 了訪問保護。
智能指引(Smart Reference )取代了簡單的指針,它在訪問對象時執(zhí)行一些附加操作。它的典型用途包括:
對指向?qū)嶋H對象的引用計數(shù),這樣當該對象沒有引用時,可以自動釋放它(也稱為SmartPointers[Ede92 ] )。
當?shù)谝淮我靡粋€持久對象時,將它裝入內(nèi)存。
在訪問一個實際對象前,檢查是否已經(jīng)鎖定了它,以確保其他對象不能改變它。
13. Interpreter(解釋器)

意圖:
給定一個語言,定義它的文法的一種表示,并定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。
適用性:
當有一個語言需要解釋執(zhí)行, 并且你可將該語言中的句子表示為一個抽象語法樹時,可使用解釋器模式。而當存在以下情況時該模式效果最好:
該文法簡單對于復雜的文法, 文法的類層次變得龐大而無法管理。此時語法分析程序生成器這樣的工具是更好的選擇。它們無需構建抽象語法樹即可解釋表達式, 這樣可以節(jié)省空間而且還可能節(jié)省時間。
效率不是一個關鍵問題最高效的解釋器通常不是通過直接解釋語法分析樹實現(xiàn)的, 而是首先將它們轉(zhuǎn)換成另一種形式。例如,正則表達式通常被轉(zhuǎn)換成狀態(tài)機。但即使在這種情況下, 轉(zhuǎn)換器仍可用解釋器模式實現(xiàn), 該模式仍是有用的。
14. Template Method(模板方法)

意圖:
定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。TemplateMethod 使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。
適用性:
一次性實現(xiàn)一個算法的不變的部分,并將可變的行為留給子類來實現(xiàn)。
各子類中公共的行為應被提取出來并集中到一個公共父類中以避免代碼重復。這是Opdyke 和Johnson 所描述過的“重分解以一般化”的一個很好的例子[ OJ93 ]。首先識別現(xiàn)有代碼中的不同之處,并且將不同之處分離為新的操作。最后,用一個調(diào)用這些新的操作的模板方法來替換這些不同的代碼。
控制子類擴展。模板方法只在特定點調(diào)用“hook ”操作(參見效果一節(jié)),這樣就只允許在這些點進行擴展。
15. Chain of Responsibility(責任鏈)

意圖:
使多個對象都有機會處理請求,從而避免請求的發(fā)送者和接收者之間的耦合關系。將這些對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有一個對象處理它為止。
適用性:
有多個的對象可以處理一個請求,哪個對象處理該請求運行時刻自動確定。你想在不明確指定接收者的情況下,向多個對象中的一個提交一個請求。可處理一個請求的對象集合應被動態(tài)指定。
16. Command(命令)

意圖:
將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數(shù)化;對請求排隊或記錄請求日志,以及支持可撤消的操作。
適用性:
抽象出待執(zhí)行的動作以參數(shù)化某對象,你可用過程語言中的回調(diào)(call back)函數(shù)表達這種參數(shù)化機制。所謂回調(diào)函數(shù)是指函數(shù)先在某處注冊,而它將在稍后某個需要的時候被調(diào)用。Command 模式是回調(diào)機制的一個面向?qū)ο蟮奶娲贰?/span>
在不同的時刻指定、排列和執(zhí)行請求。一個Command對象可以有一個與初始請求無關的生存期。如果一個請求的接收者可用一種與地址空間無關的方式表達,那么就可將負責該請求的命令對象傳送給另一個不同的進程并在那兒實現(xiàn)該請求。
支持取消操作。Command的Excute 操作可在實施操作前將狀態(tài)存儲起來,在取消操作時這個狀態(tài)用來消除該操作的影響。Command 接口必須添加一個Unexecute操作,該操作取消上一次Execute調(diào)用的效果。執(zhí)行的命令被存儲在一個歷史列表中。可通過向后和向前遍歷這一列表并分別調(diào)用Unexecute和Execute來實現(xiàn)重數(shù)不限的“取消”和“重做”。
支持修改日志,這樣當系統(tǒng)崩潰時,這些修改可以被重做一遍。在Command接口中添加裝載操作和存儲操作,可以用來保持變動的一個一致的修改日志。從崩潰中恢復的過程包括從磁盤中重新讀入記錄下來的命令并用Execute操作重新執(zhí)行它們。
用構建在原語操作上的高層操作構造一個系統(tǒng)。這樣一種結構在支持事務( transaction)的信息系統(tǒng)中很常見。一個事務封裝了對數(shù)據(jù)的一組變動。Command模式提供了對事務進行建模的方法。Command有一個公共的接口,使得你可以用同一種方式調(diào)用所有的事務。同時使用該模式也易于添加新事務以擴展系統(tǒng)。
17. Iterator(迭代器)

意圖:
提供一種方法順序訪問一個聚合對象中各個元素, 而又不需暴露該對象的內(nèi)部表示。
適用性:
訪問一個聚合對象的內(nèi)容而無需暴露它的內(nèi)部表示。支持對聚合對象的多種遍歷。為遍歷不同的聚合結構提供一個統(tǒng)一的接口(即, 支持多態(tài)迭代)。
18. Mediator(中介者)

意圖:
用一個中介對象來封裝一系列的對象交互。中介者使各對象不需要顯式地相互引用,從而使其耦合松散,而且可以獨立地改變它們之間的交互。
適用性:
19. Memento(備忘錄)

意圖:
在不破壞封裝性的前提下,捕獲一個對象的內(nèi)部狀態(tài),并在該對象之外保存這個狀態(tài)。這樣以后就可將該對象恢復到原先保存的狀態(tài)。
適用性:
必須保存一個對象在某一個時刻的(部分)狀態(tài), 這樣以后需要時它才能恢復到先前的狀態(tài)。
如果一個用接口來讓其它對象直接得到這些狀態(tài),將會暴露對象的實現(xiàn)細節(jié)并破壞對象的封裝性。
20. Observer(觀察者)

意圖:
定義對象間的一種一對多的依賴關系,當一個對象的狀態(tài)發(fā)生改變時, 所有依賴于它的對象都得到通知并被自動更新。
適用性:
當一個抽象模型有兩個方面, 其中一個方面依賴于另一方面。將這二者封裝在獨立的對象中以使它們可以各自獨立地改變和復用。
當對一個對象的改變需要同時改變其它對象, 而不知道具體有多少對象有待改變。
當一個對象必須通知其它對象,而它又不能假定其它對象是誰。換言之, 你不希望這些對象是緊密耦合的。
21. State(狀態(tài))

意圖:
允許一個對象在其內(nèi)部狀態(tài)改變時改變它的行為。對象看起來似乎修改了它的類。
適用性:
一個對象的行為取決于它的狀態(tài), 并且它必須在運行時刻根據(jù)狀態(tài)改變它的行為。
一個操作中含有龐大的多分支的條件語句,且這些分支依賴于該對象的狀態(tài)。這個狀態(tài)通常用一個或多個枚舉常量表示。通常, 有多個操作包含這一相同的條件結構。State模式將每一個條件分支放入一個獨立的類中。這使得你可以根據(jù)對象自身的情況將對象的狀態(tài)作為一個對象,這一對象可以不依賴于其他對象而獨立變化。
22. Strategy(策略)

意圖:
定義一系列的算法,把它們一個個封裝起來, 并且使它們可相互替換。本模式使得算法可獨立于使用它的客戶而變化。
適用性:
許多相關的類僅僅是行為有異。“策略”提供了一種用多個行為中的一個行為來配置一個類的方法。
需要使用一個算法的不同變體。例如,你可能會定義一些反映不同的空間/時間權衡的算法。當這些變體實現(xiàn)為一個算法的類層次時[H087] ,可以使用策略模式。
算法使用客戶不應該知道的數(shù)據(jù)。可使用策略模式以避免暴露復雜的、與算法相關的數(shù)據(jù)結構。
一個類定義了多種行為, 并且這些行為在這個類的操作中以多個條件語句的形式出現(xiàn)。將相關的條件分支移入它們各自的Strategy類中以代替這些條件語句。
23. Visitor(訪問者)

意圖:
定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。TemplateMethod 使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。
適用性:
一次性實現(xiàn)一個算法的不變的部分,并將可變的行為留給子類來實現(xiàn)。
各子類中公共的行為應被提取出來并集中到一個公共父類中以避免代碼重復。這是Opdyke和Johnson所描述過的“重分解以一般化”的一個很好的例子[OJ93]。首先識別現(xiàn)有代碼中的不同之處,并且將不同之處分離為新的操作。最后,用一個調(diào)用這些新的操作的模板方法來替換這些不同的代碼。
控制子類擴展。模板方法只在特定點調(diào)用“hook ”操作(參見效果一節(jié)),這樣就只允許在這些點進行擴展。
來源:blog.csdn.net/zsjlovesm521/article/details/94382666
干貨分享
最近將個人學習筆記整理成冊,使用PDF分享。關注我,回復如下代碼,即可獲得百度盤地址,無套路領取!
?001:《Java并發(fā)與高并發(fā)解決方案》學習筆記;?002:《深入JVM內(nèi)核——原理、診斷與優(yōu)化》學習筆記;?003:《Java面試寶典》?004:《Docker開源書》?005:《Kubernetes開源書》?006:《DDD速成(領域驅(qū)動設計速成)》?007:全部?008:加技術群討論
加個關注不迷路
喜歡就點個"在看"唄^_^
