本章主軸#

樣板方法模式(Template Method)解決另一種變動:步驟一致、實作不同。它在抽象類別中固定流程骨架,把可變的步驟下放給子類實作。也常被用來消除「複製貼上 + 微改」造成的重複。

案例:支援多種 SQL 資料庫#

要同時支援 Oracle 與 SQL Server。雖然兩者皆遵循 SQL 標準,但細節不同。執行查詢的高層步驟卻是一樣的:

  1. 格式化 CONNECT 指令
  2. 送出 CONNECT
  3. 格式化 SELECT 指令
  4. 送出 SELECT
  5. 回傳結果集

差別只在某些步驟的「格式化」實作。

GoF 對 Template Method 的意圖描述#

在一個操作中定義演算法的骨架,把某些步驟延遲到子類別。讓子類重新定義這些步驟,但不改演算法的結構。

套到案例上#

  • 抽象類別 QueryTemplate 內有 doQuery() 方法,照五個步驟跑流程
  • 抽象方法 formatConnect()formatSelect() 留給子類實作
  • OracleQTSQLSvrQT 各自提供具體實作

Figure 19-1: 用 Template Method 執行查詢——doQuery 框架共用,formatConnect/formatSelect 由子類提供

多型在這裡的關鍵:透過 QueryTemplate 參考呼叫 doQuery(),但因為實際物件是 OracleQT,所以 formatConnect 會解析到 Oracle 的版本。

新支援哪種資料庫?只需新增一個衍生類別,照「樣板」填空。

用 Template Method 消除重複#

作者另一段經驗:團隊面對許多相似但細節不同的合作夥伴系統,採用 if-then-else 與複製貼上交替——程式碼愈來愈難維護。

複製貼上後的兩種重複#

  • 明顯的重複:完全沒改的那幾行
  • 流程重複:步驟順序相同,只有實作不同

重構流程#

  1. 用「Extract Method」把會變的部分抽成獨立方法(即使尚未要泛化)
  2. 建立基底類別承載不變的 someMethod
  3. 寫第二個衍生類別實作其變化部分

「先用 Extract 把變動的部分抽出,再建立基底承接」其實就是 GoF「找出變動並封裝」的具體流程。

若一開始就 program by intention,這一步會自然發生。

重構與 OCP 的兩段式手法#

步驟內容
1. 重構但不加新功能讓即將加入的功能可以「插」進來而不必改既有程式
2. 加入新功能只動新類別與工廠

知道模式,能讓你在 XP 風格的開發中也用得上「先重構、再擴充」的雙步驟。

一個沒接觸過模式的設計者,常根本沒察覺自己在重複——而模式提供了思考的工具。

Template Method 的關鍵特性#

欄位內容
Intent在操作中定義演算法骨架,把某些步驟下放到子類
Problem高層流程一致,低層實作有變化
Solution抽象方法定義可變步驟,呼叫關係寫死在基底
Consequences適合代碼重用;若步驟需獨立變動,應改用 Strategy
Implementation抽象方法在基底類別、衍生類別補上具體實作

Figure 19-7: Template Method 模式的通用結構——AbstractClass 定義骨架,ConcreteClass 提供原始操作

實務筆記#

Template Method ≠ 一組捆在一起的 Strategy#

兩者結構乍看相像,但 Template Method 的可變步驟緊密耦合——它們是同一個流程的環節,會「一起」變。Strategy 則代表互相獨立的可換演算法。

若把 Template Method 想成「幾個 Strategy 的集合」,可能會被誤導去做不必要的解耦,反而失去靈活度。

本章重點#

  • Template Method 用來把「流程一致、步驟實作不同」的情境抽象化
  • 抽象類別保留骨架,子類提供步驟實作
  • 是消除「複製貼上 + 微改」型重複的標準手法
  • 它強調步驟之間的捆綁——若步驟間是獨立的,請改用 Strategy