模板方法模式定義算法骨架,將具體步驟延遲到子類實現。適用於「流程固定、步驟可變」場景。本文以「炒菜」為例,演示抽象模板、具體實現、鉤子方法控制可選步驟(如涼拌無需生火)。
public abstract class AbstractCookTemplate {
// 模板方法:final 禁止覆寫,鎖定流程
public final void cook() {
System.out.println("--------開始做飯-----------");
fire(); // 1. 生火
oil(); // 2. 倒油
cookFood(); // 3. 炒菜
seasoning(); // 4. 加佐料
plating(); // 5. 裝盤
System.out.println("--------結束做飯-----------");
}
// 基礎步驟:protected 允許子類覆寫
protected void fire() { System.out.println("生火"); }
protected void oil() { System.out.println("加入油"); }
protected abstract void cookFood(); // 強制子類實現
protected void seasoning() { System.out.println("加入佐料"); }
protected void plating() { System.out.println("裝盤"); }
}
public class CookChicken extends AbstractCookTemplate {
@Override protected void cookFood() { System.out.println("炒雞肉"); }
}
public class CookFish extends AbstractCookTemplate {
@Override protected void cookFood() { System.out.println("炒魚肉"); }
@Override protected void plating() { System.out.println("魚要裝入大盤"); } // 覆寫裝盤
}
public class Test {
public static void main(String[] args) {
new CookChicken().cook();
new CookFish().cook();
}
}
輸出:雞肉按標準流程,魚肉裝盤步驟被覆寫。
場景:涼拌黃瓜**不需要生火**。在抽象類加入鉤子方法:
public abstract class AbstractCookTemplate {
public final void cook() {
System.out.println("--------開始做飯-----------");
if (!isColdDish()) { // 鉤子方法判斷
fire();
}
oil();
cookFood();
seasoning();
plating();
System.out.println("--------結束做飯-----------");
}
// 鉤子方法:預設返回 false(需要生火),子類可覆寫
protected boolean isColdDish() { return false; }
// ... 其他步驟同前
}
public class CookCucumber extends AbstractCookTemplate {
@Override protected boolean isColdDish() { return true; } // 跳過生火
@Override protected void cookFood() { System.out.println("涼拌黃瓜"); }
}
--------開始做飯----------- 加入油 涼拌黃瓜 加入佐料 裝盤 --------結束做飯-----------
「生火」步驟被跳過,流程靈活適配。
| 優點 | 缺點 |
|---|---|
| 封裝不變流程,擴展變化步驟,符合開閉原則 | 子類過多導致類爆炸(每個變化組合一個類) |
| 核心邏輯集中,避免重複代碼 | 反向控制結構:父類調用子類,理解成本稍高 |
| 鉤子方法提供極佳擴展點 | 流程過於僵化時難以調整(需修改抽象類) |