深度解析Java模板方法模式:从经典案例到企业级应用实战
目录导读
- 模式本质:模板方法模式的核心定义与设计哲学
- 场景痛点:什么业务场景下必须使用该模式
- 经典案例:从框架源码到银行系统的完整代码实现
- 进阶技巧:钩子方法、好莱坞原则与性能优化
- 常见陷阱:继承滥用与模板方法反模式识别
- SEO问答:高频面试题与最佳实践总结
模式本质:让算法骨架与具体实现解耦
问:模板方法模式最核心的价值是什么?
答:定义一个操作中的算法骨架,而将一些步骤延迟到子类中实现,它使得子类可以在不改变算法结构的情况下,重新定义算法的某些特定步骤。

真实案例对比:
假如开发一个咖啡与茶冲泡系统:
- 共同步骤:烧水 → 冲泡 → 倒入杯子 → 加调料
- 不同点:咖啡需要研磨豆子,茶需要浸泡茶叶;咖啡加糖和奶,茶加柠檬
用模板方法模式,父类定义冲泡流程(模板方法),子类只需实现“冲泡”和“加调料”两个抽象方法。
代码演示(简化版):
public abstract class Beverage {
// 模板方法:不允许子类重写(final修饰)
public final void prepareRecipe() {
boilWater();
brew(); // 抽象方法
pourInCup();
addCondiments(); // 抽象方法
}
private void boilWater() { System.out.println("烧水至100℃"); }
private void pourInCup() { System.out.println("倒入杯子"); }
protected abstract void brew();
protected abstract void addCondiments();
}
设计哲学:遵循“开闭原则”——对扩展开放,对修改封闭,当新饮料出现时,只需新增子类,无需改动父类模板。
场景痛点:为什么你写的重复代码越来越多?
问:不用模板方法模式,直接用if-else行不行?
答:可以,但项目迭代3个月后会出现以下问题:
- 流程不一致:不同开发者在相同算法步骤中引入不同顺序
- 代码散落:重复的烧水、倒杯逻辑散落在N个类中
- 维护噩梦:修改烧水温度需要搜遍所有饮料类
企业级典型场景:
- 数据库连接池的初始化流程
- 工作流引擎的审批节点执行
- HTTP请求的拦截器链条(如Spring的HandlerInterceptor)
- 支付系统中不同渠道(微信/支付宝/银联)的接口调用
经典案例:从Spring源码到银行系统的完整实现
案例1:Spring的AbstractApplicationContext
这是Java生态最著名的模板方法应用,其核心方法refresh()定义了IOC容器启动的13个步骤:
public abstract class AbstractApplicationContext {
public void refresh() throws BeansException {
// 1. 准备上下文
prepareRefresh();
// 2. 告诉子类刷新内部bean工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. 填充bean工厂特性
prepareBeanFactory(beanFactory);
// 4. 允许子类后处理bean工厂
postProcessBeanFactory(beanFactory); // 钩子方法
// ... 更多步骤
}
// 钩子方法:子类可覆盖,但默认空实现
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {}
}
案例2:银行转账系统(真实业务代码)
假设需要处理“转账”业务:
- 所有渠道(柜面、网银、API)必须执行:校验账户 → 验证身份 → 执行转账 → 记录日志
- 但柜面需插U盾,网银需输入短信验证码,API需校验签名
代码设计:
public abstract class TransferProcessor {
// 模板方法(final)
public final boolean doTransfer(TransferRequest request) {
if (!validateAccount(request)) return false;
if (!verifyIdentity(request)) return false;
if (!executeTransfer(request)) return false;
logTransfer(request);
return true;
}
// 公共逻辑(直接实现)
private void logTransfer(TransferRequest req) {
System.out.println("[日志] 转账成功: " + req.getAmount());
}
// 子类必须实现
protected abstract boolean validateAccount(TransferRequest req);
protected abstract boolean verifyIdentity(TransferRequest req);
protected abstract boolean executeTransfer(TransferRequest req);
}
// 柜面转账子类
public class CounterTransfer extends TransferProcessor {
@Override
protected boolean validateAccount(TransferRequest req) {
System.out.println("验证柜面账户余额");
return true;
}
@Override
protected boolean verifyIdentity(TransferRequest req) {
System.out.println("验证U盾签名");
return true;
}
@Override
protected boolean executeTransfer(TransferRequest req) {
System.out.println("柜面发起转账");
return true;
}
}
运行效果:调用方只需new CounterTransfer().doTransfer(request),无需关心具体流程。
进阶技巧:钩子方法与好莱坞原则
1 钩子方法(Hook Method)
问:模板方法能否支持“可选步骤”?
答:通过默认实现的钩子方法,子类根据需要决定是否覆盖。
经典场景:咖啡店提供“是否加糖”选项
public abstract class CaffeineBeverage {
public final void prepareRecipe() {
boilWater();
brew();
if (customerWantsCondiments()) { // 钩子方法
addCondiments();
}
pourInCup();
}
// 钩子方法:默认返回true
protected boolean customerWantsCondiments() {
return true;
}
}
子类可通过覆盖customerWantsCondiments()返回false来跳过加调料步骤。
2 好莱坞原则(Hollywood Principle)
“不要调用我们,我们会调用你。”——控制反转的一种体现。
模板方法模式中,父类控制算法流程,子类只提供具体实现,正是好莱坞原则的典型应用。
常见陷阱:过度使用模板方法会带来什么?
陷阱1:继承层级过深
症状:抽象类A继承抽象类B,B继承抽象类C...调用链超过5层
后果:修改任何一层模板方法,都可能影响全部分支
解决:优先考虑组合+策略模式,代替继承+模板方法
陷阱2:模板方法过于庞大
反例:一个模板方法包含30个步骤,子类被迫实现所有抽象方法
后果:子类代码膨胀,且很多步骤其实有默认实现
解决:使用钩子方法减少抽象方法数量;或拆分模板方法为多个小模板
陷阱3:违反里氏替换原则(LSP)
症状:子类修改了父类模板方法的行为(如覆盖了非abstract方法)
后果:无法保证算法骨架稳定性
铁律:父类模板方法必须用final修饰,公共步骤用private,抽象步骤用protected abstract
SEO问答:高频面试题与最佳实践总结
Q1:模板方法模式与策略模式的区别?
- 模板方法:定义算法骨架,子类实现具体步骤(继承)
- 策略模式:定义算法接口,客户端选择算法实现(组合)
- 选择依据:如果算法步骤固定但实现不同 → 用模板方法;如果算法整体可替换 → 用策略模式
Q2:在JDK源码中哪里使用了模板方法?
java.io.InputStream:read(byte[])调用抽象方法read()java.util.AbstractList:addAll()调用add()javax.servlet.http.HttpServlet:doGet()、doPost()由service()模板方法调用
Q3:模板方法模式如何提升代码可测试性?
- 父类模板方法通过子类注入测试桩(Mock)
- 每个抽象方法可独立测试
TransferProcessor可以通过构造Mock子类来测试日志记录逻辑
最佳实践清单
- 模板方法标记为
final,子类不可重写 - 所有重复的算法步骤放在父类私有方法中
- 钩子方法使用
protected提供默认实现 - 子类只覆盖抽象方法,不重写任何非抽象方法
- 当子类数量超过10个时,考虑引入枚举类型+工厂模式替代继承
模板方法模式是Java设计模式中“控制反转”思想最直观的体现,它通过将算法骨架固定在父类中,既保证了业务逻辑的一致性,又给扩展留下了优雅接口,在实际项目中,建议配合单元测试和代码审查,防止继承滥用导致的类爆炸问题。
推荐阅读:
- 《Head First 设计模式》第8章
- Spring Framework源码中的
AbstractApplicationContext - 阿里巴巴Java开发手册中关于设计模式的规范章节