文章目录
- 概述
- 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. 总结
模板方法模式通过定义算法的骨架并将某些步骤的具体实现延迟到子类中,提供了一种灵活且可扩展的设计方式。它在代码复用、扩展性和灵活性方面具有显著优势,适用于需要固定流程但允许部分步骤自定义的场景。通过合理使用模板方法模式,可以构建出清晰、可维护且易于扩展的代码架构。