Skip to content

什么是模板方法模式?一般用在什么场景? ​

  • 标签:设计模式

🏛 业务架构师 ​

模板方法模式由父类定流程骨架,子类填差异步骤。它回答的业务问题是:"流程统一,细节多样"——这正是多渠道、多品类业务的通用形态。

真实场景:

  • 多支付渠道接入:骨架是"创建支付单 → 调渠道下单 → 处理异步回调 → 更新状态 → 通知业务",支付宝、微信、银联只实现各自的参数组装、签名、报文解析。渠道接入从两周缩到两三天,渠道差异被约束在受控的扩展点里,不会污染主流程;
  • 对账系统:拉渠道账单 → 格式归一 → 本地账匹配 → 差异输出 → 处理落库。每家渠道的"格式归一"不同,流程其余部分全团队共享一套正确实现——正确性只需要保证一次;
  • 报表导出:查数 → 转换 → 渲染 Excel/PDF,仅渲染环节差异化。

工程上的要点:骨架方法用 final 锁死(防止子类改坏流程语义),扩展点按"变化频率"设计——不变的沉到父类,变的留成钩子。也要防过度:只有一种实现且无扩展预期时,直接写流程即可。

和策略模式的配合我常一起用:模板方法管纵向的流程差异,策略管横向的算法差异(对账流程是模板,匹配算法是策略)。两者组合基本能覆盖渠道型业务 80% 的扩展需求,这是我评估"新渠道接入成本"时的设计底座。

🏗 系统架构师 ​

模板方法在父类固定算法骨架,把可变步骤延迟到子类实现——"流程统一,细节各异"。

结构:抽象类定义 templateMethod()(final,编排步骤顺序),步骤方法里公共的直接实现,差异化的声明为抽象/钩子方法留给子类。

典型场景:

  • JdbcTemplate:获取连接→创建语句→执行→处理结果集→关资源,骨架固定,业务只传 SQL 和 RowMapper;
  • 我做对账系统:拉取渠道账单→本地账单→逐笔核对→差异落库→生成报告,五个步骤流程固定,各渠道(微信/支付宝/银联)只重写"拉取"和"解析"两步,新渠道接入只写差异部分;
  • 并发框架:AQS 的 tryAcquire/tryRelease 就是模板方法,锁的语义子类定义,排队阻塞骨架父类兜;
  • HttpServlet 的 service → doGet/doPost。

适用判断:多个类流程相同、仅个别步骤不同时用模板方法;如果整个算法都要换,那该用策略模式。它是"复用不变、开放变化"最朴素也最高效的落地。

💻 全栈开发专家 ​

父类定死算法骨架(流程步骤顺序),子类只实现其中某些步骤,"流程复用、步骤定制"。

java
public abstract class AbstractExportTask {
    public final void export(Long taskId) {        // final 防止子类改流程
        List<Data> data = queryData(taskId);        // 步骤2:子类实现
        byte[] file = render(data);                 // 步骤3:子类实现
        upload(taskId, file);                       // 步骤1/4:父类统一
        notifyUser(taskId);
    }
    protected abstract List<Data> queryData(Long taskId);
    protected abstract byte[] render(List<Data> data);
    private void upload(Long id, byte[] file) { /* OSS 公共逻辑 */ }
}

框架里的应用:JdbcTemplate(我传 SQL 和行映射,连接/异常/资源关闭的骨架它管)、AbstractList、HttpServlet 的 service→doGet/doPost、AQS 的 acquire 骨架(tryAcquire 留给子类)。

我落地的场景:导出任务(Excel/CSV/PDF 只差 render)、对账任务(拉渠道流水/拉本地流水/比对/落差异,不同渠道只实现拉流水)、定时任务生命周期(锁抢占→执行→释放→上报)。

工程心得:模板方法最大的价值是把"不变的公共流程和容易出错的资源管理"收口到父类(比如 finally 释放分布式锁),新人新增一个渠道不会漏掉关键步骤。