本文用一句話簡述各種設(shè)計(jì)模式, 旨在給自己做個(gè)備忘. 如果我工作中有用到過的場景, 我也順帶提一嘴, 因?yàn)槠鋵?shí)你對(duì)代碼在進(jìn)行各種封裝, 分拆, 優(yōu)化等的時(shí)候, 你用的方法就是別人抽象出來的某種設(shè)計(jì)模式之一. 所以并不是先學(xué)設(shè)計(jì)模式才會(huì)架構(gòu)代碼.
比如, 以前我的老板總是要我把任何第三方庫都按他的要求包一層, 這樣輸出的接口風(fēng)格都是一樣的, 也是他最容易理解的. 老板在提這個(gè)要求的時(shí)候, 不會(huì)是因?yàn)?我要用界面模式來重構(gòu)", 而是真的就是要我封裝成更易理解的API.
- 創(chuàng)建型: 這類模式提供創(chuàng)建對(duì)象的機(jī)制柒昏, 能夠提升已有代碼的靈活性和可復(fù)用性轻要。
- 結(jié)構(gòu)型:這類模式介紹如何將對(duì)象和類組裝成較大的結(jié)構(gòu)蛤奥, 并同時(shí)保持結(jié)構(gòu)的靈活和高效软族。
- 行為模式:這類模式負(fù)責(zé)對(duì)象間的高效溝通和職責(zé)委派藏澳。
創(chuàng)建型模式
工廠模式
用create代替new, 可以通過工廠方法來創(chuàng)建(同類不同性質(zhì)的)對(duì)象, 比如new Ship()
, new Truck()
和CreateTranport()
, 哪個(gè)擴(kuò)展性更高?
生成器(構(gòu)造者)
將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離, 使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示.
相比工廠模式創(chuàng)建船只還是卡車, 如果只是創(chuàng)建卡車, 要個(gè)性化, 一般靠一堆入?yún)? 生成器模式建議將對(duì)象構(gòu)造代碼從產(chǎn)品類中抽取出來婆誓, 并將其放在一個(gè)名為生成器的獨(dú)立對(duì)象中懈费。
- create house
- build walls
- build doors
- build windows
- ...
原型模式
- 用原型實(shí)例指定創(chuàng)建對(duì)象的種類, 并且通過拷貝這些原型創(chuàng)建新的對(duì)象.
- 原型模式將克隆過程委派給被克隆的實(shí)際對(duì)象楔敌。 (即不由第三方來克隆一個(gè)對(duì)象)
單例模式
- 保證一個(gè)類只有一個(gè)實(shí)例, 并且提供一個(gè)全局訪問點(diǎn).
- 為什么會(huì)有人想要控制一個(gè)類所擁有的實(shí)例數(shù)量? 最常見的原因是控制某些共享資源 (例如數(shù)據(jù)庫或文件) 的訪問權(quán)限饲嗽。
- 將默認(rèn)構(gòu)造函數(shù)設(shè)為私有炭玫, 防止其他對(duì)象使用單例類的 new運(yùn)算符。
- 新建一個(gè)靜態(tài)構(gòu)建方法作為構(gòu)造函數(shù)貌虾。 該函數(shù)會(huì) “偷偷” 調(diào)用私有構(gòu)造函數(shù)來創(chuàng)建對(duì)象吞加, 并將其保存在一個(gè)靜態(tài)成員變量中。 此后所有對(duì)于該函數(shù)的調(diào)用都將返回這一緩存對(duì)象尽狠。
結(jié)構(gòu)型模式
適配器模式
將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另一個(gè)接口. 適配器模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作.
橋接模式
將抽象部分與它的實(shí)現(xiàn)部分分離. 沒看懂, 示例是用組合來代替繼承, 比如抽象成顏色和形狀兩個(gè)類, 這樣可以組成出來各種顏色的形狀
組合模式
將對(duì)象組合成樹形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu). 組合模式使得用戶對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性.
示例, 一堆盒子里裝著不同的產(chǎn)品, 要計(jì)算總價(jià), 你可以遍歷, 也可以讓每個(gè)盒子負(fù)責(zé)自己的總價(jià), 這樣我們只要計(jì)算盒子的總價(jià), 而無需關(guān)心每個(gè)"部分"自己的實(shí)現(xiàn)
至少在這個(gè)例子中, 有個(gè)特性, 即自己是一種東西(訂單), 組合到一起也是一樣的東西(訂單), 雖然生產(chǎn)中都是組合在一起呈現(xiàn)的, 但自己也要實(shí)現(xiàn)組合后的類型的一些功能, 比如算總價(jià)
裝飾者模式
動(dòng)態(tài)地給一個(gè)對(duì)象添加一些額外的職責(zé).
- 就增加功能來說, 裝飾者模式相比生成子類更為靈活.
- 就實(shí)現(xiàn)來說, 由繼承一個(gè)類變成了"包裝"一個(gè)類, 其實(shí)就是把原來的功能包到一個(gè)新對(duì)象里去
外觀模式
為子系統(tǒng)中的一組接口提供一個(gè)一致的界面, 外觀定義了一個(gè)高層接口, 這個(gè)接口使得這一子系統(tǒng)更加容易使用.
一般我們把第三方組件包裝一些便捷方法, 也可以理解為外觀模式, 這樣調(diào)用者就不需要關(guān)心第三方組件的實(shí)現(xiàn)了, 類似適配器, 多了個(gè)中介
享元模式
通過共享多個(gè)對(duì)象所共有的相同狀態(tài)衔憨, 讓你能在有限的內(nèi)存容量中載入更多對(duì)象。
比如一個(gè)對(duì)戰(zhàn)游戲, 每一粒子彈是一個(gè)類實(shí)例, 有形狀位置貼圖等等數(shù)據(jù), 滿屏幕的子彈內(nèi)存就吃不消了, 通過觀察可以發(fā)現(xiàn)子彈的貼圖和形狀什么的都是一樣的, 只需要根據(jù)是子彈還是炮彈做個(gè)簡單區(qū)分, 沒必要把這么消耗的資源存到每一個(gè)實(shí)例里
代理模式
為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問. 為什么要控制對(duì)于某個(gè)對(duì)象的訪問呢袄膏? 舉個(gè)例子: 有這樣一個(gè)消耗大量系統(tǒng)資源的巨型對(duì)象(數(shù)據(jù)庫連接)践图, 你只是偶爾需要使用它, 并非總是需要沉馆。
解決: 在客戶端和數(shù)據(jù)庫間加個(gè)代理, 代理可處理延遲初始化和緩存查詢結(jié)果等工作
- 信用卡是銀行賬戶的代理
- 銀行賬戶是現(xiàn)金的代理
行為模式
責(zé)任鏈模式
感覺更像一個(gè)工作流, 比如登錄, 鑒權(quán), 過濾重復(fù)IP等等, 全部揉在一起和依次有序各做各的區(qū)別
命令模式
將一個(gè)請(qǐng)求封裝為一個(gè)對(duì)象, 從而可以用不同的請(qǐng)求對(duì)客戶進(jìn)行參數(shù)化;
其實(shí)封裝GUI的事件就是一個(gè)命令模式, 不同的GUI按鈕可以完成同一個(gè)任務(wù), 而不是為每一個(gè)功能子類化一個(gè)Button, 而且萬一要改變"復(fù)制"的行為, 還得跑到每一個(gè)子類里面去改
迭代器模式
提供一種方法來訪問一個(gè)聚合對(duì)象中的各個(gè)元素, 而又無須暴露該對(duì)象的內(nèi)部表示(列表, 棧, 樹等).
中介者模式
用一個(gè)中介對(duì)象來封裝一系列的對(duì)象交互. 中介者使各對(duì)象不需要顯式地相互引用, 從而使其耦合松散, 而且可以獨(dú)立地改變它們之間的交互.
把相互引用相互依賴改為中介引用所有人, 所有人只引用中介. 組件化和解耦常用
備忘錄模式
在不破壞封裝性的前提下, 捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài), 并在該對(duì)象之外保存這個(gè)狀態(tài). 這樣以后, 就可以在不破壞封裝性的前提下, 將該對(duì)象恢復(fù)到原先保存的狀態(tài). 比如設(shè)計(jì)文檔編輯器, 需要能追溯和回滾每一步的操作, 這些快照需要由文檔類來提供
觀察者模式
- 允許你定義一種訂閱機(jī)制平项, 可在對(duì)象事件發(fā)生時(shí)通知多個(gè) “觀察” 該對(duì)象的其他對(duì)象赫舒。
- 核心就是三件事: 訂閱, 發(fā)布, 和查話詢訂閱對(duì)象.
- 次要的當(dāng)然還有解除訂閱等功能
狀態(tài)模式
- 允許一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變時(shí)改變它的行為. 對(duì)象看起來似乎修改了它的類.
- 比如一個(gè)文檔的不同狀態(tài)下, 對(duì)應(yīng)不同的方法, 而不是把這些方法實(shí)現(xiàn)在文檔類中
- 比如手機(jī)在解鎖和未解鎖狀態(tài)下, 同一按鈕對(duì)應(yīng)不同的功能(而不是在里面不停地做if-else)
策略模式
- 定義一系列算法, 將每一個(gè)算法封裝起來, 并使它們可以相互替換.
- 等于入口類只需要
set strategy
, 和execute
, 真正的執(zhí)行則由strategy來負(fù)責(zé) - 比如設(shè)計(jì)導(dǎo)航類, 你只需要關(guān)心起點(diǎn)終點(diǎn)和途徑點(diǎn), 以及設(shè)定是騎行還是導(dǎo)航, 真天上的路徑規(guī)劃在每個(gè)策略算法中
模板方法模式
- 定義一個(gè)操作中的算法的骨架, 而將一些步驟延遲到子類中. 模板方法使得子類可以不改變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟.
- 跟生成器把一個(gè)構(gòu)造變成N個(gè)構(gòu)建類似, 模板方法將一個(gè)流程變成N個(gè)step, 區(qū)別就在于build沒有先后, 而step則順序相關(guān)
- 比如解析文檔的類, 可以標(biāo)準(zhǔn)化為打開文檔, 解析文檔, 抽象出數(shù)據(jù)結(jié)構(gòu), 解析數(shù)據(jù), 生成報(bào)告等, 前幾步根據(jù)文檔類型都吧可以個(gè)性化, 因此可以交由子類自行實(shí)現(xiàn)
- 上述2和3說的不是一回事, 一個(gè)是自由組合step, 一個(gè)是從既定步驟里找到需要個(gè)性化的步驟把它抽象, 由子類延遲實(shí)現(xiàn)
訪問者模式
它能將算法與其所作用的對(duì)象隔離開來。