模板方法模式?

2025年 阅读约 10 分钟 面试指南 · 设计模式

深入解析模板方法模式:算法骨架与步骤延迟、Spring中的JdbcTemplate和AbstractApplicationContext、钩子方法、与策略模式对比,附面试模拟问答。

一句话总结

模板方法模式在父类中定义算法骨架(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() 等钩子方法由子类覆盖。

面试官:模板方法模式和策略模式有什么区别?

你:模板方法用继承,父类控制算法骨架,子类只实现可变步骤(好莱坞原则)。策略模式用组合,定义算法族,客户端选择策略。模板方法适合流程固定、部分步骤变化的场景(如数据导入流程),策略模式适合算法完全可替换的场景(如支付方式)。