你写的代码一半都是重复逻辑模板方法能帮你省掉写过 JDBC 的人应该都有这段记忆Connection conn null;PreparedStatement ps null;ResultSet rs null;try {conn dataSource.getConnection();ps conn.prepareStatement(sql);ps.setString(1, name);rs ps.executeQuery();while (rs.next()) {// 处理结果}} catch (SQLException e) {e.printStackTrace();} finally {if (rs ! null) rs.close();if (ps ! null) ps.close();if (conn ! null) conn.close();}这段代码你写一次第二次写另一个查询的时候结构几乎一模一样只有 SQL 和结果处理不同。这种骨架相同、细节不同的代码就是模板方法模式要干掉的。---什么意思模板方法的思路是**把不变的流程固化在父类里把变化的部分留给子类去实现。**获取连接→准备语句→执行→处理结果→关闭资源这个流程是不变的变化的是 SQL 和结果处理。public abstract class JdbcTemplate {public final T execute(String sql) {Connection conn null;PreparedStatement ps null;ResultSet rs null;try {conn dataSource.getConnection();ps conn.prepareStatement(sql);setParameters(ps); // 子类实现设置参数rs ps.executeQuery();return handleResult(rs); // 子类实现处理结果} catch (SQLException e) {throw new RuntimeException(e);} finally {closeQuietly(rs, ps, conn);}}protected abstract void setParameters(PreparedStatement ps) throws SQLException;protected abstract T handleResult(ResultSet rs) throws SQLException;}子类只需要关注自己的那部分public class FindUserById extends JdbcTemplate {private final Long userId;public FindUserById(DataSource ds, Long userId) {super(ds);this.userId userId;}Overrideprotected void setParameters(PreparedStatement ps) throws SQLException {ps.setLong(1, userId);}Overrideprotected User handleResult(ResultSet rs) throws SQLException {if (rs.next()) {return new User(rs.getLong(id), rs.getString(name));}return null;}}其实 Spring 的JdbcTemplate就是这个思路的工业级实现。只不过 Spring 用回调函数代替了继承更灵活一些。---Spring 里的模板方法你可能没意识到你每天都在用模板方法模式。Spring 的大量基类设计都基于这个模式。AbstractTransactionalDataSourcePlatformTransactionManager——光看名字就知道它是个抽象基类里面的doBegin()、doCommit()、doRollback()都是留给子类实现的钩子方法。DataSourceTransactionManager和JtaTransactionManager分别继承了它实现了不同的资源管理方式。Spring Batch 里更明显public abstract class ItemProcessor {public final O process(I item) throws Exception {O result doProcess(item); // 子类实现if (result null) {throw new SkipItemException(处理结果为空跳过);}return result;}protected abstract O doProcess(I item) throws Exception;}process是模板方法doProcess是钩子。模板方法用final修饰防止子类覆盖流程保证骨架不被破坏。---用回调替代继承继承是模板方法的经典实现但继承有硬限制——Java 只能单继承你的类如果已经有父类了就没法再用模板方法。更灵活的方式是用回调函数式接口本质上还是模板方法的思路——固定骨架变化部分注入public class TemplateRunner {public T runWithTransaction(ConnectionCallback action) {Connection conn null;try {conn dataSource.getConnection();conn.setAutoCommit(false);T result action.doInConnection(conn); // 回调变化的部分conn.commit();return result;} catch (Exception e) {conn.rollback();throw new RuntimeException(e);} finally {conn.close();}}}FunctionalInterfacepublic interface ConnectionCallback {T doInConnection(Connection conn) throws Exception;}用的时候User user runner.runWithTransaction(conn - {PreparedStatement ps conn.prepareStatement(SELECT * FROM user WHERE id ?);ps.setLong(1, userId);ResultSet rs ps.executeQuery();return rs.next() ? new User(rs.getLong(id), rs.getString(name)) : null;});这其实就是 SpringJdbcTemplate.query()的思路。回调 模板方法比继承更灵活Lambda 写起来也比子类清爽。---什么时候该用模板方法模式适合这类场景- 多个类有共同的执行流程只有个别步骤不同- 你想固定执行顺序不允许子类改变骨架- 公共逻辑应该在父类统一处理比如异常、日志、事务如果你发现自己在多个子类里写了一模一样的代码而且这些代码的执行顺序也相同那就可以考虑抽模板方法。---什么时候不该用骨架不固定、流程经常变化的场景模板方法反而碍事。每次流程一变你都要改父类所有子类可能都要受影响。还有一种情况不同子类之间的差异太大模板方法里定义的钩子太多超过 5 个说明你的抽象不合适。这时候不如把逻辑拆开每个子类自己决定流程。---我在做一个用卡皮巴拉讲 23 个设计模式的微信小程序「**爪爪代码冒险记**」漫画 答题闯关比翻书有意思。微信搜「爪爪代码冒险记」就能找到。