設(shè)計(jì)模式
7 大原則
單一職責(zé): 每個(gè)類(lèi)只負(fù)責(zé)一個(gè)職責(zé)(或每個(gè)方法)
接口隔離: 一個(gè)類(lèi)對(duì)另一個(gè)類(lèi)的依賴(lài)應(yīng)建立在最小的接口上
依賴(lài)倒轉(zhuǎn): 高層模塊不應(yīng)依賴(lài)低層模塊, 二者都應(yīng)該依賴(lài)接口而非細(xì)節(jié). 細(xì)節(jié)依賴(lài)抽象, 面向接口編程
里式替換: 子類(lèi)應(yīng)該做到可以替換父類(lèi), 及子類(lèi)應(yīng)盡量不重寫(xiě)父類(lèi)方法.
開(kāi)閉原則: 對(duì)提供者而已可以修改, 對(duì)使用者而言不需要修改(即代碼兼容性), 盡量使用擴(kuò)展增加功能, 而非修改原有類(lèi)
迪米特法則: 一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象保持最小了解(最少知道原則)
合成復(fù)用原則: 一個(gè)類(lèi)使用另一個(gè)類(lèi)的代碼(方法), 盡量使用合成, 而不是繼承
創(chuàng)建型
單例模式
原理: 確保一個(gè)類(lèi)只有一個(gè)實(shí)例惶看,并提供該實(shí)例的全局訪(fǎng)問(wèn)點(diǎn)盹牧。
餓漢式:
靜態(tài)常量
靜態(tài)代碼塊
懶漢式:
直接判斷(線(xiàn)程不安全)
方法加 synchronized(線(xiàn)程安全, 效率低)
判斷后再同步(錯(cuò)誤寫(xiě)法)
雙重判斷(if-同步-if) (推薦寫(xiě)法)
匿名靜態(tài)內(nèi)部類(lèi) (簡(jiǎn)單, 推薦)
枚舉(簡(jiǎn)單, 但對(duì)象方法寫(xiě)在枚舉中, 略有不適)
示例:
java.lang.Runtime#getRuntime()
java.awt.Desktop#getDesktop()
原型模式
原理: 使用原型實(shí)例指定要?jiǎng)?chuàng)建對(duì)象的類(lèi)型,通過(guò)復(fù)制這個(gè)原型來(lái)創(chuàng)建新對(duì)象.
示例: Java 的 Object 對(duì)象的 clone 方法, java.util.Arrays.ArrayList#toArray()
淺拷貝: 僅對(duì)基礎(chǔ)類(lèi)型及字符串類(lèi)型的字段拷貝值
深拷貝: 同時(shí)對(duì)引用類(lèi)型(如數(shù)組,對(duì)象) 也進(jìn)行拷貝
深拷貝實(shí)現(xiàn):
1.重寫(xiě) clone, 一一處理每個(gè)引用對(duì)象(調(diào)用對(duì)象的 clone), 麻煩, 且若對(duì)象之間關(guān)系復(fù)雜, 其中一個(gè)未實(shí)現(xiàn)深拷貝則導(dǎo)致 bug.
2.利用序列化和反序列化, 如 Json, 或 Java 自帶的序列化方式(二進(jìn)制)
創(chuàng)建者模式(生成器模式)
原理:
封裝一個(gè)對(duì)象的構(gòu)造過(guò)程,并允許按步驟構(gòu)造.
若對(duì)象的生成過(guò)于復(fù)雜(字段極多且賦值還有依賴(lài)關(guān)系, 需要順序調(diào)用), 則可將賦值過(guò)程封裝成一個(gè)build(), 并放到一個(gè) Builder 類(lèi)中. 此類(lèi)對(duì)外提供各個(gè)字段的賦值方法并先保存起來(lái), 直到調(diào)用 build(), 此方法返回對(duì)象實(shí)例.
使用此模式, 調(diào)用者無(wú)需關(guān)注構(gòu)建過(guò)程, 只需設(shè)置自己想要的值, 然后調(diào)用 build() 即可得到對(duì)象實(shí)例. 且若增加或修改字段, 構(gòu)造過(guò)程變化, 調(diào)用者無(wú)感知, 無(wú)需修改代碼. 符合開(kāi)閉原則.
示例: StringBuilder, 一些框架的 ConfigurationBuilder(如 xmpp), 用于構(gòu)建配置.
簡(jiǎn)單工廠(chǎng)模式
原理:
在創(chuàng)建一個(gè)對(duì)象時(shí)不向客戶(hù)暴露內(nèi)部細(xì)節(jié)挪凑,并提供一個(gè)創(chuàng)建對(duì)象的通用接口批销。
此模式可避免多個(gè)調(diào)用者創(chuàng)建對(duì)象時(shí)判斷創(chuàng)建哪個(gè)子類(lèi)的重復(fù)代碼, 且若多一個(gè)子類(lèi), 調(diào)用者無(wú)需修改代碼.
示例: Spring ApplicationContext 的 getBean 方法.
工廠(chǎng)方法模式
原理:
定義了一個(gè)創(chuàng)建對(duì)象的接口戈次,但由子類(lèi)決定要實(shí)例化哪個(gè)類(lèi)耸彪。工廠(chǎng)方法把實(shí)例化操作推遲到子類(lèi)。
此模式解決了簡(jiǎn)單工廠(chǎng)每增加一個(gè)子類(lèi)需要修改工廠(chǎng)類(lèi)的問(wèn)題.
此模式存在問(wèn)題, 若新增一個(gè)子類(lèi), 需同時(shí)新增一個(gè)子類(lèi)工廠(chǎng), 系統(tǒng)復(fù)雜性更高.
示例: Calendar, NumberFormat
抽象工廠(chǎng)模式
原理:
提供一個(gè)接口栈雳,用于創(chuàng)建 相關(guān)的對(duì)象家族.
同上, 由子類(lèi)工廠(chǎng)決定創(chuàng)建哪些對(duì)象.
此模式是工廠(chǎng)方法的升級(jí)版, 不同之處在于它同時(shí)創(chuàng)建多個(gè)種類(lèi)的對(duì)象(工廠(chǎng)類(lèi)具有多個(gè)方法).
此模式將一個(gè)對(duì)象家族的新建集合到一個(gè)工廠(chǎng)類(lèi)創(chuàng)建管理, 這些對(duì)象家族相互之間一般有關(guān)聯(lián), 在創(chuàng)建時(shí)就可以處理這些關(guān)聯(lián). 且對(duì)于 2 個(gè)子類(lèi)工廠(chǎng), 一般可以無(wú)縫切換, 使得修改代碼極為方便(即換一個(gè)子類(lèi)工廠(chǎng)).
此模式在新增一個(gè)對(duì)象家族的成員時(shí)非常麻煩(即所有工廠(chǎng)類(lèi)需要新增一個(gè)方法), 但再新增一類(lèi)對(duì)象家族時(shí)比較簡(jiǎn)單(即新增一個(gè)子類(lèi)工廠(chǎng)).
結(jié)構(gòu)型
適配器模式
原理:
把一個(gè)接口轉(zhuǎn)換成另一個(gè)用戶(hù)需要的接口.
定義一個(gè)類(lèi), 實(shí)現(xiàn)用戶(hù)需要的接口, 并聚合一個(gè)需要轉(zhuǎn)換的接口對(duì)象, 在重寫(xiě)的方法(用戶(hù)需要的方法)中調(diào)用聚合的對(duì)象的方法, 若需要返回值, 且返回值類(lèi)型不一致, 則還需要在方法中處理一番, 然后返回. 這個(gè)過(guò)程叫做適配.這個(gè)類(lèi)叫做適配器類(lèi).
使用此模式可對(duì)一些老舊接口適配兼容.
示例: java.util.Arrays#asList() 將數(shù)組適配成 List, Spring MVC的 HandlerAdapter
裝飾者模式
原理:
將一個(gè)或多個(gè)功能(方法)動(dòng)態(tài)的新增到一個(gè)類(lèi)中.
把需要新增功能類(lèi)稱(chēng)為 A,定義一個(gè)類(lèi)B,實(shí)現(xiàn)A的上層接口, 并聚合一個(gè)A 的實(shí)例對(duì)象, B類(lèi)實(shí)現(xiàn)的接口中, 對(duì)其他不關(guān)心的方法直接調(diào)用聚合的對(duì)象的方法. 對(duì)于關(guān)心的方法則可以在調(diào)用前后進(jìn)行加料處理(如一個(gè)方法返回一個(gè)數(shù), 可以在原來(lái)的返回值上乘以 2), 同時(shí), B類(lèi)也可以新增一些其他方法, 這些方法就是多出的功能. B類(lèi)就是裝飾者類(lèi), A就是被裝飾類(lèi).
此模式的優(yōu)點(diǎn)是, 裝飾類(lèi)也可以當(dāng)做被裝飾類(lèi), 然后再來(lái)一層裝飾, 可以無(wú)限的裝飾.
示例: java IO 流
代理模式
原理:
控制其他對(duì)象的訪(fǎng)問(wèn)(方法級(jí)), 將一些前置或后置的處理, 通過(guò)代理對(duì)象注入到目標(biāo)對(duì)象的方法前后. 面向切面編程.
類(lèi)型:
靜態(tài)代理: 定義一個(gè)代理類(lèi)實(shí)現(xiàn)目標(biāo)對(duì)象的上層接口, 并聚合一個(gè)目標(biāo)對(duì)象, 重寫(xiě)方法時(shí)將前置后置處理加上.
動(dòng)態(tài)代理:
JDK 動(dòng)態(tài)代理: 需要目標(biāo)對(duì)象有上層接口(自然接口內(nèi)的方法才可以代理)
使用java.lang.reflect.Proxy#getProxyClass
CGLIB 動(dòng)態(tài)代理: 是個(gè)類(lèi)就行. 實(shí)現(xiàn)原理是 ASM 框架動(dòng)態(tài)生成目標(biāo)對(duì)象類(lèi)的子類(lèi)字節(jié)碼, 然后通過(guò)反射生成代理對(duì)象.
示例: Spring AOP
橋接模式
原理:
將抽象與實(shí)現(xiàn)分離開(kāi)來(lái)护奈,使它們可以獨(dú)立變化。
橋接的含義是, 一個(gè)橋, 放在哪里都有橋的 2 邊, 橋的 2 邊可以變化, 但橋始終不變. 此處, 橋代表一個(gè)操作(如手機(jī)上運(yùn)行軟件), 2 邊代表 一個(gè)操作的 2 個(gè)維度(如手機(jī)和軟件). 同時(shí), 橋接后的操作也可以視為一個(gè)維度, 與另一個(gè)維度橋接(如手機(jī)上運(yùn)行軟件和人這 2 個(gè)維度, 可以進(jìn)行橋接, 組成 3 維度嵌套橋接).
示例: JDBC 獲取連接, 獲取連接是一個(gè)維度, 數(shù)據(jù)庫(kù)是一個(gè)維度, 數(shù)據(jù)庫(kù)有多個(gè), 所以這是一個(gè)數(shù)據(jù)庫(kù)維度變化, 另一維度不變的橋接模式.
享元模式
原理:
利用共享的方式來(lái)支持大量細(xì)粒度的對(duì)象哥纫,這些對(duì)象一部分內(nèi)部狀態(tài)是相同的霉旗。
如常見(jiàn)的 線(xiàn)程池, 常量池等, 使得對(duì)象的獲取速度加快.
示例: java.lang.Integer#valueOf() java.lang.Boolean#valueOf()
組合模式
原理:
將對(duì)象組合成樹(shù)形結(jié)構(gòu)來(lái)表示“整體/部分”層次關(guān)系,允許用戶(hù)以相同的方式處理單獨(dú)對(duì)象和組合對(duì)象磺箕。
一般需要部分和整體具有一定的相似度, 才能對(duì)其進(jìn)行抽象.
對(duì)部分/整體進(jìn)行抽象, 得出一個(gè)公共抽象類(lèi)或接口, 再實(shí)現(xiàn)類(lèi)中根據(jù)具體角色做不同處理.
示例:
javax.swing.JComponent#add(Component)
java.util.Map#putAll(Map)
java.util.List#addAll(Collection)
java.util.Set#addAll(Collection)
外觀(guān)模式
原理:
提供了一個(gè)統(tǒng)一的接口奖慌,用來(lái)訪(fǎng)問(wèn)子系統(tǒng)中的一群接口,從而讓子系統(tǒng)更容易使用.
行為型
職責(zé)鏈(責(zé)任鏈)模式
原理:
使多個(gè)對(duì)象都有機(jī)會(huì)處理請(qǐng)求松靡,將這些對(duì)象連成一條鏈,并沿著這條鏈發(fā)送該請(qǐng)求建椰,直到有一個(gè)對(duì)象處理它為止, 從而避免請(qǐng)求的發(fā)送者和接收者之間的耦合關(guān)系雕欺。
示例:
javax.servlet.Filter#doFilter()
netty 的 Handler Chain
觀(guān)察者模式
原理:
定義對(duì)象之間的一對(duì)多依賴(lài),當(dāng)一個(gè)對(duì)象狀態(tài)改變時(shí)棉姐,它的所有依賴(lài)都會(huì)收到通知并且自動(dòng)更新?tīng)顟B(tài)屠列。
主題(Subject)是被觀(guān)察的對(duì)象,而其所有依賴(lài)者(Observer)稱(chēng)為觀(guān)察者伞矩。
示例:
swing 的事件監(jiān)聽(tīng)(按鈕事件, 鼠標(biāo)事件)
JS 的 事件監(jiān)聽(tīng)
狀態(tài)模式
原理:
允許對(duì)象在內(nèi)部狀態(tài)改變時(shí)改變它的行為笛洛,對(duì)象看起來(lái)好像修改了它所屬的類(lèi)。
狀態(tài)模式主要是用來(lái)解決狀態(tài)轉(zhuǎn)移的問(wèn)題乃坤,當(dāng)狀態(tài)發(fā)生轉(zhuǎn)移了苛让,那么 Context 對(duì)象就會(huì)改變它的行為.
策略模式
原理:
定義一系列算法沟蔑,封裝每個(gè)算法,并使它們可以互換狱杰。
策略模式可以讓算法獨(dú)立于使用它的客戶(hù)端瘦材。
策略模式主要是用來(lái)封裝一組可以互相替代的算法族,并且可以根據(jù)需要?jiǎng)討B(tài)地去替換 Context 使用的算法.
示例: java.util.Comparator#compare() javax.servlet.http.HttpServlet
模板方法模式
原理:
定義算法框架仿畸,并將一些步驟的實(shí)現(xiàn)延遲到子類(lèi)食棕。
通過(guò)模板方法,子類(lèi)可以重新定義算法的某些步驟错沽,而不用改變算法的結(jié)構(gòu)簿晓。
示例: java.util.Collections#sort()
命令模式
原理:
將一個(gè)對(duì)象(命令接收者)的每個(gè)操作拆分到每一個(gè)命令類(lèi)中, 再使用一個(gè)命令管理類(lèi)來(lái)管理這些命令. 使得命令可以放入隊(duì)列中有序執(zhí)行, 且可以統(tǒng)一記錄命令的操作日志, 還可以支持撤銷(xiāo)操作(每個(gè)命令都實(shí)現(xiàn)對(duì)應(yīng)的撤銷(xiāo)即可).
此模式的好處是, 若將命令抽象為幾個(gè)標(biāo)準(zhǔn)的命令(如開(kāi),關(guān)), 然后管理多個(gè)命令接收者(如燈,電視機(jī),空調(diào))的操作, 可使新增命令接收者變得簡(jiǎn)單, 即擴(kuò)展性好.
又稱(chēng)萬(wàn)能遙控器.
中介模式
原理:
集中相關(guān)對(duì)象之間復(fù)雜的溝通和控制方式。降低子系統(tǒng)之間的耦合.
類(lèi)似一個(gè)消息收發(fā)中心, 負(fù)責(zé)字系統(tǒng)的消息中轉(zhuǎn), 使得子系統(tǒng)之間可以進(jìn)行一定的交互.
示例: 線(xiàn)程池管理者線(xiàn)程和要執(zhí)行的任務(wù).
備忘錄模式
原理:
在不違反封裝的情況下獲得對(duì)象的內(nèi)部狀態(tài)千埃,從而在需要時(shí)可以將對(duì)象恢復(fù)到最初狀態(tài)抢蚀。
如對(duì)游戲的當(dāng)前狀態(tài)進(jìn)行一個(gè)保存, 然后在后續(xù)游戲中死亡后可以讀取這個(gè)狀態(tài)重新開(kāi)始.
訪(fǎng)問(wèn)者模式
原理:
為一個(gè)對(duì)象結(jié)構(gòu)(比如組合結(jié)構(gòu))增加新能力。
使用訪(fǎng)問(wèn)者模式可實(shí)現(xiàn)重載的動(dòng)態(tài)綁定(即偽雙分派), 效果與重載方法內(nèi)使用 instanceof 是一樣的, 但使用訪(fǎng)問(wèn)者模式, 可擴(kuò)展性更好.
迭代器模式
原理:
提供一種順序訪(fǎng)問(wèn)聚合對(duì)象元素的方法镰禾,并且不暴露聚合對(duì)象的內(nèi)部表示皿曲。
示例: java.util.Iterator
解釋器模式
原理:
為語(yǔ)言創(chuàng)建解釋器,通常由語(yǔ)言的語(yǔ)法和語(yǔ)法分析來(lái)定義吴侦。
示例: EL 表達(dá)式, Freemaker模板
空對(duì)象模式
原理:
使用什么都不做的空對(duì)象來(lái)代替 NULL, 避免空對(duì)象判斷, 避免空指針異常.