1.1 什麼是 Design Pattern#

設計物件導向軟體很難,設計可重用的物件導向軟體更難。有經驗的設計師不會每次都從零開始解決問題,而是重用過去成功的解決方案。這些反覆出現在物件導向系統中的 class 與物件溝通模式,就是 Design Pattern。

Christopher Alexander 的定義:每個 pattern 描述一個在環境中反覆出現的問題,以及該問題的核心解法,使你能無限次使用它,而每次的應用方式都不完全相同。

一個 Design Pattern 包含四個基本要素:

要素說明
Pattern Name用一兩個詞描述設計問題及其解法,提升設計詞彙的抽象層次
Problem描述何時該套用此 pattern,包含問題的背景脈絡與前提條件
Solution描述設計中的元素、關係、職責與協作方式。不是具體實作,而是可應用於多種情境的模板
Consequences套用 pattern 的結果與權衡,包括對彈性、擴充性、可移植性的影響

本書的 Design Pattern 聚焦在特定抽象層級:不是像 linked list 那樣可直接編碼重用的資料結構,也不是整個應用系統的領域設計,而是用來解決特定 context 中一般性設計問題的、可自訂的 class 與物件溝通描述

1.2 Smalltalk MVC 中的 Design Pattern#

Model/View/Controller (MVC) 是 Smalltalk-80 中建構 UI 的經典架構,其中蘊含多個 Design Pattern:

  • Observer Pattern:Model 與 View 之間透過 subscribe/notify 協定解耦。當 Model 資料變更時通知所有 View 更新,允許同一 Model 附加多個不同呈現方式的 View
  • Composite Pattern:View 可以巢狀組合(CompositeView),將多個子 View 當作單一 View 操作,形成 part-whole 階層
  • Strategy Pattern:View 與 Controller 的關係 — Controller 封裝了使用者輸入的回應策略,可在執行期替換不同的 Controller 來改變行為

此外,MVC 也運用了 Factory Method(指定 View 的預設 Controller class)和 Decorator(為 View 加上捲動功能)。

1.3 描述 Design Pattern 的方式#

本書採用統一的模板描述每個 pattern,包含以下章節:

章節說明
Intentpattern 的目的與要解決的問題
Motivation具體情境說明問題與解法
Applicability何時適用,如何辨識適用情境
Structure以圖形表示 class 結構與物件互動
Participants參與的 class/object 及其職責
Collaborations參與者如何協作
Consequences權衡與影響
Implementation實作時的注意事項與語言相關議題
Sample CodeC++ 或 Smalltalk 範例
Known Uses至少兩個來自不同領域的實際應用
Related Patterns相關 pattern 及其差異

1.4 Design Pattern 目錄#

本書收錄 23 個 Design Pattern,依功能可分為三大類:

  • Creational:Abstract Factory、Builder、Factory Method、Prototype、Singleton
  • Structural:Adapter、Bridge、Composite、Decorator、Facade、Flyweight、Proxy
  • Behavioral:Chain of Responsibility、Command、Interpreter、Iterator、Mediator、Memento、Observer、State、Strategy、Template Method、Visitor

1.5 如何組織 Design Pattern 目錄#

Design Pattern 依兩個維度分類:

  • Purpose(目的)

    • Creational — 處理物件建立過程
    • Structural — 處理 class 或 object 的組合
    • Behavioral — 描述 class 或 object 如何互動與分配職責
  • Scope(範圍)

    • Class pattern — 透過 Inheritance 處理 class 間的關係,屬於靜態(compile-time)
    • Object pattern — 處理 object 間的關係,屬於動態(run-time),大多數 pattern 屬此類

Pattern 之間也存在多種關聯:常一起使用(如 Composite + Iterator)、互為替代方案(如 Prototype vs Abstract Factory)、或結構相似但意圖不同(如 Composite vs Decorator)。

Figure 1.1: Design pattern relationships

1.6 Design Pattern 如何解決設計問題#

尋找適當的物件#

物件導向設計的困難在於將系統分解為物件,需要考慮 encapsulation、granularity、dependency、flexibility 等因素。Design Pattern 幫助辨識不那麼明顯的抽象,例如:

  • Strategy — 將演算法表示為物件
  • State — 將狀態表示為物件

決定物件粒度#

不同 pattern 處理不同粒度的問題:

Pattern粒度處理
Facade將整個子系統表示為單一物件
Flyweight支援大量細粒度物件的共享
Abstract Factory / Builder專門負責建立其他物件
Command / Visitor將對物件的操作本身封裝為物件

指定物件 Interface#

  • Interface 是物件所有 operation signature 的集合,定義了物件能接收的請求
  • Type 是特定 interface 的名稱;一個物件可擁有多個 type
  • Dynamic binding 讓請求在 run-time 才與具體實作關聯
  • Polymorphism 讓擁有相同 interface 的物件可在 run-time 互相替換

Program to an interface, not an implementation. 不要將變數宣告為具體 class 的實例,而是只依賴 abstract class 所定義的 interface。這是本書 Design Pattern 的共通主題。

Class Inheritance vs Interface Inheritance#

  • Class inheritance(class 繼承):定義物件的實作,是程式碼與表示的共享機制
  • Interface inheritance(subtyping):定義物件何時可替代另一物件

許多語言(如 C++)不區分兩者,但實務上這個區分非常重要。許多 Design Pattern 依賴此區分,例如 Chain of Responsibility 中的物件必須共享 type 但不需共享實作。

Inheritance vs Composition#

  • Inheritance(白箱重用):靜態、compile-time 定義,父類內部對子類可見。缺點是破壞封裝,父類變動會連帶影響子類
  • Object Composition(黑箱重用):動態、run-time 組合,透過 interface 互動不破壞封裝,物件可在 run-time 替換

Favor object composition over class inheritance. 設計師傾向過度使用繼承。優先使用物件組合通常能讓設計更簡單、更易重用。

Delegation#

Delegation 是讓 composition 擁有如同 inheritance 般強大重用能力的手段。接收物件將操作委託給其 delegate 物件,並將自身傳入讓 delegate 能回頭引用。

  • 優點:可在 run-time 動態組合行為
  • 缺點:動態、高度參數化的軟體較難理解
  • 使用 delegation 的 pattern:State、Strategy、Visitor(重度依賴);Mediator、Chain of Responsibility、Bridge(較輕度使用)

Parameterized Types(泛型)#

Parameterized Types(又稱 Generics / Templates)提供第三種組合行為的方式,讓你定義 type 時不需指定所有使用的型別。與 inheritance 和 composition 的差異在於:它無法在 run-time 改變。

為變化而設計#

Design Pattern 的核心價值在於讓系統能以特定方式應對變化。常見的重新設計原因與對應的 pattern:

重新設計原因對應 Pattern
明確指定 class 建立物件Abstract Factory、Factory Method、Prototype
依賴特定操作Chain of Responsibility、Command
平台依賴Abstract Factory、Bridge
依賴物件表示或實作Abstract Factory、Bridge、Memento、Proxy
演算法依賴Builder、Iterator、Strategy、Template Method、Visitor
緊耦合Abstract Factory、Bridge、Chain of Responsibility、Command、Facade、Mediator、Observer
透過 subclassing 擴充功能Bridge、Chain of Responsibility、Composite、Decorator、Observer、Strategy
無法方便地修改 classAdapter、Decorator、Visitor

書中也討論了 Application、Toolkit、Framework 三種軟體層級。Framework 強調 design reuse(設計重用)而非僅 code reuse(程式碼重用),並帶來控制反轉(inversion of control)。成熟的 framework 通常整合了多個 Design Pattern。

1.7 如何選擇 Design Pattern#

找到合適 pattern 的幾種策略:

  • 閱讀 Section 1.6 了解 pattern 如何解決各類設計問題
  • 瀏覽每個 pattern 的 Intent 段落,找出與問題相關者
  • 研究 pattern 之間的關聯圖(Figure 1.1),循線找到相關 pattern
  • purpose 分類(Creational / Structural / Behavioral)縮小範圍
  • 檢視你面臨的重新設計原因,對應上述列表
  • 思考設計中哪些部分需要可變,找出能封裝該變化的 pattern

1.8 如何使用 Design Pattern#

套用 Design Pattern 的步驟:

  1. 通讀一遍:特別注意 Applicability 與 Consequences,確認 pattern 適合你的問題
  2. 研究結構:理解 Structure、Participants、Collaborations 中的 class 與 object 關係
  3. 看範例程式:透過 Sample Code 學習實作方式
  4. 命名參與者:在應用情境中選擇有意義的名稱(例如用 SimpleLayoutStrategy 而非抽象的 ConcreteStrategy
  5. 定義 class:宣告 interface、建立繼承關係、定義 instance variable
  6. 定義操作名稱:依據職責與協作關係命名,保持一致的命名慣例
  7. 實作:參考 Implementation 段落的提示完成實作

Design Pattern 不應被不加思考地套用。許多 pattern 透過增加間接層來獲得彈性,這會使設計更複雜或犧牲效能。只有在確實需要該彈性時才應使用。