意圖(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. 組合」的經典對比