本章主軸#

本章把上一章的「以模式思考(thinking in patterns)」實際套用在 CAD/CAM 問題上,與第 4 章的初版解法做對照。最後得出的設計大幅減少類別數,並能輕鬆應對未來 V3。

思考流程#

步驟內容
1. 識別模式找出問題中存在的模式
2. 分析並套用對每個模式重複以下小步:
2a. 排序脈絡找出哪個模式為哪個建立脈絡
2b. 套用模式擴展設計拿排在最前的模式做高層設計
2c. 識別新模式過程中可能浮現新模式
2d. 重複對剩下未整合的模式重複
3. 補上細節完善方法與類別

「以模式思考」並非萬用——只能在問題能用既知模式描述時奏效。一般情況下會結合下一部介紹的 共通/差異分析(CVA)

步驟 1:識別模式#

在前面章節中,作者已從 CAD/CAM 問題抽出四個候選模式:

  • Abstract Factory
  • Adapter
  • Bridge
  • Facade

步驟 2a:找出 seniormost pattern#

「Seniormost」是作者用語,指為其他模式建立脈絡的最外層模式。判斷方式:

  • 兩兩配對比較
  • 看是否一個模式的存在與意義由另一個決定

通則:使用先於建立#

一條重要規則:先決定要哪些物件,再決定如何建立它們。

處理「如何使用」的模式,會為處理「如何建立」的模式設定脈絡。

依此原則,Abstract Factory 一定排在最後(除非另一個建立類模式並列)。

比較剩下三個模式#

  • Bridge vs Adapter:Bridge 定義了實作要符合的介面,Adapter 才知道要把 V2 的 OOGFeature 轉成什麼樣。Bridge 為 Adapter 建立脈絡。
  • Bridge vs Facade:Facade 把 V1 的繁瑣呼叫封起來,Bridge 的實作端會使用這個 Facade。Bridge 為 Facade 建立脈絡。

Bridge 勝出,是 seniormost pattern。

步驟 2b:套用 Bridge#

從整體看,系統把 CAD/CAM 模型轉成 NC set。中央類別是 Model,內含一群 Feature

Figure 13-5: Model 設計——Model 含一群 Feature 子類(Slot、Hole、Cutout 等)

Figure 13-6: 使用繼承處理兩種 Model 類型——V1Model 與 V2Model

Figure 13-7: 用 Bridge 處理 Model 的兩種版本——但因只有實作變動,這裡不真的需要 Bridge

  • Feature 為 Bridge 中的 Abstraction
  • 衍生出 SlotFeatureHoleFeatureCutoutFeatureSpecialFeatureIrregularFeature
  • Implementation 端,定義 ImpFeature,讓 V1ImpV2Imp 各自實作

Figure 13-8: Bridge 模式的通用結構——Abstraction、RefinedAbstraction、Implementor、ConcreteImplementor

Figure 13-9: 套用 Bridge 後的設計——Feature 抽象 + ImpFeature 實作

ImpFeature 的介面包含:

  • getXgetYgetLength
  • 部分特徵專用方法如 getEdgeType

Model 本身沒有真正的 Bridge——只有實作隨版本不同。直接以繼承做出 V1ModelV2Model 即可,不需強行套 Bridge。

步驟 2c:尋找新出現的模式#

掃過目前設計後沒有新模式浮現。剩下的工作是把 V1、V2 串接進來——交給 Facade 與 Adapter。

一個額外收穫:套用 Bridge 後可看出 V1、V2 在 feature 物件眼中其實已可互換——這個彈性在原本充滿細節的設計中被隱藏起來了。

步驟 2a/2b 重複:套用 Facade(V1 端)#

V1 是一堆 C 風格函式呼叫。建立 V1Facade

  • 對外提供 V1Imp 真正需要的簡化方法
  • 內部呼叫 V1 模組
  • V1Imp 持有自己的 model number 與 feature ID,依此向 V1Facade 詢問

每個 Feature 會對應一個 V1Imp,因為 V1 需要每個 feature 自己記住識別資訊。

步驟 2a/2b 重複:套用 Adapter(V2 端)#

V2 提供物件導向的 OOGFeature。建立 V2Imp 包住 OOGFeature,讓它能以 ImpFeature 介面被使用。

步驟 2a/2b 重複:Abstract Factory?#

最終結論:不需要

  • 起初判斷 Abstract Factory 適用於「同版本下要拿一整組 V? feature」
  • Model 物件本身已經知道現在用的是哪個版本,能直接負責建立
  • 既然已有清楚的封裝,不需要硬塞模式

一開始把 Abstract Factory 列入考慮並不是浪費——它幫助我們從多角度檢視問題。錯估某模式存在不會帶來損失,反而能釐清思路。

步驟 3:補上細節#

  • Feature 子類只擁有概念性方法(getLocationgetLength),不含實作資訊
  • V1Imp 知道自己對應的 V1 model 與 feature ID,再向 V1Facade 取資料
  • V2Imp 持有對應的 OOGFeature,把請求轉發過去

與第 4 章解法的對照#

Figure 13-12: 第 4 章的初版解法——5 種 Feature × 2 個 CAD/CAM 版本造成類別爆炸

用「念」的方式比較#

第一版:Model 包含 Features;Feature 是 slot/hole/cutout/…;Slot 又分 V1Slot 與 V2Slot;V1Slot 用 V1 系統、V2Slot 用 OOGSlot;Hole 又分 V1Hole 與 V2Hole;V1Hole 用 V1 系統、V2Hole 用 OOGHole⋯⋯

第二版:Model 包含 Features;Feature 是 slot/hole/cutout/…;所有 Feature 都包含一個實作物件,它是 V1 實作或 V2 實作;V1 實作透過 V1Facade 存取 V1,V2 實作把 OOGFeature 包成 Adapter。

第二版「念」起來短得多,也更乾淨。

V3 出現時#

  • 第一版:每種 feature 都要新增一個 V3 衍生類
  • 第二版:只需新增一個 Adapter(或 Facade),舊類別完全不動

本章要記住的事#

  • 「先用後造」:先決定要哪些物件,再思考怎麼建立它們
  • 透過比對哪個模式為哪個建立脈絡,可找到 seniormost pattern
  • 模式並非套愈多愈好;不適用就乾脆放掉
  • 從整體脈絡推導出的設計,能直接展現可擴展性
  • 用「念」整個設計圖,是一個簡單但有效的好設計檢驗法