模板方法模式要求将不变流程控制权锁死在抽象类中,子类只能覆写钩子或abstract方法,templatemethod()必须声明为final;abstract方法强制实现,hook方法可选覆写;不适用于流程频繁变动或需彻底替换步骤的场景。

模板方法模式不是“写个模板然后填空”,而是把不变的流程控制权锁死在抽象类里,子类只能改具体步骤——一旦子类偷偷重写了模板方法本身,整个模式就失效了。
为什么子类重写 templateMethod() 会破坏模式
模板方法的核心是“流程不可变”:抽象类定义好调用顺序(比如 prepare() → cook() → serve()),再用 final 封住 templateMethod()。如果子类绕过这个方法、自己另起炉灶,或者干脆重写它,那抽象类定的骨架就形同虚设。
- 常见错误现象:
NullPointerException或流程跳步,比如serve()在cook()前执行 - 根本原因:子类没遵守“只覆写钩子方法”的约定,动了
templateMethod()或直接调用底层步骤 - 正确做法:把
templateMethod()声明为final,强制子类只能覆写prepare()、cook()这类钩子方法
abstract 方法和 hook 方法的区别在哪
两者都允许子类定制,但语义和强制程度完全不同。
-
abstract方法:必须被子类实现,否则编译不通过;适合核心不可缺的步骤(如cook()) -
hook方法:有默认空实现(protected void serve() {}),子类可选覆写;适合可插拔的扩展点(如addCondiments()) - 性能影响:
hook方法多一层虚调用,但现代 JVM 基本内联掉,不用刻意规避
什么时候不该用模板方法模式
不是所有“父类定流程、子类填细节”的场景都适合它——关键看流程是否真的稳定、是否需要强约束。
立即学习“Java免费学习笔记(深入)”;
- 适用场景:框架代码(如 Spring 的
AbstractApplicationContext.refresh())、构建流程(如 Gradle 的 Task 执行链) - 不适用场景:算法步骤经常增删(比如今天三步、明天五步)、子类需要彻底替换某一步逻辑(这时策略模式更合适)
- 容易踩的坑:把太多逻辑塞进抽象类,导致子类被迫继承一堆无关功能;应该只留骨架,其余用组合引入
最常被忽略的一点:模板方法的调用栈是固定的,调试时看到 templateMethod() 调 cook() 再调 doCook(),别急着在子类里加日志——先确认 doCook() 是不是 abstract 的,还是只是个可选 hook。流程对了,才轮得到看实现对不对。








