摘要
模版方法設計模式是一種行為型設計模式,定義了算法的步驟順序和整體結構,將某些步驟的具體實現延遲到子類中。它通過抽象類定義模板方法,子類實現抽象步驟,實現代碼復用和算法流程控制。該模式適用于有固定流程但部分步驟可變的場景,如業務流程控制等。
1. 模版設計模式定義
定義一個操作中的算法骨架(即步驟的順序和整體結構),而將某些步驟的具體實現延遲到子類中。子類可以在不改變算法結構的前提下,重新定義算法中的某些具體步驟。
1.1.1. 模版設計模式的關鍵點
- 抽象模版類:定義一個模板方法,描述算法的整體流程。模板方法一般是
final
,防止子類改變算法結構。 - 基本方法(基本操作):模板方法所依賴的步驟,這些步驟可以是抽象的,也可以有默認實現。
- 具體子類:實現抽象類中的抽象步驟,完成具體的業務邏輯。
1.1.2. 作用
- 復用代碼:把不變的行為放在父類,變化的行為由子類實現,避免代碼重復。
- 控制算法流程:子類只需關注具體步驟實現,算法整體流程由父類控制,增強代碼的可維護性和可擴展性。
2. 模版設計模式結構
- 抽象類 (Abstract-Class) 會聲明作為算法步驟的方法, 以及依次調用它們的實際模板方法。 算法步驟可以被聲明為
抽象
類型, 也可以提供一些默認實現。 - 具體類 (Concrete-Class) 可以重寫所有步驟, 但不能重寫模板方法自身。
2.1. 模版設計模式類圖
2.2. 模版設計模式時序圖
3. 模版設計模式實現方式
3.1. 模版設計模式的實現方式核心在于:
- 在抽象父類中定義一個模板方法(通常是
final
的),它規定了算法的執行順序和骨架。 - 模板方法調用若干個基本方法(步驟),其中部分基本方法是抽象的,由子類實現;部分基本方法可以有默認實現。
- 子類繼承抽象父類,實現抽象步驟,完成具體業務邏輯。
3.2. 模板設計模式實現步驟
- 創建抽象類(AbstractClass)
-
- 定義模板方法
templateMethod()
,并用final
修飾,防止子類重寫改變流程。 - 模板方法中按照固定步驟順序調用基本操作。
- 定義基本操作(抽象方法或具體方法),其中抽象方法由子類實現。
- 定義模板方法
- 創建具體子類(ConcreteClass)
-
- 繼承抽象類,實現抽象的基本方法,完成具體業務。
3.3. 示例代碼(Java)
// 抽象模板類
public abstract class AbstractTemplate {// 模板方法,定義固定流程,防止子類覆蓋public final void templateMethod() {step1();step2();step3();}// 抽象基本操作,由子類實現protected abstract void step1();protected abstract void step2();// 具體基本操作,父類實現,子類可選擇復寫protected void step3() {System.out.println("默認實現步驟3");}
}// 具體子類A
public class ConcreteTemplateA extends AbstractTemplate {@Overrideprotected void step1() {System.out.println("ConcreteTemplateA 實現步驟1");}@Overrideprotected void step2() {System.out.println("ConcreteTemplateA 實現步驟2");}
}// 具體子類B
public class ConcreteTemplateB extends AbstractTemplate {@Overrideprotected void step1() {System.out.println("ConcreteTemplateB 實現步驟1");}@Overrideprotected void step2() {System.out.println("ConcreteTemplateB 實現步驟2");}// 可以覆蓋父類默認實現@Overrideprotected void step3() {System.out.println("ConcreteTemplateB 重寫步驟3");}
}
3.4. 模版模式示例
public class Client {public static void main(String[] args) {AbstractTemplate templateA = new ConcreteTemplateA();templateA.templateMethod();// 輸出:// ConcreteTemplateA 實現步驟1// ConcreteTemplateA 實現步驟2// 默認實現步驟3AbstractTemplate templateB = new ConcreteTemplateB();templateB.templateMethod();// 輸出:// ConcreteTemplateB 實現步驟1// ConcreteTemplateB 實現步驟2// ConcreteTemplateB 重寫步驟3}
}
說明
- 模板方法
templateMethod()
固定了整體流程,子類不能改變流程,只能重寫步驟細節。 - 這樣保證了算法骨架不變,細節可變。
4. 模版設計模式適合場景
4.1. ? 適合使用模版設計模式的場景
場景 | 說明 |
多個子類有相同算法骨架 | 多個子類共享固定流程,只有具體步驟實現不同,便于代碼復用和規范流程。 |
需要復用公共流程代碼 | 將不變的算法結構封裝在父類,避免重復代碼,提升維護性。 |
需要統一控制算法執行順序 | 模板方法定義執行順序,防止子類隨意改變流程,保證算法正確執行。 |
算法結構清晰、變化點集中 | 業務流程穩定,只有個別步驟需要子類實現,方便集中管理和擴展。 |
希望固定流程,允許步驟擴展 | 允許子類通過實現抽象步驟或覆蓋鉤子方法靈活擴展功能,而不破壞整體流程。 |
4.2. ? 不適合使用模版設計模式的場景
場景 | 原因 |
需要動態調整或拼裝流程 | 模板方法流程固定,難以支持運行時動態改變步驟或流程組合。 |
繼承層次過深,代碼復雜 | 模板方法依賴繼承,過多層次會導致系統復雜且難維護。 |
業務變化點不明顯或過少 | 過度抽象導致代碼冗余,簡單業務用模版模式反而增加復雜度。 |
多維度變化且復雜 | 多個變化點分布在算法不同部分,模板方法難以靈活應對,策略模式或責任鏈模式更合適。 |
需要高度靈活、組合式的行為 | 模板方法結構靜態,不適合高動態組合或插件式設計。 |
5. 模版設計模式實戰示例
5.1. 場景描述
在金融風控中,不同風控策略的執行流程大致相同:
- 數據準備
- 規則校驗
- 風控決策(通過/拒絕)
- 結果記錄
不同風控策略的規則校驗細節不同,適合用模板設計模式抽象固定流程,把校驗邏輯由子類實現。
5.2. 項目結構示例(Spring Boot)
com.example.riskcontrol
├── RiskControlTemplate.java // 抽象模板類
├── UserRiskControl.java // 具體風控策略1
├── TransactionRiskControl.java // 具體風控策略2
├── RiskControlService.java // 調用客戶端
└── SpringBootApplication.java // 啟動類
5.3. 抽象模板類 RiskControlTemplate
package com.example.riskcontrol;public abstract class RiskControlTemplate {// 模板方法,定義風控流程public final void executeRiskControl(String userId) {prepareData(userId);boolean passed = validateRules(userId);makeDecision(passed);recordResult(userId, passed);}// 準備數據,具體實現可重寫,默認空實現protected void prepareData(String userId) {System.out.println("準備風控數據,用戶ID:" + userId);}// 抽象規則校驗步驟,由具體策略實現protected abstract boolean validateRules(String userId);// 風控決策步驟,固定流程private void makeDecision(boolean passed) {if (passed) {System.out.println("風控通過,繼續后續流程");} else {System.out.println("風控拒絕,終止流程");}}// 記錄風控結果,默認實現protected void recordResult(String userId, boolean passed) {System.out.println("記錄風控結果,用戶ID:" + userId + ", 結果:" + (passed ? "通過" : "拒絕"));}
}
5.4. 具體策略實現類
package com.example.riskcontrol;import org.springframework.stereotype.Component;@Component("userRiskControl")
public class UserRiskControl extends RiskControlTemplate {@Overrideprotected boolean validateRules(String userId) {System.out.println("執行用戶維度的風控規則校驗,用戶ID:" + userId);// 簡單示例,實際接入數據庫或外部接口判斷return userId.hashCode() % 2 == 0; // 偶數通過,奇數拒絕}
}
package com.example.riskcontrol;import org.springframework.stereotype.Component;@Component("transactionRiskControl")
public class TransactionRiskControl extends RiskControlTemplate {@Overrideprotected boolean validateRules(String userId) {System.out.println("執行交易維度的風控規則校驗,用戶ID:" + userId);// 這里模擬判斷交易風險return userId.length() > 5; // 用戶ID長度大于5通過}
}
5.5. 業務調用層 RiskControlService
package com.example.riskcontrol;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.Map;@Service
public class RiskControlService {// 用Spring注解注入所有實現的模板,key為bean名字private final Map<String, RiskControlTemplate> riskControlMap;@Autowiredpublic RiskControlService(Map<String, RiskControlTemplate> riskControlMap) {this.riskControlMap = riskControlMap;}// 執行指定策略public void executeRiskControl(String strategyName, String userId) {RiskControlTemplate strategy = riskControlMap.get(strategyName);if (strategy == null) {throw new IllegalArgumentException("未找到對應風控策略:" + strategyName);}strategy.executeRiskControl(userId);}
}
5.6. Spring Boot 啟動類
package com.example.riskcontrol;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class SpringBootRiskControlApplication implements CommandLineRunner {@Autowiredprivate RiskControlService riskControlService;public static void main(String[] args) {SpringApplication.run(SpringBootRiskControlApplication.class, args);}@Overridepublic void run(String... args) throws Exception {System.out.println("模擬執行用戶風控策略:");riskControlService.executeRiskControl("userRiskControl", "user12345");System.out.println("\n模擬執行交易風控策略:");riskControlService.executeRiskControl("transactionRiskControl", "user12345");}
}
5.7. 運行結果示例
模擬執行用戶風控策略:
準備風控數據,用戶ID:user12345
執行用戶維度的風控規則校驗,用戶ID:user12345
風控拒絕,終止流程
記錄風控結果,用戶ID:user12345, 結果:拒絕模擬執行交易風控策略:
準備風控數據,用戶ID:user12345
執行交易維度的風控規則校驗,用戶ID:user12345
風控通過,繼續后續流程
記錄風控結果,用戶ID:user12345, 結果:通過
5.8. 模版模式總結
- 抽象父類
RiskControlTemplate
封裝公共流程(模板方法)。 - 具體策略類只需實現風控規則校驗步驟。
- 通過 Spring 的
@Component
注解和自動裝配Map<String, RiskControlTemplate>
,方便策略的靈活管理和調用。
6. 模版設計模式思考
6.1. 模版設計模式是不是用于父子類?
是的,模板設計模式(Template Method Pattern)確實是基于父子類繼承關系實現的設計模式。
6.1.1. 關鍵點總結:
- 父類(抽象類):定義一個模板方法,規定算法的整體流程和執行順序。模板方法通常是
final
,防止子類改變流程。 - 子類(具體類):繼承父類,實現父類中定義的抽象步驟,完成具體業務邏輯。
換句話說,模板模式就是把不變的流程寫在父類里,把可變的步驟留給子類實現。
6.1.2. 為什么是父子類?
- 模板方法模式的核心就是“復用公共代碼,且允許子類重寫部分行為”,這是繼承的典型應用場景。
- 父類定義了算法框架,子類只實現細節,滿足“開閉原則”(對擴展開放,對修改關閉)。
6.1.3. 舉個簡單類比:
- 父類像“烘焙蛋糕的流程”
- 子類像“不同口味蛋糕的具體做法”(巧克力、草莓等)
父類確定做蛋糕的步驟(比如準備材料、攪拌、烘焙、裝飾),子類決定每步的具體實現。
模板設計模式在實戰開發中常和以下設計模式配合使用,發揮協同優勢:
6.2. 模版設計模式常和哪些模式用于實戰開發中?
設計模式 | 結合方式及應用場景 |
策略模式 | 模板模式定義算法骨架,策略模式封裝可替換的具體行為,實現靈活的步驟替換。比如模板方法中調用策略接口完成某步驟。 |
工廠方法模式 | 用工廠方法創建模板方法中需要的具體實現對象,解耦模板和具體子類的實例化。 |
鉤子方法(Hook Method) | 模板方法模式中提供可選的“鉤子”方法,允許子類決定是否覆蓋,靈活控制流程細節。 |
裝飾器模式 | 在模板方法執行前后動態增強功能,如日志、權限校驗等,避免修改模板代碼。 |
責任鏈模式 | 將模板方法中的步驟拆分成責任鏈上的多個處理對象,形成更靈活的處理流程。 |
命令模式 | 模板方法中調用命令對象完成某些具體操作,命令模式封裝請求,增強擴展性。 |
觀察者模式 | 模板方法執行過程中發生重要事件時通知觀察者,實現業務解耦。 |
簡單示例場景
- 金融風控:模板定義風控流程,策略模式封裝不同風控規則。
- Web請求處理:模板方法定義請求處理流程,工廠方法創建具體處理器。
- 消息發送:模板定義消息發送步驟,裝飾器動態添加日志或限流。
博文參考
- 模板方法設計模式
- 設計模式之模板方法模式 | DESIGN