文章目錄
- 概述
- 1. 核心思想
- 2. 結構
- 3. 示例代碼
- 4. 優點
- 5. 缺點
- 6. 適用場景
- 7. 案例:模板方法模式在數據處理中的應用
- 案例背景
- UML
- 搭建抽象基類 - 數據處理的 “總指揮”
- 子類定制 - 適配不同供應商
- 供應商 A 的數據處理器
- 供應商 B 的數據處理器
- 在業務代碼中整合運用
- 8. 總結
概述
模板方法模式(Template Method Pattern)是一種行為設計模式,其核心思想是:定義一個操作的算法骨架,將一些步驟的實現延遲到子類中。在軟件設計中,模板方法模式通常用于處理一系列相似的操作,這些操作可以被抽象為固定的流程,而流程中的某些步驟則由子類來具體實現。
1. 核心思想
模板方法模式的核心思想可以總結為以下幾點:
- 固定流程:定義一個固定的算法流程,確保所有子類都遵循相同的步驟。
- 可變步驟:將某些步驟的具體實現延遲到子類中,允許子類根據需求定制。
- 代碼復用:通過將通用邏輯放在父類中,減少代碼重復。
2. 結構
模板方法模式通常由以下幾個部分組成:
- 抽象類(Abstract Class):定義算法的骨架,并包含一些抽象方法供子類實現。
- 具體子類(Concrete Class):實現抽象類中的抽象方法,完成具體的業務邏輯。
- 模板方法(Template Method):在抽象類中定義的一個方法,它調用了算法中的各個步驟。
3. 示例代碼
一個簡單的模板方法模式示例,展示如何定義一個算法骨架并在子類中實現具體步驟。
// 抽象類
abstract class AbstractClass {// 模板方法,定義了算法的骨架public final void templateMethod() {step1();step2();step3();}// 具體方法,子類可以直接使用private void step1() {System.out.println("執行步驟1");}// 抽象方法,子類需要實現protected abstract void step2();// 鉤子方法,子類可以選擇性重寫protected void step3() {System.out.println("執行步驟3");}
}// 具體子類A
class ConcreteClassA extends AbstractClass {@Overrideprotected void step2() {System.out.println("ConcreteClassA 執行步驟2");}
}// 具體子類B
class ConcreteClassB extends AbstractClass {@Overrideprotected void step2() {System.out.println("ConcreteClassB 執行步驟2");}@Overrideprotected void step3() {System.out.println("ConcreteClassB 執行步驟3");}
}// 客戶端代碼
public class TemplateMethodPatternDemo {public static void main(String[] args) {AbstractClass classA = new ConcreteClassA();classA.templateMethod();AbstractClass classB = new ConcreteClassB();classB.templateMethod();}
}
為了防止惡意修改,模板方法通常使用final關鍵字修飾,避免被子類重寫。
4. 優點
- 代碼復用:通過將通用邏輯放在父類中,減少了代碼重復。
- 擴展性:子類可以通過實現抽象方法來擴展算法的某些步驟,而無需修改算法的整體結構。
- 靈活性:鉤子方法(Hook Method)允許子類選擇性地覆蓋某些步驟,增加了靈活性。
5. 缺點
- 復雜性:如果算法的步驟過多,可能會導致類的層次結構變得復雜。
- 限制性:模板方法模式要求算法的步驟是固定的,這可能會限制某些場景下的靈活性。
6. 適用場景
- 固定流程:當某個算法的流程是固定的,但某些步驟的具體實現可能不同時,可以使用模板方法模式。
- 代碼復用:當多個子類有共同的邏輯時,可以將這些邏輯提取到父類中,減少代碼重復。
- 框架設計:在框架設計中,模板方法模式常用于定義框架的核心流程,而將具體實現留給用戶自定義。
7. 案例:模板方法模式在數據處理中的應用
案例背景
我們的平臺需要處理來自以下兩個供應商的商品數據:
- 供應商 A:提供 JSON 格式的商品數據,需要對商品名稱進行標準化處理。
- 供應商 B:提供 XML 格式的商品數據,需要對商品價格進行匯率轉換。
盡管數據格式和處理邏輯不同,但整體的處理流程是相同的,包括以下步驟:
- 數據驗證:驗證數據的完整性和合法性。
- 數據解析:將原始數據解析為內部數據結構。
- 數據處理:根據業務需求對數據進行處理。
- 數據存儲:將處理后的數據存儲到數據庫中。
UML
搭建抽象基類 - 數據處理的 “總指揮”
首先,我們創建一個抽象的基類 AbstractProductDataHandler
,它將定義整個數據處理流程的骨架。
public abstract class AbstractProductDataHandler {// 模板方法,定義了數據處理的固定流程public final void handleProductData(String rawData) {if (validateData(rawData)) {logStart(rawData);Product product = parseData(rawData);processProduct(product);saveProduct(product);logCompletion(product);} else {handleValidationFailure(rawData);}}// 抽象方法,子類需要實現數據驗證邏輯protected abstract boolean validateData(String rawData);// 抽象方法,子類需要實現數據解析邏輯protected abstract Product parseData(String rawData);// 抽象方法,子類需要實現數據處理邏輯protected abstract void processProduct(Product product);// 具體方法,通用的數據存儲邏輯private void saveProduct(Product product) {System.out.println("保存商品數據:" + product);// 實際項目中,這里會調用數據庫操作}// 具體方法,記錄處理開始日志private void logStart(String rawData) {System.out.println("開始處理商品數據,來源:" + getSource());}// 具體方法,記錄處理完成日志private void logCompletion(Product product) {System.out.println("商品數據處理完成,結果:" + product);}// 具體方法,處理驗證失敗的情況protected void handleValidationFailure(String rawData) {System.out.println("數據驗證失敗:" + rawData);}// 抽象方法,子類需要實現以返回數據來源protected abstract String getSource();
}
子類定制 - 適配不同供應商
接下來,我們為每個供應商創建具體的子類,實現抽象基類中的抽象方法。
供應商 A 的數據處理器
@Component
public class SupplierADataHandler extends AbstractProductDataHandler {@Overrideprotected boolean validateData(String rawData) {// 簡單的 JSON 格式驗證return rawData != null && rawData.startsWith("{") && rawData.endsWith("}");}@Overrideprotected Product parseData(String rawData) {// 解析 JSON 數據// 實際項目中,可以使用 Jackson 或 Gson 等庫Product product = new Product();product.setName("SupplierA Product");product.setPrice(100.0);return product;}@Overrideprotected void processProduct(Product product) {// 對商品名稱進行標準化處理product.setName(product.getName().toUpperCase());}@Overrideprotected String getSource() {return "Supplier A";}
}
供應商 B 的數據處理器
@Component
public class SupplierBDataHandler extends AbstractProductDataHandler {@Overrideprotected boolean validateData(String rawData) {// 簡單的 XML 格式驗證return rawData != null && rawData.startsWith("<") && rawData.endsWith(">");}@Overrideprotected Product parseData(String rawData) {// 解析 XML 數據// 實際項目中,可以使用 JAXB 或 DOM 解析器Product product = new Product();product.setName("SupplierB Product");product.setPrice(200.0);return product;}@Overrideprotected void processProduct(Product product) {// 對商品價格進行匯率轉換product.setPrice(product.getPrice() * 0.85); // 假設匯率為 0.85}@Overrideprotected String getSource() {return "Supplier B";}
}
在業務代碼中整合運用
最后,我們在業務代碼中使用這些數據處理器。通過 Spring 的依賴注入機制,我們可以輕松地切換不同的處理器。
@Service
public class ProductDataProcessingService {@Autowiredprivate SupplierADataHandler supplierAHandler;@Autowiredprivate SupplierBDataHandler supplierBHandler;public void processDataFromSupplierA(String rawData) {supplierAHandler.handleProductData(rawData);}public void processDataFromSupplierB(String rawData) {supplierBHandler.handleProductData(rawData);}
}
8. 總結
模板方法模式通過定義算法的骨架并將某些步驟的具體實現延遲到子類中,提供了一種靈活且可擴展的設計方式。它在代碼復用、擴展性和靈活性方面具有顯著優勢,適用于需要固定流程但允許部分步驟自定義的場景。通過合理使用模板方法模式,可以構建出清晰、可維護且易于擴展的代碼架構。