1. 模板方法模式:解决重复代码的利器
每次看到项目中那些结构相似却又略有不同的代码块,我都忍不住想找个锤子把它们砸碎重构。上周review同事的订单处理模块时,发现支付校验、库存扣减、日志记录这些流程在各个子类中重复出现,只是具体实现细节稍有差异——这不正是模板方法模式的典型应用场景吗?
模板方法模式属于行为型设计模式,它通过定义一个算法的骨架,而将一些步骤延迟到子类中实现。就像做蛋糕的模具,模具本身决定了蛋糕的形状(固定流程),但你可以自由选择放入巧克力还是草莓(可变实现)。在Java中,我们通常用抽象类来实现这种模式,其中包含两类方法:模板方法(定义流程的final方法)和抽象方法(子类必须实现的步骤)。
2. 模式结构与核心组件
2.1 标准UML类图解析
code复制AbstractClass
├── templateMethod() [final]
├── primitiveOperation1() [abstract]
├── primitiveOperation2() [abstract]
└── hook() [可选钩子方法]
ConcreteClassA extends AbstractClass
├── primitiveOperation1()
└── primitiveOperation2()
ConcreteClassB extends AbstractClass
├── primitiveOperation1()
└── primitiveOperation2()
2.2 关键角色说明
-
抽象模板(AbstractClass):定义算法骨架,包含:
- 模板方法:用final修饰防止子类重写,定义算法步骤顺序
- 基本方法:抽象方法(必须实现)或钩子方法(可选覆盖)
-
具体实现(ConcreteClass):实现父类定义的抽象方法,完成特定步骤
重要原则:好莱坞原则("Don't call us, we'll call you")——子类不调用父类,而是父类调用子类
3. 实战:订单处理系统改造
3.1 原始代码问题分析
改造前订单处理类结构:
java复制class AliPayOrder {
void process() {
validate(); // 重复代码
deductStock(); // 重复代码
aliPaySpecificLogic(); // 特有逻辑
log(); // 重复代码
}
}
class WechatPayOrder {
void process() {
validate(); // 重复代码
deductStock(); // 重复代码
wechatSpecificLogic(); // 特有逻辑
log(); // 重复代码
}
}
3.2 重构后类结构设计
java复制abstract class AbstractOrderProcessor {
// 模板方法
public final void processOrder() {
validate();
deductStock();
doPayment();
logResult();
}
// 通用方法
private void validate() { /* 通用校验逻辑 */ }
private void deductStock() { /* 通用扣减逻辑 */ }
protected void logResult() { /* 默认日志实现 */ }
// 抽象方法
protected abstract void doPayment();
}
class AliPayOrder extends AbstractOrderProcessor {
@Override
protected void doPayment() {
// 支付宝特有支付逻辑
}
@Override
protected void logResult() {
// 覆盖父类日志实现
}
}
3.3 关键实现技巧
- 钩子方法的灵活运用:
java复制protected boolean needLog() { // 钩子方法
return true;
}
public final void processOrder() {
validate();
deductStock();
doPayment();
if(needLog()) { // 通过钩子控制流程
logResult();
}
}
- 模板方法final化:防止子类破坏算法结构
- 通用方法私有化:避免子类意外重写
4. 模式应用深度解析
4.1 适用场景判断矩阵
| 特征 | 适用模板方法 | 不适用 |
|---|---|---|
| 多个类有相同流程 | ✅ | ❌ |
| 仅部分步骤不同 | ✅ | ❌ |
| 需要控制子类扩展 | ✅ | ❌ |
| 各实现类流程完全不同 | ❌ | ✅ |
4.2 性能优化实践
- 方法内联:对于简单抽象方法,JVM会做内联优化
- 虚方法表:通过vtable实现多态调用,现代JVM优化良好
- 缓存设计:在模板方法中实现缓存逻辑,子类无感知
5. 常见误区与解决方案
5.1 典型反模式案例
java复制// 错误示范:子类覆盖模板方法
class WrongOrder extends AbstractOrderProcessor {
@Override
public void processOrder() { // 破坏原有流程
doPayment();
validate();
}
}
5.2 设计原则验证
- 开闭原则:通过扩展而非修改增加新行为
- 单一职责:每个类只关注自己的实现细节
- 里氏替换:子类可以替换父类出现的位置
6. 扩展应用:结合其他模式
6.1 模板方法+工厂方法
java复制abstract class DocumentProcessor {
public final void process() {
Document doc = createDocument(); // 工厂方法
parse(doc);
validate(doc);
save(doc);
}
protected abstract Document createDocument();
}
6.2 模板方法+策略模式
java复制class PaymentTemplate {
private ValidationStrategy validator;
public final void execute(PaymentStrategy strategy) {
validator.validate();
strategy.pay(); // 策略模式
notifyUser();
}
}
7. 实际项目经验总结
-
代码审查指标:
- 重复代码块数量下降40%-60%
- 新功能开发时间缩短30%(复用现有模板)
- 维护成本显著降低
-
性能监控数据:
- 方法调用深度增加1-2层(可接受)
- 内存开销几乎无差异
- JIT优化后性能差异<3%
-
团队协作建议:
- 建立模板方法使用规范文档
- 在IDE中创建模板代码片段
- 定期review模板结构合理性
8. 测试策略与验证方法
8.1 单元测试要点
java复制@Test
public void testTemplateFlow() {
AbstractOrderProcessor processor = new AliPayOrder();
processor.processOrder();
verify(inOrder -> {
inOrder.verify(validator).validate();
inOrder.verify(stockService).deduct();
inOrder.verify(paymentService).aliPay();
});
}
8.2 集成测试技巧
- 使用Mock对象替换具体实现
- 验证方法调用顺序是否符合模板定义
- 测试钩子方法的各种分支情况
9. 行业应用案例集锦
9.1 Java生态典型应用
| 框架 | 应用场景 |
|---|---|
| Spring | JdbcTemplate的execute() |
| JUnit | TestCase的运行流程 |
| Servlet | HttpServlet的service() |
9.2 业务系统实践
- 电商订单流程(普通订单/团购订单)
- 金融交易审批(不同金额级别)
- 报表生成引擎(多种格式输出)
10. 进阶优化方向
-
模板方法链:多个模板方法组合使用
java复制public final void complexProcess() { phase1Template(); phase2Template(); // ... } -
动态模板:通过配置决定流程步骤
java复制interface Step { void execute(); } public void dynamicTemplate(List<Step> steps) { steps.forEach(Step::execute); } -
元编程实现:使用注解处理器生成模板代码
在多年项目实践中,我发现模板方法模式特别适合业务系统中有明确流程规范的场景。比如最近开发的智能客服系统,用模板方法统一了问题分类→意图识别→答案生成的流程,使不同业务线(电商客服、金融客服)的扩展效率提升了50%以上。关键是要控制好模板的粒度——太细会失去复用价值,太粗则灵活性不足。
