1. 模板方法模式的核心思想

第一次接触模板方法模式时,我正被一个重复代码问题困扰着。项目中需要处理多种文件导入流程,每种文件的校验规则不同,但解析、日志记录、异常处理的步骤却完全一致。当时写了大量重复代码,直到发现Spring框架中JdbcTemplate类的实现方式,才恍然大悟——这不就是模板方法模式的经典应用吗?

模板方法模式的核心在于定义算法骨架。举个生活中的例子,就像泡茶的固定流程:烧水→放茶叶→冲泡→倒出,其中只有"放茶叶"这个步骤会根据茶叶种类变化。在代码中体现为抽象类定义模板方法(泡茶流程),具体步骤(放什么茶叶)交给子类实现。

这种模式在Java生态中随处可见:

  • Spring的JdbcTemplate封装了获取连接、执行SQL、释放连接等固定流程
  • MyBatis的BaseExecutor处理了缓存查询、生成语句等通用逻辑
  • Servlet规范中的HttpServlet定义了service()方法骨架
// 典型模板方法结构示例
public abstract class FileImporter {
    // 模板方法(final防止子类修改流程)
    public final void importProcess() {
        validateHeader();
        parseData();  // 抽象方法
        saveLog();
        handleException();
    }
    
    private void validateHeader() {
        System.out.println("校验文件头信息");
    }
    
    protected abstract void parseData();
    
    private void saveLog() {
        System.out.println("记录导入日志");
    }
}

2. Spring框架中的实战案例

在Spring 5.3.18源码中,JdbcTemplate的query()方法完美展示了模板方法模式。我调试过整个执行流程:获取连接→创建语句→执行查询→处理结果集→释放资源,这些固定操作被封装在模板方法中,而RowMapper回调接口允许我们自定义结果集处理。

更精妙的是Spring事务管理的AbstractPlatformTransactionManager:

  1. 定义了标准事务流程:开启→执行业务→提交/回滚
  2. 将获取事务、提交等具体操作抽象为doBegin()、doCommit()等方法
  3. 不同数据源只需继承并实现这些抽象方法
// 模拟Spring事务模板
public abstract class AbstractTransaction {
    public final void execute() {
        try {
            doBegin();  // 抽象方法
            businessLogic();
            doCommit(); // 抽象方法
        } catch (Exception e) {
            doRollback(); // 抽象方法
        }
    }
    
    protected abstract void doBegin();
    protected abstract void doCommit();
    protected abstract void doRollback();
    
    private void businessLogic() {
        System.out.println("执行业务代码");
    }
}

实际项目中,我曾用这种模式统一处理Redis缓存操作。模板方法封装了缓存查询→命中判断→反序列化流程,子类只需实现具体的反序列化逻辑,代码复用率提升了70%。

3. 微服务架构中的典型应用

在电商微服务架构中,订单处理流程是个经典场景。不同业务线(普通订单、预售订单、拼团订单)的核心处理逻辑不同,但都需要经历:风控检查→库存锁定→支付触发→状态更新这些步骤。我们团队通过模板方法模式将公共流程收拢到OrderProcessTemplate基类,各业务线只需关注自己的特殊逻辑。

分布式任务调度系统也适用这个模式。比如定时任务的标准流程:

  1. 从数据库获取任务列表
  2. 获取分布式锁
  3. 执行具体任务(抽象方法)
  4. 释放锁并记录日志
public abstract class DistributedJob {
    public final void executeJob() {
        List<Job> jobs = fetchJobs();
        for (Job job : jobs) {
            if (acquireLock(job.getId())) {
                try {
                    processJob(job);  // 抽象方法
                } finally {
                    releaseLock(job.getId());
                }
            }
        }
    }
    
    protected abstract void processJob(Job job);
    
    private List<Job> fetchJobs() {
        // 查询待处理任务
    }
    
    private boolean acquireLock(String jobId) {
        // 获取Redis分布式锁
    }
}

实际开发中遇到过坑点:如果子类在实现抽象方法时抛出异常,可能导致锁未释放。后来我们在模板方法中添加了try-finally块,这就是模板方法的优势——集中维护公共逻辑。

4. 高级技巧与最佳实践

钩子方法(Hook Method)是模板方法的进阶玩法。在电商优惠券系统中,我们设计了这样的结构:

public abstract class CouponTemplate {
    public final void process() {
        validate();
        calculateDiscount();  // 抽象方法
        if (needRecordLog()) {  // 钩子方法
            saveLog();
        }
    }
    
    protected boolean needRecordLog() {
        return true;  // 默认记录日志
    }
    
    protected abstract void calculateDiscount();
}

这样设计带来两个好处:

  1. 普通优惠券自动记录日志
  2. 特殊优惠券通过重写needRecordLog()关闭日志

性能优化方面要注意:

  • 避免在模板方法中做耗时操作
  • 抽象方法尽量设计为无状态
  • 考虑使用ThreadLocal传递上下文

在DDD架构中,可以将模板方法与策略模式结合。比如支付处理:

  1. 模板方法定义支付流程:验证→执行→回调
  2. 策略模式实现不同支付方式(支付宝、微信)
  3. 子类通过组合支付策略实现具体逻辑

5. 源码级调试技巧

阅读框架源码时,我习惯用IDEA的调试功能追踪模板方法执行流程。以MyBatis的BaseExecutor为例:

  1. 在query()方法打条件断点
  2. 观察SqlSession如何调用模板方法
  3. 查看SimpleExecutor/ReuseExecutor等子类实现

调试Spring的JdbcTemplate时,重点关注:

  • 如何通过RowMapper回调实现结果处理
  • 资源清理如何在finally块中保证执行
  • 异常如何被统一转换为DataAccessException

通过源码学习,我总结出模板方法的三个黄金规则:

  1. 模板方法应该是final的
  2. 抽象方法数量要严格控制
  3. 子类不应该知道其他子类的存在

6. 复杂业务场景实战

在金融风控系统中,我们设计了多级审批模板:

public abstract class RiskControlTemplate {
    public final void process() {
        initContext();
        checkRuleA();
        checkRuleB();  // 抽象方法
        if (needExtraCheck()) {
            checkRuleC();
        }
        persistResult();
    }
    
    protected boolean needExtraCheck() {
        return false;
    }
    
    protected abstract void checkRuleB();
}

这种结构带来三个优势:

  1. 新人快速上手,只需关注具体规则实现
  2. 审批流程变更只需修改基类
  3. 通过钩子方法支持灵活扩展

在物联网设备管理中,我们将设备上报数据处理抽象为:

  1. 数据解码(抽象方法)
  2. 数据校验(模板方法)
  3. 持久化存储(模板方法)
  4. 事件触发(钩子方法控制)

7. 设计模式组合应用

模板方法常与其他模式联用:

  1. 配合工厂方法模式:在Spring的Bean初始化中,AbstractAutowireCapableBeanFactory定义了初始化流程,具体实例化通过createBeanInstance()工厂方法实现
  2. 配合策略模式:JVM的类加载机制中,ClassLoader定义了双亲委派模板,具体查找逻辑由findClass()策略实现
  3. 配合观察者模式:JavaFX的Application类定义了应用生命周期模板,通过init()、start()等抽象方法支持扩展

在微服务网关开发中,我们这样组合模式:

  1. 模板方法定义过滤器链流程
  2. 策略模式实现不同路由策略
  3. 装饰器模式增强请求处理
public abstract class ApiGatewayFilter {
    public final void handle(Request request) {
        auth(request);
        transform(request);  // 抽象方法
        if (shouldCache(request)) {
            cache(request);
        }
        dispatch(request);
    }
    
    protected abstract void transform(Request request);
    
    protected boolean shouldCache(Request request) {
        return false;
    }
}

这些实战经验让我深刻体会到:模板方法模式不是简单的代码复用工具,而是架构设计中的重要思想。它帮助我们在保持架构统一性的同时,又不失灵活性。

更多推荐