跳到主要内容

模板方法模式

问题

什么是模板方法模式?Java 和 Spring 中有哪些典型应用?

答案

核心思想

在父类中定义算法的骨架(步骤顺序),将某些步骤的具体实现延迟到子类。子类可以重写特定步骤,但不能改变整体流程。

实现示例

模板方法 - 数据导出
public abstract class DataExporter {

// 模板方法:定义导出流程(final 防止子类修改流程)
public final void export() {
List<Map<String, Object>> data = queryData(); // 第 1 步:查数据
List<String[]> rows = transformData(data); // 第 2 步:转换格式
byte[] file = generateFile(rows); // 第 3 步:生成文件(子类实现)
if (needCompress()) { // 钩子方法:是否压缩
file = compress(file);
}
upload(file); // 第 4 步:上传
}

// 通用步骤:父类已实现
protected List<Map<String, Object>> queryData() { /* SQL 查询 */ return List.of(); }
protected void upload(byte[] file) { /* OSS 上传 */ }

// 抽象步骤:子类必须实现
protected abstract byte[] generateFile(List<String[]> rows);

// 钩子方法:子类可选覆盖,默认不压缩
protected boolean needCompress() { return false; }
}

// Excel 导出
public class ExcelExporter extends DataExporter {
@Override
protected byte[] generateFile(List<String[]> rows) {
// 使用 Apache POI 生成 Excel
return new byte[0];
}
}

// CSV 导出 + 压缩
public class CsvExporter extends DataExporter {
@Override
protected byte[] generateFile(List<String[]> rows) {
// 生成 CSV 文件
return new byte[0];
}

@Override
protected boolean needCompress() { return true; } // 覆盖钩子
}

Java/Spring 中的典型应用

应用模板方法子类/回调
AbstractQueuedSynchronizeracquire/releasetryAcquire/tryRelease
AbstractListiterator()get(int index)
HttpServletservice()doGet()/doPost()
JdbcTemplateexecute()RowMapper/ResultSetExtractor
RedisTemplateexecute()RedisCallback
RestTemplate请求流程ResponseExtractor
JdbcTemplate 中的模板方法
// JdbcTemplate 封装了获取连接、执行SQL、处理异常、关闭资源的流程
// 用户只需提供 SQL 和结果映射逻辑
List<User> users = jdbcTemplate.query(
"SELECT * FROM users WHERE age > ?",
(rs, rowNum) -> new User(rs.getLong("id"), rs.getString("name")), // 回调
18
);
模板方法 vs 策略模式
  • 模板方法:通过继承改变算法的部分步骤,流程固定
  • 策略模式:通过组合替换整个算法
  • JdbcTemplate 使用回调(类似策略模式)代替继承,更灵活

常见面试问题

Q1: AQS 中的模板方法模式?

答案

AbstractQueuedSynchronizer 定义了加锁/解锁的流程(acquire/release),但将具体的锁逻辑(tryAcquire/tryRelease)留给子类实现。

  • ReentrantLock:可重入锁
  • CountDownLatch:倒计数
  • Semaphore:信号量

详见 Lock 接口与 AQS

Q2: 模板方法模式的缺点?

答案

  1. 类爆炸:每个变体一个子类
  2. 继承耦合:子类和父类绑定,父类修改可能影响所有子类
  3. 不够灵活:流程固定,无法在运行时切换

现代 Java 更倾向用回调/Lambda 代替继承(如 JdbcTemplate 的 RowMapper),既保留模板方法的流程控制,又避免继承耦合。

相关链接