模式定義 (GoF)
定義一個操作中算法的骨架,將某些步驟延遲到子類中。模板方法使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。
結構角色
- AbstractClass:定義抽象基本操作 (供子類實現) + 模板方法 (組裝算法骨架)
- ConcreteClass:實現抽象基本操作,完成特定步驟
JUnit 經典案例:TestCase.runBare()
public abstract class TestCase {
// 模板方法:定義測試算法骨架
public void runBare() throws Throwable {
setUp(); // 1. 初始化
try {
runTest(); // 2. 執行測試 (抽象/鉤子)
} finally {
tearDown(); // 3. 清理 (保證執行)
}
}
// 鉤子方法:預設空實現,子類可選覆寫
protected void setUp() throws Exception {}
protected void tearDown() throws Exception {}
// 抽象方法:強制子類實現具體測試邏輯
protected abstract void runTest();
}
關鍵設計決策:為什麼用「鉤子」而非「抽象」?
| 方案 | setUp/tearDown 為抽象 | setUp/tearDown 為鉤子 (空實現) |
| 子類義務 | 必須實現,哪怕不需要 | 按需覆寫,無負擔 |
| 靈活性 | 低 | 高 |
| 適用場景 | 步驟強制性強 | 步驟可選、預設無操作 |
JUnit 選擇鉤子,因為測試未必需要初始化/清理資源。
適用場景檢核表
- 一次性實現算法不變部分,將可變行為委託子類
- 提取子類公共行為到父類,消除代碼重複 (良好編碼習慣)
- 控制子類擴展點:模板方法僅在特定點調用操作
- 不想暴露模板方法 →
private 或不在 API 中體現
- 不想被覆寫 →
final 修飾模板方法
模板方法 vs 策略模式