模板方法模式为什么说是通过继承复用流程?
简化版
模板方法模式通过继承把公共流程放到父类,把差异步骤交给子类实现。父类复用流程代码,子类通过重写步骤方法参与流程,本质是利用继承和多态完成代码复用与扩展。
详细版
模板方法模式的复用方式和组合类模式不同,它主要依赖继承:
- 父类沉淀公共流程;
- 子类继承父类流程;
- 子类实现抽象步骤;
- 父类模板方法通过多态调用子类步骤。
例如多个任务都要执行“校验参数、执行业务、记录日志、释放资源”,父类可以统一实现这些公共步骤,子类只实现具体业务执行。
这种方式的优点是流程复用直接、顺序可控;缺点是父子类耦合较强,继承层次深时不容易维护。因此模板方法适合流程非常稳定的场景,不适合频繁变化或多维度组合的场景。
完整版教学
一、继承复用的核心是父类沉淀公共部分
模板方法模式常用于一组类有相同流程的场景。
比如批处理任务:
加载配置 -> 校验参数 -> 执行任务 -> 记录结果 -> 清理资源
不同任务的“执行任务”不同,但其他步骤相似。父类可以这样设计:
public abstract class BaseJob {
public final void run() {
loadConfig();
validate();
execute();
recordResult();
cleanup();
}
protected abstract void execute();
}
子类只实现 execute()。
二、多态让父类调用子类实现
模板方法模式并不是父类知道所有子类,而是通过多态完成调用。
BaseJob job = new ImportJob();
job.run();
执行 run() 时,父类控制流程;执行到 execute() 时,实际调用的是 ImportJob 的实现。
这就是模板方法模式的机制:父类写稳定流程,子类补可变步骤。
三、继承复用和复制粘贴的区别
复制粘贴复用的问题是,一旦公共流程要改,所有复制过的地方都要改。
继承复用把公共流程集中在父类。比如要给所有任务增加耗时统计,只需要改父类模板方法:
long start = System.currentTimeMillis();
try {
execute();
} finally {
logCost(start);
}
所有子类都自动获得这段逻辑。
四、继承复用也有代价
继承不是免费的。模板方法模式会带来父子类耦合。
父类流程一改,所有子类都会受影响。子类如果很多,修改父类前必须非常谨慎。
此外,Java 只有单继承。如果一个类已经继承了其他父类,就不能再继承这个模板父类。这也是模板方法模式的局限。
如果需要灵活组合多个维度的行为,策略模式、装饰器模式或组合方式可能更合适。
五、如何让继承复用更稳
设计模板父类时要注意:
- 模板方法尽量稳定,不要频繁修改顺序。
- 抽象步骤要少而清晰。
- 钩子方法不要太多。
- 父类不要依赖具体子类。
- 子类不要绕过模板方法直接调用内部步骤。
这样继承关系才不会变成沉重包袱。
六、继承复用与多态回调的代价
4 个任务各自复制 30 行公共流程会重复 120 行;上移父类后只保留约 30 行骨架,但 4 个子类都开始依赖父类契约。
subclass.template() -> inherited skeleton -> overridden primitive operations
这里真正需要保护的是父类掌握的流程不变量:客户端只能进入模板入口,子类只能改写约定好的步骤。评审时应逐步标出哪些行为固定、哪些行为必须实现、哪些行为只是可选钩子,避免父类和子类互相猜测。
七、继承边界与流程契约验证
| 检查维度 | 应确认的内容 |
|---|---|
| 机制正确性 | 消除重复只是收益的一面,脆弱基类和单继承限制是必须同时回答的代价。 |
| 适用边界 | 继承建立编译期强耦合,父类步骤签名变化会影响全部子类;变化频繁或需多维组合时优先考虑组合。 |
| 测试证据 | 记录步骤进入与退出序列,覆盖默认钩子、覆写钩子、步骤异常和清理路径 |
| 工程代价 | 重点评估父类改动会影响多少子类、protected 扩展面有多大,以及新增变化是否迫使无关子类一起修改 |
易错点:消除重复只是收益的一面,脆弱基类和单继承限制是必须同时回答的代价。
八、常见误区与追问
- 误区:只要有重复代码就应该抽象父类。 只有重复代码表达同一稳定概念和相同流程不变量时才适合继承复用。
- 误区:模板方法要求所有步骤都由子类覆写。 稳定步骤应在父类直接实现;只有必要变化点才做抽象步骤,可选变化点才做有默认实现的钩子。
- 追问:怎样降低脆弱基类问题? 缩小 protected 面、固定模板入口、记录扩展契约,并为所有子类运行父类契约测试。
- 追问:模板方法为什么常声明为 final? 为了防止子类改写算法骨架和步骤顺序;若框架有意允许重写,则必须明确不变量和扩展契约。
- 追问:模板方法最关键的回归测试是什么? 是父类契约测试:所有子类都必须满足固定步骤顺序、公共前后置行为和异常清理规则。
九、加强记忆
模板方法模式的复用方式是“父类复用流程,子类复用骨架并补充步骤”。它利用继承和多态减少重复、固定顺序,但也带来父子类耦合,所以适合流程稳定、变化点少的场景。