# Design Pattern
###### tags: `design pattern`
[toc]
## [Design Pattern 前情提要](/G75gyOnSR_SrCc4lbbxoNg)
## [類與類的關係](https://hackmd.io/RvsDMWdSSoG6q2JMyXseng)
## [設計模式的原則](https://hackmd.io/amK1jYMqQ4qDjewBuc7Xsw?view)
要去除if-else -> 策略模式
要去除狀態相關if-else -> 狀態模式
要延後選擇創建物件的類別 -> 工廠模式
要做callback -> 觀察者模式
要解耦第三套件的依賴 -> 轉接器模式
## Design Pattern Overview
一般,用九個屬性描述模式
- 名字與分類
* 創建型模式主要用於描述**如何創建對象**
* 結構型模式主要用於描述**如何實現類或對象的組合**
* 行為型模式主要用於描述**類或對象怎樣交互以及怎樣分配職責**
- 意圖(目的/預期成效)
- 動機(解決何種問題)
- 適用場景
- 物件導向架構圖(類別圖)
- 物件實體參與者說明
- 參與者之間的互動說明
- 套用此模式之後的優缺點
- 不同語言下的各種實作方法

> 在日常應用中,設計模式從來都不是單個設計模式獨立使用的。在實際應用中,通常多個設計模式混合使用。
| 模式名稱 | 難度1-5 | 使用頻率1-5 |
| ------------------------ | ------------------------------ | ------------------------------ |
| Singleton | :star: | :star::star::star::star: |
| Simple Factory | :star::star: | :star::star::star: |
| Factory Method | :star::star: | :star::star::star::star::star: |
| Abstract Factory | :star::star::star::star: | :star::star::star::star::star: |
| Builder | :star::star::star::star: | :star::star: |
| Prototype | :star::star::star: | :star::star::star: |
| Bridge | :star::star::star: | :star::star::star: |
| Adapter | :star::star: | :star::star::star::star: |
| Composite | :star::star::star: | :star::star::star::star: |
| Decorator | :star::star::star: | :star::star::star: |
| Facade | :star: | :star::star::star::star::star: |
| Flyweight | :star::star::star::star: | :star: |
| Proxy | :star::star::star: | :star::star::star::star: |
| Chain of Responsibility | :star::star::star: | :star::star: |
| Command | :star::star::star: | :star::star::star::star: |
| Interpreter | :star::star::star::star::star: | :star: |
| Iterator | :star::star::star: | :star::star::star::star::star: |
| Mediator | :star::star::star: | :star::star: |
| Observer | :star::star::star: | :star::star::star::star::star: |
| Memento | :star::star: | :star::star: |
| State | :star::star::star: | :star::star::star: |
| Strategy | :star: | :star::star::star::star: |
| Visitor | :star::star: | :star::star::star: |
| Template Method | :star::star::star::star: | :star: |
outline
- 介紹 + bad design 例子
- 實現 觀念講解
- 結構 需要哪些 元件
- 例子 java為主 python為補充
- 優缺點
### 創建型模式
---
- 將對象的建立與使用分離
- 運行機制基於對象的創建方式
- 將對象創建的細節隔離
- 程式碼跟創建對象的類無關
- [單例模式 Singleton](/g_jEd7M1SpOZx1wAMUxoyg)
- [工廠方法模式,抽象工廠模式](/cABD4n8XQlGHyKcGta88xw)
- [建造者模式](/68efHrUDQ4mKxReP9u-ueg)[java example](https://ithelp.ithome.com.tw/articles/10204732), [python example](https://www.cnblogs.com/baxianhua/p/11535677.html)
- [原型模式 prototype](/yFVVMcMCRtCk3dt9c2dMwQ)
工廠模式:用於創建物件,將實際建立的過程封裝起來,提供一個通用接口。
抽象工廠模式:創建相關的物件家族,而不需要指定具體的類別。
單例模式:確保一個類別只有一個實例,通常用於全執行週期。
建造者模式:分步創建複雜物件,允許客戶端定制。
原型模式:通過複製現有物件來創建新物件,減少實體化成本。
### 結構型模式
---
+ 通過組合或其他佈局獲得更強大功能的物件或類的結構
+ 簡化結構並識別類與對象的關係
+ 主要關注類的繼承與組合
- [裝飾器模式 (Decorator Pattern)](/oWXpKi1FS2OAsQzBpKJ0gQ) : 動態添加物件功能,而不修改其代碼。
- [外觀模式(Facade Pattern)](/NN5lMYp9QBG5TvF19J5zwA) : 簡化一個子系統的接口,提供更高級的界面。
- [代理模式 (Proxy Pattern)](/JoF8jm-SSqeUg91lGoEk6g) : 提供一個替代物件,控制對其他物件的訪問。
- [轉接器模式 ( Adapter Pattern )](https://hackmd.io/d3QW1ayZTCeUv-MrtQZY9A):允許接口不兼容的類別協同工作。
- [組合模式 ( Composite Pattern)](https://ithelp.ithome.com.tw/articles/10207478):將物件組織成樹狀結構,以表示部分-整體層次結構。
- [享元模式 ( Flyweight Pattern )](https://hackmd.io/C65VoM4jTcuZKVM0xuZARw):共享大量相似物件的內部狀態,以節省記憶體。
- [橋接模式 ( Bridge Pattern )](https://hackmd.io/iuneEW9SSX6O-tL2gmG0Vw):將抽象和實現分離,以允許它們各自獨立變化。
### 行為(方法)型模式
---
- 用於描述類與對象之間如何相互協作以完成單個對象無法完成的任務以及如何分配職責
- 關注對象間的交互作用及對象的響應性
- 對象應該能夠交互,同時保持鬆散耦合
- [策略模式 (Strategy Pattern)](https://hackmd.io/Efbvbs3tTC6U-oCOMfgERw):定義一系列算法,將它們封裝起來,使它們可以相互替換。
- [ 責任鍊模式 ( Chain of Responsibility Pattern )](https://hackmd.io/zWvUmFxxSTKzL0KnkhWakQ) :多個對象按順序處理請求,直到有一個處理為止。:通過不同的處理者來處理請求,直到有一個處理為止。
- [模板方法模式 Template Method](/rq5-WUjoR_GCGcv5THz9qw?both):定義一個算法的骨架,將一些步驟推遲到子類實現。
> 如果你有一組行為想到**策略模式**,而行為不進相同想到**樣板方法**,如果想要有彈性的在系統中支援新的需求處理者,想到**責任鍊模式**。
- [觀察者模式(Observer Pattern)](/Kx4tFJnJSPqnAt8PjeSjyQ):定義一對多的依賴關係,當一個物件改變狀態時,所有依賴它的物件都會受到通知。
- [命令模式 Command](/bfR07BniRi67zETkvi-6yQ):封裝請求成物件,允許參數化客戶端物件。
- [狀態設計模式 State](/GDr5Gja4QHyQP9GJ14dVbw):將對象的行為委託到不同的狀態對象,使其能夠在內部狀態變化時改變行為。
- [備忘錄模式](/eWNZgszuRFCJU_UvjSvLNg):允許對象捕獲和恢復其內部狀態,而不依賴其他對象。
- [迭代器模式 ( Iterator Pattern )](https://hackmd.io/r0jJZ312Sf6SUdY4WU9CKQ) :提供一種方法來順序訪問一個聚合對象中的元素。
- [ 仲介者模式 ( Mediator Pattern )](https://hackmd.io/wPGYHajaQYKLA4ElhIdvyw) :定義一個中介對象,來協調多個對象之間的通信。
- [解譯器模式 ( Interpreter Pattern)](https://hackmd.io/mdHi6tMyQMWOwT6hLK4siA):定義語言文法的解釋,以執行對語言的特定操作。
- [訪問者模式 (Visitor Pattern)](https://hackmd.io/3sZOBQhUQdaB37CeYnbDyg):允許你對一組物件的操作進行封裝,而不需要改變它們。
### 模型-視圖-控制器---複合模式
- 複合模式將兩個或更多模式組合成解決方案
- 模型提供數據及業務邏輯,聲明一個存儲和操作數據的類
- 視圖負責數據的展示,聲明一個類來建構用戶界面和顯示數據
- 控制器是兩者間的黏合劑,聲明一個連接模型和視圖的類
- 控制器和視圖依賴於模型,模型是可以獨立工作的
- 網站就是一個MVC例子
- 模型類定義針對的所有操作(創建修改刪除),並提供與數據使用方式有關的方法
- 視圖類代表用戶界面,提供相應的方法,幫助我們根據上下文和應用程序的需要來建構WEB或GUI界面,不應該包含自己的任何邏輯也不應該與數據庫直接交互,而只應該用來顯示收到的數據
- 控制器類從請求接收數據,並將其發送到系統的其他部份,它需要提供用於路由請求的方法
```python=
class Model:
def logic(self):
data = 'Got it!'
print('Model: Crinching data as per business logic')
return data
class View:
def update(self, data):
print('View: Updating the view with results: ', data)
class Controller:
def __init__(self):
self.model = Model()
self.view = View()
def interface(self):
print('Controller: Relayed the Client asks')
data = self.model.logic()
self.view.update(data)
class Client:
print('Client: asks for certain information')
controller = Controller()
controller.interface()
```
### 反模式
- 不良設計
- 不動性:此種方式開發程序非常難以重用
- 剛性:此種方式開發程序,任何小變動都導致太多部份必須進行相應的變動
- 脆弱性:當前程序的更改,導致系統變的非常容易崩潰
- 黏滯性:由於架構層面的修改非常困難,因此修改必須由開發人員在代碼或環境本身進行
- 原因
- 開發人員不了解軟體開發實踐
- 開發人員沒有將設計模式應用在正確上下文
### 軟體開發反模式
- 原因
- 開發人員的想法會隨著開發過程的推進而發生改變
- 用例通常會隨著客戶的反饋進行修改
- 最初的數據結構可能會隨著功能或可伸縮性等方面的考慮而發生變化
- 義大利麵條式代碼
- 原因
- 對面向對象的無知
- 沒有考慮產品架構或設計
- 快餐式思維
- 症狀
- 後果
- 結構重用性會降低
- 維護工作量大
- 進行修改時,擴展性和靈活性或降低
- 金錘
- 原因
- 由於某解決方案過去多次經驗有效,所以推廣到更多地方,一把鎚子搞定所有釘子
- 來自不了解具體問題的高層(架構司或技術領袖)的建議
- 雖然某解決方案過去多次經驗有效,但當前項目卻具有不同的背景和要求
- 公司已經被這種技術綁定,或員攻對這種技術情有獨鍾,因為已經用順手
- 後果
- 癡迷一種解決方案,並把它應用在所有項目
- 不是通過功能,而是通過開發中使用的技術來描述產品
- 沒有滿足需求,造成與用戶預期不同
- 熔岩流
- 原因
- 這種反模式與軟體應用程序中的死代碼或一段用不到的代碼有關,人們害怕一旦對其修改,就會破壞其他東西,隨著時間流逝,這段代碼一直留在軟件中,固定其位置,就像熔岩變硬岩
- 大量的試錯程式碼
- 由於一個人單獨編寫代碼,未經審查,就在沒有任何培訓情況下轉交給其他開發團隊
- 軟體架構或設計的初始思想是通過代碼庫實現的,但沒人能理解
- 症狀
- 開發的測試工作具有很低的代碼覆蓋率
- 代碼中含有莫名其妙的註釋
- 過時的接口,或開發人員需要圍繞既有代碼展開工作
- 複製貼上或剪貼式編程
- 原因
- 許多經驗豐富的開發人員會將自己代碼片段放在網路上(Github Stack Overflow),從而成為一些常見問題的解決方案,有些開發人員會原封不動複製片段,並應用在自己程式中,沒有考慮這些代碼是否最大程度的優化
- 新手開發人員不習慣編寫代碼或不知道如何開發代碼
- 快速修復bug或急就章式的開發
- 代碼重複,無法滿足跨模塊標準化以及代碼結構化的要求
- 缺乏長遠打算
- 後果
- 多個軟體應用程序存在同種類型的問題
- 維護成本會變高,同時bug的生命週期也會變長
- 較少的模塊代碼庫,相同的代碼會散落於各處
- 繼承問題
### 軟體架構反模式
- 重新發明輪子
- 不要重新發明輪子,架構的重用,重新審視相同的問題並為它重新設計解決方案並沒有意義,這基本上就是重新發明輪子
- 原因
- 缺乏中央文檔或存儲庫來講解架構級的問題或存放已實現的解決方案
- 社區或公司內的技術領袖缺乏溝通
- 組織中遵循的流程是從頭開始構建的,通常情況下,這樣的流程是不成熟的,並且流程的實現通常是不嚴謹的,很難堅持
- 後果
- 解決一個標準問題德解決方案太多,其中許多解決方案並不周全
- 耗費團對的時間和資源
- 封閉的系統架構,重複的勞動,糟糕的風險管理
- 供應商套牢
- 產品公司往往依賴供應商提供的某些技術,這些技術對於他們的系統來說密不可分,以至於系統很難擺脫這些技術
- 供應商鎖定的原因
- 熟悉供應商公司的權威人士以及採購的折扣
- 基於營銷或銷售業務而不是技術評估選擇的技術
- 當前項目使用過驗證的技術(使用此技術的投資報酬率高),即時它不適用當其項目的需求或要求
- 技術人員或開發人員已經接受過技術的培訓
- 後果
- 產品的發布週期或維護週期直接取決於供應商發布時間
- 產品是圍繞技術而不是根據客戶要求開發
- 產品上市時間不可靠,不能滿足客戶的期望
- 委員會設計
- 有時根據組織的流程,一群人會坐在一起設計特定的系統,所得到的軟體架構通常是複雜或不及格的,因為涉及太多的思維過程,並且設計思想可能是由沒有相應的技能或供應產品設計經驗的技術專家所提出的
- 原因
- 根據組織的流程,產品的架構或設計是由眾多的利益相關者批準的
- 沒有指定單獨的聯繫人或負責設計的架構司
- 營銷或技術專家確定設計優先級,而不是由客戶反饋來決定
- 症狀
- 開發人員和架構司之間的觀點衝突,即使在設計完成後依舊如此
- 過於複雜的設計,很難紀錄
- 規格或設計的任何變動都需要多次審查,導致實現延遲
## 別人的應用
- [美團工厂模式、策略模式、状态模式、责任链模式](https://tech.meituan.com/2020/03/19/design-pattern-practice-in-marketing.html)
## 參考資料
- Python設計模式
- [鐵人blog](https://ithelp.ithome.com.tw/articles/10201706)
- [Refactoring.guru](https://refactoring.guru/design-patterns/)
- [C語言中文網](http://c.biancheng.net/view/1317.html)
- [史上最全设计模式导学目录(完整版)](https://blog.csdn.net/lovelion/article/details/17517213)
- [Gitbook](https://wizardforcel.gitbooks.io/w3school-design-patterns/content/1.html)
- 大話設計模式