設計模式精簡圖冊
首發於我的公眾號
設計模式分類
建立型模式:
主要用於建立物件,包括
- 工廠方法(Factory Method)
- 抽象工廠(Abstract Factory)
- 單例(Singleton)
- 生成器(Builder)
- 原型(Prototype)
結構型模式:
用於處理類或者物件的組合,包括
- 介面卡(Adapter)
- 裝飾者(Decorator)
- 代理(Proxy)
- 外觀(Facade)
- 橋接(Bridge Pattern)
- 組合(Composite)
- 輕量(Flyweigh)
行為型模式:
用於描述類與物件怎樣的互動和分配職責,包括
- 策略(Strategy)
- 觀察者(Observer)
- 命令(Command)
- 模板方法(Template Method)
- 迭代器(Iterator)
- 狀態(State)
- 責任鏈(Chain)
- 直譯器(Interpreter)
- 中介者(Mediator)
- 備忘錄(Memo)
- 訪問者(Visitor)
設計原則
單一職責原則(Single responsibility principle)
- 核心 不要存在多於一個導致類變更的原因。通俗的說,即一個類只負責一項職責。
- 問題產生 類T負責兩個不同的職責:職責P1,職責P2。當由於職責P1需求發生改變而需要修改類T時,有可能會導致原本執行正常的職責P2功能發生故障。
- 解決方案 遵循單一職責原則。分別建立兩個類T1、T2,使T1完成職責P1功能,T2完成職責P2功能。這樣,當修改類T1時,不會使職責P2發生故障風險;同理,當修改T2時,也不會使職責P1發生故障風險。
但是由於職責擴散會導致在實際中往往會有悖於單一職責
里氏代換原則(Liskov Substitution Principle LSP)
- 核心 所有引用基類的地方必須能透明地使用其子類的物件。
- 問題產生 有一功能P1,由類A完成。現需要將功能P1進行擴充套件,擴充套件後的功能為P,其中P由原有功能P1與新功能P2組成。新功能P由類A的子類B來完成,則子類B在完成新功能P2的同時,有可能會導致原有功能P1發生故障。
- 解決方案 當使用繼承時,遵循里氏替換原則。類B繼承類A時,除新增新的方法完成新增功能P2外,儘量不要重寫父類A的方法,也儘量不要過載父類A的方法。
繼承包含這樣一層含義:父類中凡是已經實現好的方法(相對於抽象方法而言),實際上是在設定一系列的規範和契約,雖然它不強制要求所有的子類必須遵從這些契約,但是如果子類對這些非抽象方法任意修改,就會對整個繼承體系造成破壞。而里氏替換原則就是表達了這一層含義。
繼承作為物件導向三大特性之一,在給程式設計帶來巨大便利的同時,也帶來了弊端。比如使用繼承會給程式帶來侵入性,程式的可移植性降低,增加了物件間的耦合性,如果一個類被其他的類所繼承,則當這個類需要修改時,必須考慮到所有的子類,並且父類修改後,所有涉及到子類的功能都有可能會產生故障。
介面隔離原則(Interface Segregation Principle)
- 核心 不應該依賴它不需要的介面;一個類對另一個類的依賴應該建立在最小的介面上。
- 問題產生 類A通過介面I依賴類B,類C通過介面I依賴類D,如果介面I對於類A和類B來說不是最小介面,則類B和類D必須去實現他們不需要的方法。
- 解決方案 將臃腫的介面I拆分為獨立的幾個介面,類A和類C分別與他們需要的介面建立依賴關係。也就是採用介面隔離原則。
介面隔離原則跟之前的單一職責原則很相似,其實不然。其一,單一職責原則原注重的是職責;而介面隔離原則注重對介面依賴的隔離。其二,單一職責原則主要是約束類,其次才是介面和方法,它針對的是程式中的實現和細節;而介面隔離原則主要約束介面介面,主要針對抽象,針對程式整體框架的構建。
採用介面隔離原則對介面進行約束時,要注意以下幾點:
- 介面儘量小,但是要有限度。對介面進行細化可以提高程式設計靈活性是不掙的事實,但是如果過小,則會造成介面數量過多,使設計複雜化。所以一定要適度。
- 為依賴介面的類定製服務,只暴露給呼叫的類它需要的方法,它不需要的方法則隱藏起來。只有專注地為一個模組提供定製服務,才能建立最小的依賴關係。
- 提高內聚,減少對外互動。使介面用最少的方法去完成最多的事情。
運用介面隔離原則,一定要適度,介面設計的過大或過小都不好。設計介面的時候,只有多花些時間去思考和籌劃,才能準確地實踐這一原則。
迪米特原則(Law of Demeter/Least Knowledge Principle )
- 核心 迪米特法則又叫最少知道原則,一個物件應該對其他物件保持最少的瞭解。
- 問題產生 類與類之間的關係越密切,耦合度越大,當一個類發生改變時,對另一個類的影響也越大。
- 解決方案 儘量降低類與類之間的耦合。
通俗的來講,就是一個類對自己依賴的類知道的越少越好。也就是說,對於被依賴的類來說,無論邏輯多麼複雜,都儘量地的將邏輯封裝在類的內部,對外除了提供的public方法,不對外洩漏任何資訊, 迪米特法則還有一個更簡單的定義:只與直接的朋友通訊。首先來解釋一下什麼是直接的朋友:每個物件都會與其他物件有耦合關係,只要兩個物件之間有耦合關係,我們就說這兩個物件之間是朋友關係。耦合的方式很多,依賴、關聯、組合、聚合等。其中,我們稱出現成員變數、方法引數、方法返回值中的類為直接的朋友,而出現在區域性變數中的類則不是直接的朋友。也就是說,陌生的類最好不要作為區域性變數的形式出現在類的內部。
依賴倒置原則(Dependence Inversion Principle)
- 核心 高層模組不應該依賴低層模組,二者都應該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象。
- 問題產生 類A直接依賴類B,假如要將類A改為依賴類C,則必須通過修改類A的程式碼來達成。這種場景下,類A一般是高層模組,負責複雜的業務邏輯;類B和類C是低層模組,負責基本的原子操作;假如修改類A,會給程式帶來不必要的風險。
- 解決方案 將類A修改為依賴介面I,類B和類C各自實現介面I,類A通過介面I間接與類B或者類C發生聯絡,則會大大降低修改類A的機率。
依賴倒置原則基於這樣一個事實:相對於細節的多變性,抽象的東西要穩定的多。以抽象為基礎搭建起來的架構比以細節為基礎搭建起來的架構要穩定的多。在java中,抽象指的是介面或者抽象類,細節就是具體的實現類,使用介面或者抽象類的目的是制定好規範和契約,而不去涉及任何具體的操作,把展現細節的任務交給他們的實現類去完成。
依賴倒置原則的核心思想是面向介面程式設計,
開閉原則(Open Close Principle)
- 核心 一個軟體實體如類、模組和函式應該對擴充套件開放,對修改關閉。
- 問題產生 在軟體的生命週期內,因為變化、升級和維護等原因需要對軟體原有程式碼進行修改時,可能會給舊程式碼中引入錯誤,也可能會使我們不得不對整個功能進行重構,並且需要原有程式碼經過重新測試。
- 解決方案 當軟體需要變化時,儘量通過擴充套件軟體實體的行為來實現變化,而不是通過修改已有的程式碼來實現變化。
開閉原則無非就是想表達這樣一層意思:用抽象構建框架,用實現擴充套件細節。因為抽象靈活性好,適應性廣,只要抽象的合理,可以基本保持軟體架構的穩定。而軟體中易變的細節,我們用從抽象派生的實現類來進行擴充套件,當軟體需要發生變化時,我們只需要根據需求重新派生一個實現類來擴充套件就可以了。當然前提是我們的抽象要合理,要對需求的變更有前瞻性和預見性才行。
幾個原則的關聯性
用抽象構建框架,用實現擴充套件細節的注意事項而已:
- 單一職責原則告訴我們實現類要職責單一;
- 里氏替換原則告訴我們不要破壞繼承體系;
- 依賴倒置原則告訴我們要面向介面程式設計;
- 介面隔離原則告訴我們在設計介面的時候要精簡單一;
- 迪米特法則告訴我們要降低耦合。 而開閉原則是總綱,他告訴我們要對擴充套件開放,對修改關閉。
建立型設計模式(建立物件)
工廠方法(Factory Method Pattern)
名稱
|
Factory Method
|
結構
|
|
動機
|
定義一個用於建立物件的介面,讓子類決定例項化哪一個類。Factory Method 使一個類的例項化延遲到其子類。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
抽象工廠(Abstract Factory Pattern)
名稱
|
Abstract Factory
|
結構
|
|
動機
|
提供一個建立一系列相關或相互依賴物件的介面,而無需指定它們具體的類。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
生成器/建造者模式(Builder Pattern)
名稱
|
Builder
|
結構
|
|
動機
|
將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
原型模式(Prototype Pattern)
名稱
|
Prototype
|
結構
|
|
動機
|
用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
單例模式(Singleton Pattern)
名稱
|
Singleton
|
結構
|
|
動機
|
保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
public class BetterSingleton {
private BetterSingleton2() {
}
public static BetterSingleton getInstance() {
return Singleton.BETTER_SINGLETON;
}
public static class Singleton{
private static final BetterSingleton BETTER_SINGLETON = new BetterSingleton();
}
}
複製程式碼
建立型設計模式小結
模式 | 場景發散 | 一句話概括 |
---|---|---|
工廠方法(Factory Method) | new太多如何管理 | 生產系列產品。 |
抽象工廠(Abstract Factory) | new太多如何管理 | 一次生產多個不同產品。 |
生成器(Builder) | 車手選車 | 生產有很多元件的產品。 |
原型(Prototype) | 複製不能很難 | 克隆物件。 |
單件(Singleton) | 如何管理全域性資訊 | 全域性只有一個。 |
結構型設計模式(處理類或者物件的組合)
橋接模式(Bridge Pattern)
名稱
|
Bridge
|
結構
|
|
動機
|
將抽象部分與它的實現部分分離,使它們都可以獨立地變化。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
輕量模式/享元模式(FlyWeightPattern)
名稱
|
Flyweight
|
結構
|
|
動機
|
運用共享技術有效地支援大量細粒度的物件。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
外觀模式(Facade Pattern)
名稱
|
Facade
|
結構
|
|
動機
|
為子系統中的一組介面提供一個一致的介面,F a c a d e 模式定義了一個高層介面,這個介面使得這一子系統更加容易使用。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
裝飾者模式(Decorator Pattern)
名稱
|
Decorator
|
結構
|
|
動機
|
動態地給一個物件新增一些額外的職責。就增加功能來說,D e c o r a t o r 模式相比生成子類更為靈活。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
組合模式(Composite Pattern)
名稱
|
Composite
|
結構
|
|
動機
|
將物件組合成樹形結構以表示?部分-整體?的層次結構。C o m p o s i t e 使得使用者對單個物件和組合物件的使用具有一致性。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
代理模式(Proxy Pattern)
名稱
|
Proxy
|
結構
|
|
動機
|
為其他物件提供一種代理以控制對這個物件的訪問。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
介面卡模式(Adapter Pattern)
名稱
|
Adapter
|
結構
|
|
結構型設計模式小結
模式 | 場景發散 | 一句話說明 |
---|---|---|
橋(Bridge) | 麻煩的日誌記錄 | 將“抽象”和“實現”自由搭配。 |
輕量(Flyweight) | 森林裡的樹太多了 | 輕鬆地處理“大量”物件。 |
外觀(Façade) | 超級手機 | 同時提供簡單介面和複雜介面。 |
裝飾者(Decorator) | 星巴克的飲料計較系統 | 不改變介面但要增強功能。 |
組合(Composite) | 超酷的繪圖軟體 | 不管你是老子還是兒子,都一樣處理。 |
代理(Proxy) | 找中介租房 | 代理要控制你的訪問,同時讓你的訪問更舒服 。 |
介面卡(Adapter) | 老掉牙系統的重生 | 不改變功能但要改變介面 |
行為型設計模式(類與物件怎樣的互動和分配職責)
觀察者模式(Observer Pattern)
名稱
|
Observer
|
結構
|
|
動機
|
定義物件間的一種一對多的依賴關係,當一個物件的狀態發生改變時, 所有依賴於它的物件都得到通知並被自動更新。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
策略模式(Strategy Pattern)
名稱
|
Strategy
|
結構
|
|
動機
|
定義一系列的演算法,把它們一個個封裝起來, 並且使它們可相互替換。本模式使得演算法可獨立於使用它的客戶而變化。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
迭代器模式(Iterator Pattern)
名稱
|
Iterator
|
結構
|
|
動機
|
提供一種方法順序訪問一個聚合物件中各個元素, 而又不需暴露該物件的內部表示。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
命令模式(Command Pattern)
名稱
|
Command
|
結構
|
|
動機
|
將一個請求封裝為一個物件,從而使你可用不同的請求對客戶進行引數化;對請求排隊或記錄請求日誌,以及支援可撤消的操作。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
訪問者模式(Visitor Pattern)
名稱
|
Visitor
|
結構
|
|
動機
|
表示一個作用於某物件結構中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用於這些元素的新操作。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
直譯器模式(Interpreter Pattern)
名稱
|
Interpreter
|
結構
|
|
動機
|
給定一個語言,定義它的文法的一種表示,並定義一個直譯器,這個直譯器使用該表示來解釋語言中的句子。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
中介者模式(Mediator Pattern)
名稱
|
Mediator
|
結構
|
|
動機
|
用一箇中介物件來封裝一系列的物件互動。中介者使各物件不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的互動。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
備忘錄模式(Memento Pattern)
名稱
|
Mediator
|
結構
|
|
動機
|
用一箇中介物件來封裝一系列的物件互動。中介者使各物件不需要顯式地相互引用,從而使其耦合鬆散,而且可以獨立地改變它們之間的互動。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
模板方法模式(TemplateMethod Pattern)
名稱
|
Template Method
|
結構
|
|
動機
|
定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中。Te m p l a t e M e t h o d 使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
狀態模式(State Pattern)
名稱
|
State
|
結構
|
|
動機
|
允許一個物件在其內部狀態改變時改變它的行為。物件看起來似乎修改了它的類。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
責任鏈模式(Chain of Responsibility Pattern)
名稱
|
Chain of Responsibility
|
結構
|
|
動機
|
使多個物件都有機會處理請求,從而避免請求的傳送者和接收者之間的耦合關係。將這些物件連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個物件處理它為止。
|
適用性
|
|
優點
|
|
缺點
|
|
小結
|
|
行為型設計模式小結
模式 | 場景發散 | 一句話說明 |
---|---|---|
觀察者(Observer) | 同步更新的問題 | 一呼百應。 |
策略(Strategy) | 設計你的戰士 | 分離演算法。 |
迭代器(Iterator) | 集合訪問的煩惱 | 以一致的方式訪問集合, “鬆綁 ”遍歷演算法程式碼。 |
命令(Command) | 神奇的Do與Undo | 分離功能呼叫者與功能實現者。 |
訪問者(Visitor) | 增加新方法的煩惱 | 不改變物件結構增加新方法。 |
直譯器(Interpreter) | 超級表達解釋引擎 | 語法解釋。 |
中介者(Mediator) | 麻煩的多角關係 | 處理多對多關係。 |
備忘錄(Memento) | 假如一切可以重來 | 儲存物件的多個狀態並可任意恢復。 |
模板方法(Template Method) | 萬能的排序器 | 定義好框架演算法 ,某些步驟可自己定義 。 |
狀態(State) | 是攻擊還是逃走 | 方便地處理不同狀態不同行為,以及狀態之間的轉換。 |
責任鏈(Chain) | 郵件自動處理系統 | 用不同的規則去處理請求。 |
模式比較
參考
-
《硬啃設計模式》
-
《HeadFirst 設計模式》
-
GoF 《可複用物件導向軟體的基礎》
歡迎關注我的公眾號,一起學習,共同提高~ 複製程式碼