模板方法模式:定義算法骨架的設計模式
一、模式核心:模板方法定義算法骨架,具體步驟延遲到子類實現
在軟件開發中,經常會遇到這樣的情況:某個算法的步驟是固定的,但具體步驟的實現可能因不同情況而有所不同。例如,在電商系統中,訂單的處理流程通常包括創建訂單、支付、發貨、通知用戶等步驟,但不同類型的訂單(如普通訂單、秒殺訂單)在支付和發貨環節的實現可能不同。
模板方法模式(Template Method Pattern) 定義了一個算法的骨架,將算法中的具體步驟延遲到子類中實現。模板方法模式讓子類在不改變算法結構的前提下,重新定義算法中的某些具體步驟,核心解決:
- 代碼復用:將算法的公共步驟封裝在父類中,避免子類重復實現。
- 算法擴展:子類可以通過重寫父類的具體步驟來擴展算法的實現。
- 流程控制:父類控制算法的整體流程,子類負責具體步驟的實現,確保算法的步驟順序不變。
核心思想與 UML 類圖(PlantUML 語法)
模板方法模式包含抽象類(Abstract Class)和具體子類(Concrete Class)。抽象類中定義了模板方法(Template Method)和若干基本方法(Primitive Methods),模板方法定義了算法的骨架,基本方法包括具體方法和抽象方法,具體方法在抽象類中已經實現,抽象方法在子類中實現。
二、核心實現:電商訂單處理流程
1. 定義抽象訂單類(模板類)
public abstract class AbstractOrder {// 模板方法:訂單處理流程public final void processOrder() {createOrder(); // 創建訂單(具體方法,在抽象類中實現)pay(); // 支付(抽象方法,由子類實現)deliverGoods(); // 發貨(抽象方法,由子類實現)notifyUser(); // 通知用戶(具體方法,在抽象類中實現)}// 具體方法:創建訂單(公共步驟,無需子類重寫)protected void createOrder() {System.out.println("創建訂單");}// 抽象方法:支付(不同訂單類型實現不同)protected abstract void pay();// 抽象方法:發貨(不同訂單類型實現不同)protected abstract void deliverGoods();// 具體方法:通知用戶(公共步驟,無需子類重寫)protected void notifyUser() {System.out.println("通知用戶訂單處理完成");}
}
2. 實現具體訂單類(普通訂單)
public class NormalOrder extends AbstractOrder {@Overrideprotected void pay() {System.out.println("普通訂單使用支付寶支付");}@Overrideprotected void deliverGoods() {System.out.println("普通訂單使用普通快遞發貨");}
}
3. 實現具體訂單類(秒殺訂單)
public class FlashSaleOrder extends AbstractOrder {@Overrideprotected void pay() {System.out.println("秒殺訂單使用微信支付(優先扣款)");}@Overrideprotected void deliverGoods() {System.out.println("秒殺訂單使用順豐快遞加急發貨");}
}
4. 客戶端使用模板方法模式
public class ClientDemo {public static void main(String[] args) {// 處理普通訂單AbstractOrder normalOrder = new NormalOrder();System.out.println("處理普通訂單:");normalOrder.processOrder();System.out.println("\n處理秒殺訂單:");AbstractOrder flashSaleOrder = new FlashSaleOrder();flashSaleOrder.processOrder();}
}
輸出結果:
處理普通訂單:
創建訂單
普通訂單使用支付寶支付
普通訂單使用普通快遞發貨
通知用戶訂單處理完成處理秒殺訂單:
創建訂單
秒殺訂單使用微信支付(優先扣款)
秒殺訂單使用順豐快遞加急發貨
通知用戶訂單處理完成
三、進階:鉤子方法(Hook Method)增強模板靈活性
在模板方法模式中,可以通過 鉤子方法 來增加算法的靈活性。鉤子方法是一個在抽象類中默認實現的方法,子類可以根據需要重寫該方法,以控制算法的流程。
1. 添加鉤子方法(是否需要短信通知)
public abstract class AbstractOrder {// ... 其他方法不變 ...// 鉤子方法:是否需要通知用戶(默認需要)protected boolean needNotifyUser() {return true;}// 模板方法中調用鉤子方法public final void processOrder() {createOrder();pay();deliverGoods();if (needNotifyUser()) { // 根據鉤子方法結果決定是否通知用戶notifyUser();}}
}
2. 子類重寫鉤子方法(秒殺訂單不需要通知用戶)
public class FlashSaleOrder extends AbstractOrder {// ... 其他方法不變 ...@Overrideprotected boolean needNotifyUser() {return false; // 秒殺訂單不通知用戶}
}
3. 客戶端測試鉤子方法效果
public class ClientDemo {public static void main(String[] args) {// ... 處理普通訂單 ...System.out.println("\n處理秒殺訂單(不通知用戶):");AbstractOrder flashSaleOrder = new FlashSaleOrder();flashSaleOrder.processOrder();}
}
輸出結果:
處理秒殺訂單(不通知用戶):
創建訂單
秒殺訂單使用微信支付(優先扣款)
秒殺訂單使用順豐快遞加急發貨
四、框架與源碼中的模板方法實踐
1. Java 的 AbstractList 類
Java 集合框架中的 AbstractList
類是模板方法模式的典型應用。AbstractList
定義了列表的基本操作流程,如 add
、get
等方法,具體的實現由子類(如 ArrayList
、LinkedList
)完成。
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {// 模板方法:獲取元素public E get(int index) {throw new AbstractMethodError(); // 抽象方法,由子類實現}// 具體方法:添加元素(基于 get 和 set 實現)public boolean add(E e) {add(size(), e); // 調用子類實現的 add(int, E) 方法return true;}
}
2. Spring 的 JdbcTemplate
Spring 框架中的 JdbcTemplate
使用模板方法模式封裝了 JDBC 的操作流程。JdbcTemplate
定義了執行 SQL 的模板方法(如 queryForObject
),具體的結果映射由回調接口(如 RowMapper
)實現。
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {// 模板方法:查詢單個對象public <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args) {return execute(sql, new PreparedStatementCallback<T>() {@Overridepublic T doInPreparedStatement(PreparedStatement ps) throws SQLException {ps.execute();ResultSet rs = ps.getResultSet();return rowMapper.mapRow(rs, 1); // 回調接口實現結果映射}}, args);}
}
五、避坑指南:正確使用模板方法模式的 3 個要點
1. 合理設計模板方法的訪問權限
模板方法通常定義為 final
方法,防止子類重寫,確保算法骨架的穩定性。如果需要子類重寫模板方法,可以將其定義為 protected
方法,但需謹慎使用,避免破壞算法結構。
2. 控制抽象類中的抽象方法數量
抽象類中的抽象方法應盡可能少,只包含那些必須由子類實現的步驟。如果抽象方法過多,會導致子類的實現復雜度增加,違背模板方法模式的初衷。
3. 避免在模板方法中調用子類的方法
在模板方法中應優先調用抽象類中的方法,避免直接調用子類的方法,否則可能導致循環依賴或子類未初始化的問題。如果需要調用子類的方法,可以通過抽象方法或鉤子方法實現。
六、總結:何時該用模板方法模式?
適用場景 | 核心特征 | 典型案例 |
---|---|---|
算法步驟固定 | 算法的步驟順序是固定的,但具體步驟實現可變 | 訂單處理流程、考試流程 |
代碼復用 | 多個子類有共同的算法骨架和部分公共代碼 | 日志記錄器、文件處理器 |
流程控制 | 需要確保算法步驟的執行順序不被篡改 | 工作流引擎、游戲關卡流程 |
模板方法模式通過將算法骨架與具體實現分離,實現了代碼的復用和算法的擴展,是一種非常實用的設計模式。下一篇我們將深入探討迭代器模式,解析如何統一遍歷不同數據結構的方式,敬請期待!
擴展思考:模板方法模式 vs 策略模式
類型 | 核心思想 | 適用場景 |
---|---|---|
模板方法模式 | 定義算法骨架,具體步驟由子類實現 | 算法步驟固定,部分步驟需變化 |
策略模式 | 定義一系列算法,可動態切換算法實現 | 算法可動態選擇,客戶端主動切換 |
理解這種差異,能幫助我們在不同場景下選擇更合適的設計模式。