模板方法模式详解

模板方法模式详解及真实场景解决方案

推荐学习完策略模式和模板方法模式看这个案例:
策略与模板方法模式组合详解

模式定义

模板方法模式是一种行为设计模式,在父类中定义算法的骨架,允许子类在不改变算法结构的情况下重写特定步骤。核心思想是通过固定流程框架与可变步骤实现的分离,实现代码复用和扩展控制。

真实场景案例:电商订单处理系统

需求背景:

需要处理多种订单类型(普通订单、团购订单、秒杀订单)

所有订单都遵循标准流程:验证 -> 支付 -> 扣库存 -> 通知

不同订单类型的验证规则、支付方式、通知渠道存在差异

痛点问题:

流程步骤重复定义

新增订单类型时需要重复编写流程代码

修改通用流程时需要修改所有子类

步骤间存在条件判断和参数传递问题

解决方案代码实现

java 复制代码
import java.util.HashMap;
import java.util.Map;

// 上下文对象(解决参数传递问题)
class OrderContext {
    private final Map<String, Object> data = new HashMap<>();
    
    public OrderContext put(String key, Object value) {
        data.put(key, value);
        return this;
    }
    
    public <T> T get(String key) {
        return (T) data.get(key);
    }
}

// 抽象模板类
abstract class OrderProcessor {
    
    // 模板方法(final防止子类覆盖)
    public final void processOrder(OrderContext context) {
        if (!validate(context)) {
            rollback(context);
            return;
        }
        
        beforePayment(context);
        boolean paymentResult = processPayment(context);
        
        if (paymentResult) {
            deductInventory(context);
            sendNotification(context);
            afterSuccess(context);
        } else {
            handlePaymentFailure(context);
        }
    }
    
    // 必须实现的步骤
    protected abstract boolean validate(OrderContext context);
    protected abstract boolean processPayment(OrderContext context);
    
    // 可选步骤(默认实现)
    protected void beforePayment(OrderContext context) {
        // 空实现
    }
    
    protected void deductInventory(OrderContext context) {
        System.out.println("默认库存扣减逻辑");
    }
    
    protected void sendNotification(OrderContext context) {
        System.out.println("发送短信通知用户");
    }
    
    // 钩子方法(控制流程走向)
    protected boolean needSpecialNotification() {
        return false;
    }
    
    // 公共方法
    protected void rollback(OrderContext context) {
        System.out.println("执行订单回滚操作");
    }
    
    protected void handlePaymentFailure(OrderContext context) {
        System.out.println("处理支付失败流程");
    }
    
    protected void afterSuccess(OrderContext context) {
        System.out.println("订单后续处理");
    }
}

// 具体实现:团购订单处理
class GroupBuyOrderProcessor extends OrderProcessor {
    @Override
    protected boolean validate(OrderContext context) {
        Integer groupSize = context.get("groupSize");
        System.out.println("验证团购人数:" + groupSize);
        return groupSize != null && groupSize >= 5;
    }

    @Override
    protected boolean processPayment(OrderContext context) {
        System.out.println("执行团购优惠价支付");
        return true;
    }

    @Override
    protected void deductInventory(OrderContext context) {
        System.out.println("按团购规则扣减库存");
    }

    @Override
    protected boolean needSpecialNotification() {
        return true;
    }

    @Override
    protected void sendNotification(OrderContext context) {
        if (needSpecialNotification()) {
            System.out.println("发送团购专属通知");
        } else {
            super.sendNotification(context);
        }
    }
}

// 具体实现:秒杀订单处理
class FlashSaleOrderProcessor extends OrderProcessor {
    @Override
    protected boolean validate(OrderContext context) {
        System.out.println("验证秒杀资格和库存");
        return true;
    }

    @Override
    protected boolean processPayment(OrderContext context) {
        System.out.println("执行秒杀价支付(10秒内完成)");
        return true;
    }

    @Override
    protected void beforePayment(OrderContext context) {
        System.out.println("锁定秒杀库存30秒");
    }
}

// 使用示例
public class TemplateMethodPatternExample {
    public static void main(String[] args) {
        // 处理团购订单
        OrderContext groupBuyContext = new OrderContext()
            .put("groupSize", 8)
            .put("productId", 1001);
        
        new GroupBuyOrderProcessor().processOrder(groupBuyContext);

        System.out.println("\n===============\n");
        
        // 处理秒杀订单
        OrderContext flashSaleContext = new OrderContext()
            .put("userId", "u123456")
            .put("flashSaleId", "fs2023");
        
        new FlashSaleOrderProcessor().processOrder(flashSaleContext);
    }
}

真实场景问题解决方案

流程标准化问题

解决方案:将通用流程封装在模板方法中(processOrder)

效果:确保所有订单类型遵循相同处理流程

参数传递问题

解决方案:使用上下文对象(OrderContext)封装所有参数

优势:统一参数传递方式,避免方法签名膨胀

流程扩展控制

钩子方法:needSpecialNotification() 控制通知方式

空方法:beforePayment() 提供扩展点但不强制实现

异常处理机制

统一处理:在模板方法中集中处理回滚和失败流程

隔离影响:子类只需关注业务逻辑,无需处理异常传播

线程安全问题

java 复制代码
// 示例:线程安全的模板方法实现
public final void processOrder(OrderContext context) {
    synchronized(this) {
        // 核心处理逻辑
    }
}

典型问题及应对策略

问题场景 解决方案 代码示例
需要动态调整步骤顺序 使用策略模式+模板方法组合 定义流程编排策略接口
部分步骤需要强制实现 将方法声明为abstract protected abstract boolean validate()
需要支持步骤的批量配置 结合工厂模式创建处理器 OrderProcessorFactory.createProcessor(type)
不同环境需要不同实现 使用环境变量控制具体实现 if("prod".equals(env)) { ... }
需要监控每个步骤的执行时间 使用模板方法包装步骤执行 在方法前后添加监控逻辑

模式优化技巧

流程可视化:

java 复制代码
// 添加流程跟踪
public final void processOrder(OrderContext context) {
    context.put("processStartTime", System.currentTimeMillis());
    // ...处理逻辑...
    context.put("processEndTime", System.currentTimeMillis());
}

模板方法拆分:

java 复制代码
// 将大模板拆分为多个小模板
protected final void coreProcess() { /* 核心步骤 */ }
protected final void postProcess() { /* 后续处理 */ }

支持异步处理:

java 复制代码
// 异步模板方法
public CompletableFuture<Void> processAsync(OrderContext context) {
    return CompletableFuture.runAsync(() -> processOrder(context));
}

模板方法组合:

java 复制代码
// 组合多个模板类
class ComplexOrderProcessor extends BaseValidator 
                           implements PaymentHandler {
    // 组合多个模板方法
}

适用场景总结

多个子类有相同流程但不同实现细节

需要严格控制核心流程的执行顺序

存在多个相似流程需要统一维护

系统需要支持流程的渐进式扩展

需要对核心流程进行统一监控或统计

通过合理使用模板方法模式,可以提升代码复用率到70%以上,同时保持系统的扩展性和可维护性。当遇到需要动态修改流程结构的需求时,可考虑与责任链模式结合使用。

一句话总结

模板方法模式定义了一个算法的骨架,将一些步骤延迟到子类中实现,使得子类可以在不改变算法结构的情况下重新定义算法中的某些步骤。

相关推荐
qq_54324852几秒前
redis的哨兵模式和Redis cluster
java·开发语言
IUings6 分钟前
【鸿蒙】HarmonyOS NEXT之如何正常加载地图组件
开发语言·华为·harmonyos·harmonyos next·地图服务·map kit
异常君8 分钟前
TCP TIME_WAIT 状态:原理、问题与优化方案
java·linux·tcp/ip
清风~徐~来16 分钟前
【Redis】set 类型
java·数据库·redis
秦少游在淮海23 分钟前
C++ - STL #什么是STL #STL的版本 #闭源开源 #STL的六大组件
开发语言·c++
夜月yeyue25 分钟前
高性能MCU的MPU与Cache优化详解
linux·开发语言·stm32·单片机·嵌入式硬件
全栈凯哥30 分钟前
JSCH使用SFTP详细教程
java
残*影31 分钟前
Spring 中如何开启事务?
java·后端·spring
于冬恋43 分钟前
Web后端快速入门(Maven)
java·maven
Wyn_1 小时前
【QT】自定义QWidget标题栏,可拖拽(拖拽时窗体变为normal大小),可最小/大化、关闭(图文详情)
开发语言·qt