一句话总结
模板方法模式在父类中定义算法骨架(final 模板方法),将可变步骤延迟到子类实现。核心:抽象方法(子类必须实现)、钩子方法(子类可选覆盖,控制流程)、具体方法(父类实现,子类不可改)。Spring 经典应用:JdbcTemplate(execute() 定义流程,子步骤回调)、AbstractApplicationContext.refresh()(定义 12 步容器刷新流程,子类覆盖特定步骤)。与策略模式区别:模板方法用继承,策略模式用组合。
初级理解
手写模板方法模式
# 抽象父类:定义算法骨架
public abstract class BeverageMaker {
// 模板方法(final,子类不能重写)
public final void make() {
boilWater(); // 1. 烧水(固定步骤)
brew(); // 2. 冲泡(子类实现)
pourInCup(); // 3. 倒入杯中(固定步骤)
if (needCondiments()) { // 4. 钩子方法
addCondiments(); // 5. 加调料(子类实现)
}
}
private void boilWater() {
System.out.println("烧水");
}
private void pourInCup() {
System.out.println("倒入杯中");
}
// 抽象方法:子类必须实现
protected abstract void brew();
protected abstract void addCondiments();
// 钩子方法:子类可选覆盖
protected boolean needCondiments() {
return true; // 默认需要加调料
}
}
# 具体子类:泡茶
public class TeaMaker extends BeverageMaker {
protected void brew() {
System.out.println("用沸水冲泡茶叶");
}
protected void addCondiments() {
System.out.println("加柠檬");
}
}
# 具体子类:冲咖啡
public class CoffeeMaker extends BeverageMaker {
protected void brew() {
System.out.println("用沸水冲泡咖啡粉");
}
protected void addCondiments() {
System.out.println("加糖和牛奶");
}
}
# 使用
new TeaMaker().make();
# 输出:烧水 → 冲泡茶叶 → 倒入杯中 → 加柠檬
new CoffeeMaker().make();
# 输出:烧水 → 冲泡咖啡粉 → 倒入杯中 → 加糖和牛奶
中级深入
Spring JdbcTemplate
# JdbcTemplate 是模板方法模式的经典应用
# 模板方法:execute() 定义 JDBC 操作骨架
# 可变步骤:通过回调接口(StatementCallback)延迟到调用方
public class JdbcTemplate {
// 模板方法:定义 JDBC 操作骨架
public <T> T execute(StatementCallback<T> action) {
Connection conn = null;
Statement stmt = null;
try {
// 1. 获取连接(固定步骤)
conn = dataSource.getConnection();
// 2. 创建 Statement(固定步骤)
stmt = conn.createStatement();
// 3. 执行 SQL(可变步骤,回调实现)
T result = action.doInStatement(stmt);
// 4. 返回结果(固定步骤)
return result;
} catch (SQLException e) {
// 5. 异常处理(固定步骤)
throw translateException(e);
} finally {
// 6. 释放资源(固定步骤)
closeStatement(stmt);
closeConnection(conn);
}
}
}
# 使用(回调实现可变步骤):
String result = jdbcTemplate.execute(stmt -> {
ResultSet rs = stmt.executeQuery("SELECT name FROM user");
rs.next();
return rs.getString("name");
});
# 模板方法模式 vs 回调:
# 传统模板方法:通过继承,子类覆盖抽象方法
# JdbcTemplate:通过回调接口,组合方式实现
# 回调方式更灵活,避免类膨胀
AbstractApplicationContext.refresh()
# Spring 容器刷新流程(模板方法模式)
public abstract class AbstractApplicationContext {
// 模板方法(final)
public void refresh() {
// 1. 准备刷新
prepareRefresh();
// 2. 获取 BeanFactory
ConfigurableListableBeanFactory beanFactory =
obtainFreshBeanFactory();
// 3. 准备 BeanFactory
prepareBeanFactory(beanFactory);
// 4. BeanFactory 后置处理(钩子,子类覆盖)
postProcessBeanFactory(beanFactory);
// 5. 执行 BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 注册 BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// 7. 初始化消息源
initMessageSource();
// 8. 初始化事件广播器
initApplicationEventMulticaster();
// 9. 钩子方法(子类覆盖,如创建 Web 容器)
onRefresh();
// 10. 注册监听器
registerListeners();
// 11. 实例化所有非懒加载单例 Bean
finishBeanFactoryInitialization(beanFactory);
// 12. 完成刷新
finishRefresh();
}
// 钩子方法:子类可选覆盖
protected void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) {}
protected void onRefresh() {}
}
# 子类覆盖钩子方法:
# GenericWebApplicationContext.onRefresh()
# → 创建 Web 容器(Tomcat/Jetty)
# GenericApplicationContext.postProcessBeanFactory()
# → 注册 Bean 等
高级进阶
模板方法 vs 策略模式
| 对比维度 | 模板方法 | 策略模式 |
|---|---|---|
| 复用方式 | 继承 | 组合 |
| 控制权 | 父类控制流程(好莱坞原则) | 客户端选择策略 |
| 粒度 | 算法骨架级别 | 单个算法 |
| 扩展方式 | 新增子类 | 新增策略类 |
| 典型场景 | JdbcTemplate、refresh() | 支付方式、折扣计算 |
好莱坞原则
# 好莱坞原则:"Don't call us, we'll call you."
# 模板方法模式完美体现好莱坞原则
# 传统方式:子类控制流程
class TeaMaker {
void make() {
boilWater(); // 子类自己调用每个步骤
brew();
pourInCup();
}
}
# 模板方法:父类控制流程,回调子类
abstract class BeverageMaker {
final void make() {
boilWater(); // 父类控制流程
brew(); // 回调子类实现
pourInCup();
}
}
# 优势:
# 1. 父类控制算法骨架,保证流程正确
# 2. 子类只需关注可变部分
# 3. 代码复用:固定步骤只写一次
实战场景
# 场景1:数据导入模板
public abstract class DataImporter {
public final void importData(String filePath) {
List<String> lines = readFile(filePath); // 读文件
validate(lines); // 校验
List<T> dataList = parse(lines); // 解析(子类实现)
beforeSave(dataList); // 保存前钩子
save(dataList); // 保存
afterSave(dataList); // 保存后钩子
}
protected abstract List<T> parse(List<String> lines);
protected void beforeSave(List<T> dataList) {} // 钩子
protected void afterSave(List<T> dataList) {} // 钩子
}
# 场景2:MyBatis BaseExecutor
# BaseExecutor.query() 定义查询骨架
# doQuery() 抽象方法,由子类实现
# SimpleExecutor / ReuseExecutor / BatchExecutor
# 场景3:Servlet API
# HttpServlet.service() 定义处理骨架
# doGet() / doPost() 由子类实现
面试模拟
面试官:模板方法模式是什么?Spring 中哪里用到了?
你:模板方法模式在父类定义算法骨架(final 方法),可变步骤延迟到子类实现。Spring 中 JdbcTemplate.execute() 定义 JDBC 操作骨架(获取连接→执行SQL→释放资源),SQL 执行通过回调接口延迟到调用方。AbstractApplicationContext.refresh() 定义 12 步容器刷新流程,onRefresh() 等钩子方法由子类覆盖。
面试官:模板方法模式和策略模式有什么区别?
你:模板方法用继承,父类控制算法骨架,子类只实现可变步骤(好莱坞原则)。策略模式用组合,定义算法族,客户端选择策略。模板方法适合流程固定、部分步骤变化的场景(如数据导入流程),策略模式适合算法完全可替换的场景(如支付方式)。