設計模式小結

設計模式

7 大原則

單一職責: 每個類只負責一個職責(或每個方法)
接口隔離: 一個類對另一個類的依賴應建立在最小的接口上
依賴倒轉(zhuǎn): 高層模塊不應依賴低層模塊, 二者都應該依賴接口而非細節(jié). 細節(jié)依賴抽象, 面向接口編程
里式替換: 子類應該做到可以替換父類, 及子類應盡量不重寫父類方法.
開閉原則: 對提供者而已可以修改, 對使用者而言不需要修改(即代碼兼容性), 盡量使用擴展增加功能, 而非修改原有類
迪米特法則: 一個對象應該對其他對象保持最小了解(最少知道原則)
合成復用原則: 一個類使用另一個類的代碼(方法), 盡量使用合成, 而不是繼承

創(chuàng)建型

單例模式

原理: 確保一個類只有一個實例,并提供該實例的全局訪問點。

餓漢式:
    靜態(tài)常量
    靜態(tài)代碼塊
懶漢式:
    直接判斷(線程不安全)
    方法加 synchronized(線程安全, 效率低)
    判斷后再同步(錯誤寫法)
    雙重判斷(if-同步-if) (推薦寫法)
    匿名靜態(tài)內(nèi)部類 (簡單, 推薦)
    枚舉(簡單, 但對象方法寫在枚舉中, 略有不適)
    
示例:
    java.lang.Runtime#getRuntime()
    java.awt.Desktop#getDesktop()

原型模式

原理: 使用原型實例指定要創(chuàng)建對象的類型,通過復制這個原型來創(chuàng)建新對象.
示例: Java 的 Object 對象的 clone 方法, java.util.Arrays.ArrayList#toArray()

淺拷貝: 僅對基礎類型及字符串類型的字段拷貝值
深拷貝: 同時對引用類型(如數(shù)組,對象) 也進行拷貝

深拷貝實現(xiàn):
1.重寫 clone, 一一處理每個引用對象(調(diào)用對象的 clone), 麻煩, 且若對象之間關系復雜, 其中一個未實現(xiàn)深拷貝則導致 bug.
2.利用序列化和反序列化, 如 Json, 或 Java 自帶的序列化方式(二進制)

創(chuàng)建者模式(生成器模式)

原理:
    封裝一個對象的構造過程,并允許按步驟構造.
    若對象的生成過于復雜(字段極多且賦值還有依賴關系, 需要順序調(diào)用), 則可將賦值過程封裝成一個build(), 并放到一個 Builder 類中. 此類對外提供各個字段的賦值方法并先保存起來, 直到調(diào)用 build(), 此方法返回對象實例. 
    使用此模式, 調(diào)用者無需關注構建過程, 只需設置自己想要的值, 然后調(diào)用 build() 即可得到對象實例. 且若增加或修改字段, 構造過程變化, 調(diào)用者無感知, 無需修改代碼. 符合開閉原則.

示例: StringBuilder, 一些框架的 ConfigurationBuilder(如 xmpp), 用于構建配置.

簡單工廠模式

原理:
    在創(chuàng)建一個對象時不向客戶暴露內(nèi)部細節(jié),并提供一個創(chuàng)建對象的通用接口。
    此模式可避免多個調(diào)用者創(chuàng)建對象時判斷創(chuàng)建哪個子類的重復代碼, 且若多一個子類, 調(diào)用者無需修改代碼.
    
示例: Spring ApplicationContext 的 getBean 方法.

工廠方法模式

原理:
    定義了一個創(chuàng)建對象的接口,但由子類決定要實例化哪個類。工廠方法把實例化操作推遲到子類。
    此模式解決了簡單工廠每增加一個子類需要修改工廠類的問題.
    此模式存在問題, 若新增一個子類, 需同時新增一個子類工廠, 系統(tǒng)復雜性更高.

示例: Calendar, NumberFormat

抽象工廠模式

原理:
    提供一個接口,用于創(chuàng)建 相關的對象家族.
  同上, 由子類工廠決定創(chuàng)建哪些對象.
    此模式是工廠方法的升級版, 不同之處在于它同時創(chuàng)建多個種類的對象(工廠類具有多個方法).
    此模式將一個對象家族的新建集合到一個工廠類創(chuàng)建管理, 這些對象家族相互之間一般有關聯(lián), 在創(chuàng)建時就可以處理這些關聯(lián). 且對于 2 個子類工廠, 一般可以無縫切換, 使得修改代碼極為方便(即換一個子類工廠).
    此模式在新增一個對象家族的成員時非常麻煩(即所有工廠類需要新增一個方法), 但再新增一類對象家族時比較簡單(即新增一個子類工廠).

結構型

適配器模式

原理:
    把一個接口轉(zhuǎn)換成另一個用戶需要的接口.
    定義一個類, 實現(xiàn)用戶需要的接口, 并聚合一個需要轉(zhuǎn)換的接口對象, 在重寫的方法(用戶需要的方法)中調(diào)用聚合的對象的方法, 若需要返回值, 且返回值類型不一致, 則還需要在方法中處理一番, 然后返回. 這個過程叫做適配.這個類叫做適配器類.
    使用此模式可對一些老舊接口適配兼容.
    
示例: java.util.Arrays#asList() 將數(shù)組適配成 List, Spring MVC的 HandlerAdapter

裝飾者模式

原理:
    將一個或多個功能(方法)動態(tài)的新增到一個類中.
    把需要新增功能類稱為 A,定義一個類B,實現(xiàn)A的上層接口, 并聚合一個A 的實例對象, B類實現(xiàn)的接口中, 對其他不關心的方法直接調(diào)用聚合的對象的方法. 對于關心的方法則可以在調(diào)用前后進行加料處理(如一個方法返回一個數(shù), 可以在原來的返回值上乘以 2), 同時, B類也可以新增一些其他方法, 這些方法就是多出的功能. B類就是裝飾者類, A就是被裝飾類.
    此模式的優(yōu)點是, 裝飾類也可以當做被裝飾類, 然后再來一層裝飾, 可以無限的裝飾.
    
示例: java IO 流

代理模式

原理:
    控制其他對象的訪問(方法級), 將一些前置或后置的處理, 通過代理對象注入到目標對象的方法前后. 面向切面編程.

類型:
    靜態(tài)代理: 定義一個代理類實現(xiàn)目標對象的上層接口, 并聚合一個目標對象, 重寫方法時將前置后置處理加上.
    動態(tài)代理: 
        JDK 動態(tài)代理: 需要目標對象有上層接口(自然接口內(nèi)的方法才可以代理) 
            使用java.lang.reflect.Proxy#getProxyClass
        CGLIB 動態(tài)代理: 是個類就行. 實現(xiàn)原理是 ASM 框架動態(tài)生成目標對象類的子類字節(jié)碼, 然后通過反射生成代理對象.

示例: Spring AOP

橋接模式

原理:
    將抽象與實現(xiàn)分離開來,使它們可以獨立變化。
    橋接的含義是, 一個橋, 放在哪里都有橋的 2 邊, 橋的 2 邊可以變化, 但橋始終不變. 此處, 橋代表一個操作(如手機上運行軟件), 2 邊代表 一個操作的 2 個維度(如手機和軟件). 同時, 橋接后的操作也可以視為一個維度, 與另一個維度橋接(如手機上運行軟件和人這 2 個維度, 可以進行橋接, 組成 3 維度嵌套橋接).
    
示例: JDBC 獲取連接, 獲取連接是一個維度, 數(shù)據(jù)庫是一個維度, 數(shù)據(jù)庫有多個, 所以這是一個數(shù)據(jù)庫維度變化, 另一維度不變的橋接模式.

享元模式

原理:
    利用共享的方式來支持大量細粒度的對象,這些對象一部分內(nèi)部狀態(tài)是相同的。
    如常見的 線程池, 常量池等, 使得對象的獲取速度加快.
    
示例: java.lang.Integer#valueOf() java.lang.Boolean#valueOf()

組合模式

原理:
    將對象組合成樹形結構來表示“整體/部分”層次關系,允許用戶以相同的方式處理單獨對象和組合對象。
    一般需要部分和整體具有一定的相似度, 才能對其進行抽象.
    對部分/整體進行抽象, 得出一個公共抽象類或接口, 再實現(xiàn)類中根據(jù)具體角色做不同處理. 

示例:
    javax.swing.JComponent#add(Component) 
  java.util.Map#putAll(Map)
  java.util.List#addAll(Collection)
  java.util.Set#addAll(Collection)

外觀模式

原理:
    提供了一個統(tǒng)一的接口,用來訪問子系統(tǒng)中的一群接口,從而讓子系統(tǒng)更容易使用.

行為型

職責鏈(責任鏈)模式

原理:
    使多個對象都有機會處理請求,將這些對象連成一條鏈,并沿著這條鏈發(fā)送該請求,直到有一個對象處理它為止, 從而避免請求的發(fā)送者和接收者之間的耦合關系。
    
示例:
    javax.servlet.Filter#doFilter()
    netty 的 Handler Chain

觀察者模式

原理:
    定義對象之間的一對多依賴,當一個對象狀態(tài)改變時,它的所有依賴都會收到通知并且自動更新狀態(tài)。
    主題(Subject)是被觀察的對象,而其所有依賴者(Observer)稱為觀察者。

示例: 
    swing 的事件監(jiān)聽(按鈕事件, 鼠標事件)
    JS 的 事件監(jiān)聽

狀態(tài)模式

原理:
    允許對象在內(nèi)部狀態(tài)改變時改變它的行為,對象看起來好像修改了它所屬的類。
    狀態(tài)模式主要是用來解決狀態(tài)轉(zhuǎn)移的問題,當狀態(tài)發(fā)生轉(zhuǎn)移了,那么 Context 對象就會改變它的行為.

策略模式

原理:
    定義一系列算法,封裝每個算法,并使它們可以互換。
    策略模式可以讓算法獨立于使用它的客戶端。
    策略模式主要是用來封裝一組可以互相替代的算法族,并且可以根據(jù)需要動態(tài)地去替換 Context 使用的算法.

示例: java.util.Comparator#compare() javax.servlet.http.HttpServlet

模板方法模式

原理:
    定義算法框架,并將一些步驟的實現(xiàn)延遲到子類。
    通過模板方法,子類可以重新定義算法的某些步驟,而不用改變算法的結構。

示例: java.util.Collections#sort()

命令模式

原理:
    將一個對象(命令接收者)的每個操作拆分到每一個命令類中, 再使用一個命令管理類來管理這些命令. 使得命令可以放入隊列中有序執(zhí)行, 且可以統(tǒng)一記錄命令的操作日志, 還可以支持撤銷操作(每個命令都實現(xiàn)對應的撤銷即可).
    此模式的好處是, 若將命令抽象為幾個標準的命令(如開,關), 然后管理多個命令接收者(如燈,電視機,空調(diào))的操作, 可使新增命令接收者變得簡單, 即擴展性好.
    
    又稱萬能遙控器.

中介模式

原理:
    集中相關對象之間復雜的溝通和控制方式。降低子系統(tǒng)之間的耦合.
    類似一個消息收發(fā)中心, 負責字系統(tǒng)的消息中轉(zhuǎn), 使得子系統(tǒng)之間可以進行一定的交互.
    
示例: 線程池管理者線程和要執(zhí)行的任務.

備忘錄模式

原理:
    在不違反封裝的情況下獲得對象的內(nèi)部狀態(tài),從而在需要時可以將對象恢復到最初狀態(tài)。
    如對游戲的當前狀態(tài)進行一個保存, 然后在后續(xù)游戲中死亡后可以讀取這個狀態(tài)重新開始.

訪問者模式

原理: 
    為一個對象結構(比如組合結構)增加新能力。
    使用訪問者模式可實現(xiàn)重載的動態(tài)綁定(即偽雙分派), 效果與重載方法內(nèi)使用 instanceof 是一樣的, 但使用訪問者模式, 可擴展性更好.

迭代器模式

原理:
    提供一種順序訪問聚合對象元素的方法,并且不暴露聚合對象的內(nèi)部表示。

示例: java.util.Iterator

解釋器模式

原理:
    為語言創(chuàng)建解釋器,通常由語言的語法和語法分析來定義。

示例: EL 表達式, Freemaker模板

空對象模式

原理:
    使用什么都不做的空對象來代替 NULL, 避免空對象判斷, 避免空指針異常.
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容