意圖(Intent)#
在一個操作中定義演算法的骨架,將某些步驟延遲到子類別實作。Template Method 讓子類別在不改變演算法結構的前提下,重新定義其中的特定步驟。
動機(Motivation)#
假設一個應用程式框架提供 Application 和 Document 類別。Application 負責開啟外部格式的文件,Document 代表讀入後的文件資訊。不同應用程式(繪圖、試算表)透過子類別化來客製化行為。
Application 的 OpenDocument 操作定義了開啟文件的完整流程:檢查能否開啟、建立 Document 物件、加入集合、從檔案讀取。這就是一個 template method——它以抽象操作定義演算法骨架,子類別覆寫這些操作以提供具體行為:
- CanOpenDocument——檢查是否能開啟
- DoCreateDocument——建立對應的 Document
- DoRead——讀取文件內容
Template method 固定了步驟的順序,但讓各子類別自由決定每個步驟的具體實作。
適用場景(Applicability)#
- 將演算法的不變部分實作一次,可變的行為留給子類別
- 多個子類別之間有共同行為,應抽取到共同父類別以避免重複程式碼——即「refactoring to generalize」:先辨識差異、將差異抽取為新操作、以 template method 呼叫這些操作
- 控制子類別的擴展點:在 template method 中特定位置呼叫 hook 操作,只在這些位置允許擴展
結構(Structure)#
- AbstractClass(如 Application)定義抽象的 primitive operations 與 template method
- ConcreteClass(如 MyApplication)實作 primitive operations 以完成演算法的子類別專屬步驟
classDiagram
class AbstractClass {
+TemplateMethod()
+PrimitiveOperation1()*
+PrimitiveOperation2()*
}
class ConcreteClass {
+PrimitiveOperation1()
+PrimitiveOperation2()
}
AbstractClass <|-- ConcreteClass參與者(Participants)#
| 參與者 | 範例 | 職責 |
|---|---|---|
| AbstractClass | - | 定義抽象的 primitive operations,由子類別實作以完成演算法的各個步驟;實作 template method,定義演算法骨架,呼叫 primitive operations 及其他操作 |
| ConcreteClass | - | 實作 primitive operations,完成子類別專屬的演算法步驟 |
協作方式(Collaborations)#
- ConcreteClass 依賴 AbstractClass 實作演算法的不變步驟
優缺點(Consequences)#
Template method 是程式碼重用的基礎技術,在類別庫中尤為重要——它們是在程式庫類別中抽取共同行為的主要手段。
Template method 產生一種反轉的控制結構,稱為 Hollywood 原則:「別打電話給我們,我們會打給你」(Don’t call us, we’ll call you)。父類別呼叫子類別的操作,而非反過來。
Template method 呼叫的操作類型:
| 操作類型 | 說明 |
|---|---|
| 具體操作 | ConcreteClass 或客戶端類別上的操作 |
| AbstractClass 的具體操作 | 對子類別普遍有用的操作 |
| Primitive operations | 抽象操作,子類別必須覆寫 |
| Factory Methods | - |
| Hook operations | 提供預設行為,子類別可以選擇性覆寫(預設通常什麼都不做) |
明確區分哪些操作是 hook(可覆寫)、哪些是 abstract(必須覆寫)至關重要。可透過命名慣例來標示,例如 MacApp 以「Do-」前綴標記可覆寫的操作:DoCreateDocument、DoRead 等。
使用 hook 取代直接覆寫的好處:若子類別透過覆寫父類別操作並手動呼叫 super 來擴展行為,一旦忘記呼叫就會破壞流程。改用 template method 呼叫 hook,讓父類別掌控流程,子類別只需覆寫 hook 即可安全擴展。
實作要點(Implementation)#
- 存取控制:在 C++ 中,primitive operations 應宣告為 protected(確保只被 template method 呼叫),必須覆寫的宣告為 pure virtual。Template method 本身不應被覆寫,因此宣告為 non-virtual
- 最小化 primitive operations 的數量:需要覆寫的操作越少,子類別實作越輕鬆
- 命名慣例:透過前綴或命名規則(如「Do-」前綴)標示可覆寫的操作,幫助開發者辨識擴展點
已知應用(Known Uses)#
- Template method 極為基礎,幾乎所有抽象類別中都能找到
- NeXT AppKit 的 View 類別:Display template method 先呼叫 SetFocus 設置繪圖狀態,再呼叫 DoDisplay hook 讓子類別繪製,最後呼叫 ResetFocus 釋放狀態,確保繪圖前後的不變條件
相關模式(Related Patterns)#
- Factory Method:經常被 template method 呼叫。例如 OpenDocument 中的 DoCreateDocument 就是 Factory Method
- Strategy:Template Method 透過繼承改變演算法的部分步驟;Strategy 透過委派改變整個演算法。兩者互補,是「繼承 vs. 組合」的經典對比