本章主軸#
學設計模式有兩種方式:學會把模式套到合適的情境,或學會模式背後的原則與策略。本章專注在後者——這些原則才是讓你在沒有現成模式可套時,也能想出「像模式般優雅」的解法的能力來源。
開放封閉原則(OCP, Open-Closed Principle)#
模組、方法、類別應對「擴充」開放,對「修改」封閉。
由 Bertrand Meyer 提出。意思是:設計軟體時,要讓未來能加新功能而不必改動既有程式。
100% 達成 OCP 通常不可能,但作為目標,它指引我們做出更耐改的設計。Bridge 就是 OCP 的典型示範——加新實作完全不用動既有類別。
從脈絡設計(Designing from Context)#
延續 Alexander 的思想。Bridge 模式是最佳範例:
- 設計 Implementation 介面時,要看 Abstraction 衍生類別會怎麼用它
- 介面從「服務需求」反推回來決定,而不是先做完才看怎麼配
依賴倒置原則(Dependency Inversion Principle)#
- 高層模組不應依賴低層模組——雙方都應依賴抽象
- 抽象不應依賴細節——細節應依賴抽象
Liskov 替換原則(Liskov Substitution Principle)#
衍生類別必須支援基底類別的所有行為。
作者更進一步:使用方根本不該察覺有衍生發生。子類別不應在基底介面之外再加新公開方法,且基底必須是該概念完整的規格。
「機會 ≠ 必須立刻採用」#
模式提醒我們可能的變動點,但不代表現在就要為這些變動寫程式碼。
透過模式建立的良好抽象,就足以容納未來的變動——即使那是當初沒預料到的變動。
用脈絡決定實作#
以 Abstract Factory 為例,可有多種實作方式:
| 實作方式 | 取捨 |
|---|---|
| 為每個家族做一個衍生類 | 經典做法,遵守 OCP,但較繁瑣 |
| 單一物件加 switch | 不嚴格 OCP,但簡單,所有規則集中一處 |
| 配置檔 + switch | 較具彈性,仍要改程式 |
| 配置檔 + 動態類別載入 | 最具彈性,新類別不需改程式 |
比較不同方案時,不要問「哪個比較好」,要問「在什麼情境下哪一個比較好」「哪個情境最像我的問題」。
這個小小的思考切換,會讓你考慮到變動性與可擴展性,並避免太快收斂在第一個答案。
Adapter 與 Facade 都是脈絡型模式#
- Adapter 必須依被適配到的介面而定,介面不存在就無法定義
- Facade 的介面常常隨著系統發展逐步成形,每個新的使用者都會塑造它
作者一度以為「Adapter / Facade 一定不是 seniormost」。後來發現有些系統必須符合既定介面,這時 Facade 或 Adapter 反而會是最外層。
封裝變動的原則#
作者觀察自己的設計:繼承很少超過兩層;只在 Decorator 那種模式下會有第三層。理由:
我從不讓一個類別同時承載兩個會獨立變動的議題,除非這兩者本就緊密耦合。
各模式封裝變動的方式:
- Bridge:封裝實作的變動
- Abstract Factory:封裝「該建立哪一群物件」的變動
- Adapter:把不同介面統一,配合其他模式的變動需求
- Facade:通常不是封裝變動,但同一介面包不同子系統時,可順便達成
模式不只在封裝變動,也定義了物件之間的關係——例如 Bridge 同時定義抽象與實作的兩個變動軸如何關聯。
抽象類別 vs 介面#
表面差異#
抽象類別可包含共同狀態與行為;介面只能定義方法。Java、C# 只允許單一繼承,因此選錯就回不去。
設計層差異#
- 抽象類別:把相關實作收攏(focus on encapsulating implementations),仍可遵循依賴倒置
- 介面:思考「使用方需要什麼樣的介面」,可拆得更細、更精簡(thin interface)
兩者不是一個比另一個好。可先用介面定義出最小契約,再用抽象類別實作以共享預設行為。
健康的懷疑(Healthy Skepticism)#
模式是經驗的提煉,不是真理。
它必須與真實世界資料對照後使用。
模式可能犯下的錯誤:
| 錯誤 | 描述 |
|---|---|
| 表面化(Superficiality) | 對下層情況了解不深,太快選定模式 |
| 偏見(Bias) | 過度相信模式,把所有資料都用它來解讀 |
| 選錯(Selection) | 不熟悉適用脈絡,選了錯誤模式 |
| 誤診(Misdiagnosis) | 對整套模式不夠熟悉,導致誤判 |
| 削足適履(Fit) | 為了讓現實符合模式,刻意忽略例外 |
「對的」模式存在於問題本身,不是被你硬套上去。實作方式也應由問題的限制決定,而不是模式書上長什麼樣。
本章重點#
| 原則 | 摘要 |
|---|---|
| OCP | 開放擴充、封閉修改 |
| 依賴倒置 | 高低層皆依賴抽象 |
| 封裝變動 | 找出變動軸並隔離 |
| 健康懷疑 | 模式只是工具,不是真理 |