一、模式概述
1.1 定义
**模板方法模式(Template Method Pattern)** 是一种行为型设计模式,它在一个抽象类中定义了一个算法的骨架,将某些步骤的具体实现延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些特定步骤。
1.2 核心思想
**"好莱坞原则"** - "不要打电话给我们,我们会打电话给你"(Don't call us, we'll call you)。父类控制整体流程,子类负责具体实现。
封装不变部分,扩展可变部分:将公共代码提取到父类,通过子类扩展新的实现。
1.3 结构组成
-
抽象类(AbstractClass):定义算法骨架和模板方法
-
具体类(ConcreteClass):实现抽象类中的抽象操作
1.4 关键方法类型
-
模板方法(Template Method):定义算法骨架,通常用final修饰防止子类重写
-
基本方法(Primitive Methods):
-
抽象方法:由子类实现
-
具体方法:父类已实现,子类可选择重写
-
钩子方法:父类提供默认实现,子类可选择重写以影响模板方法行为
-
二、模式结构
// 1. 抽象模板类
public abstract class AbstractTemplate {
// 模板方法:定义算法骨架(通常声明为final,防止子类重写)
public final void templateMethod() {
// 步骤1:固定操作
step1();
// 步骤2:钩子方法(可选步骤)
if (hookMethod()) {
step2();
}
// 步骤3:抽象方法(必须由子类实现)
abstractStep();
// 步骤4:固定操作
step3();
}
// 具体方法:已实现的方法
private void step1() {
System.out.println("执行步骤1");
}
// 具体方法:已实现的方法
protected void step2() {
System.out.println("执行步骤2");
}
private void step3() {
System.out.println("执行步骤3");
}
// 抽象方法:必须由子类实现
protected abstract void abstractStep();
// 钩子方法:提供默认实现,子类可选择重写
protected boolean hookMethod() {
return true;
}
}
// 2. 具体实现类A
public class ConcreteClassA extends AbstractTemplate {
@Override
protected void abstractStep() {
System.out.println("具体类A实现抽象步骤");
}
@Override
protected boolean hookMethod() {
return false; // 选择不执行步骤2
}
}
// 3. 具体实现类B
public class ConcreteClassB extends AbstractTemplate {
@Override
protected void abstractStep() {
System.out.println("具体类B实现抽象步骤");
}
@Override
protected void step2() {
System.out.println("具体类B重写步骤2");
}
}
三、Java基础示例
3.1 制作饮料示例
// 抽象类:饮料制作模板
public abstract class BeverageTemplate {
// 模板方法(final防止子类重写算法骨架)
public final void prepareBeverage() {
boilWater();
brew();
pourInCup();
if (customerWantsCondiments()) {
addCondiments();
}
}
// 抽象方法:由子类实现
protected abstract void brew();
protected abstract void addCondiments();
// 具体方法:公共步骤
private void boilWater() {
System.out.println("烧开水");
}
private void pourInCup() {
System.out.println("倒入杯中");
}
// 钩子方法:子类可选择重写
protected boolean customerWantsCondiments() {
return true;
}
}
// 具体子类:制作咖啡
public class Coffee extends BeverageTemplate {
@Override
protected void brew() {
System.out.println("冲泡咖啡粉");
}
@Override
protected void addCondiments() {
System.out.println("加入糖和牛奶");
}
}
// 具体子类:制作茶
public class Tea extends BeverageTemplate {
@Override
protected void brew() {
System.out.println("冲泡茶叶");
}
@Override
protected void addCondiments() {
System.out.println("加入柠檬");
}
// 重写钩子方法:不加调料
@Override
protected boolean customerWantsCondiments() {
return false;
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
BeverageTemplate coffee = new Coffee();
coffee.prepareBeverage();
// 输出:烧开水、冲泡咖啡粉、倒入杯中、加入糖和牛奶
BeverageTemplate tea = new Tea();
tea.prepareBeverage();
// 输出:烧开水、冲泡茶叶、倒入杯中
}
}
四、Spring框架中的应用
4.1 JdbcTemplate - 经典应用
// Spring的JdbcTemplate是模板方法模式的典型应用
@Repository
public class UserRepository {
@Autowired
private JdbcTemplate jdbcTemplate;
public User findById(Long id) {
String sql = "SELECT * FROM users WHERE id = ?";
return jdbcTemplate.queryForObject(sql, new Object[]{id},
(rs, rowNum) -> {
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));
user.setEmail(rs.getString("email"));
return user;
});
}
}
// 模拟JdbcTemplate的核心结构
public abstract class JdbcTemplate {
public final <T> T execute(ConnectionCallback<T> action) throws DataAccessException {
Connection con = null;
try {
// 固定步骤1:获取连接
con = obtainConnection();
// 固定步骤2:应用连接配置
configureConnection(con);
// 可变步骤:由回调接口实现
return action.doInConnection(con);
} catch (SQLException ex) {
// 固定步骤3:异常处理
throw translateException("ConnectionCallback", ex);
} finally {
// 固定步骤4:释放资源
releaseConnection(con);
}
}
protected abstract Connection obtainConnection() throws SQLException;
protected abstract void releaseConnection(Connection con);
// 其他模板方法...
}
4.2 RestTemplate - HTTP请求模板
// RestTemplate中的模板方法模式
@RestController
public class UserController {
@Autowired
private RestTemplate restTemplate;
public User getUserById(Long id) {
String url = "http://api.example.com/users/{id}";
return restTemplate.getForObject(url, User.class, id);
}
}
// RestTemplate核心结构
public class RestTemplate extends InterceptingHttpAccessor
implements RestOperations {
@Override
public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
@Nullable HttpEntity<?> requestEntity, Class<T> responseType,
Object... uriVariables) throws RestClientException {
// 请求准备(固定步骤)
RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor =
responseEntityExtractor(responseType);
// 执行请求(模板方法)
return execute(url, method, requestCallback, responseExtractor, uriVariables);
}
// 模板方法定义
@Override
public <T> T execute(String url, HttpMethod method,
@Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor,
Object... uriVariables) throws RestClientException {
URI expanded = getUriTemplateHandler().expand(url, uriVariables);
// 执行请求的固定流程
return doExecute(expanded, method, requestCallback, responseExtractor);
}
// 具体实现交给子类
protected abstract <T> T doExecute(URI url, HttpMethod method,
@Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor)
throws RestClientException;
}
4.3 TransactionTemplate - 事务管理
// Spring事务管理的模板方法模式
@Service
public class UserService {
@Autowired
private TransactionTemplate transactionTemplate;
public void updateUser(User user) {
transactionTemplate.execute(status -> {
// 业务逻辑
userRepository.update(user);
return null;
});
}
}
// TransactionTemplate核心结构
public class TransactionTemplate extends DefaultTransactionDefinition
implements TransactionOperations, InitializingBean {
@Override
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
// 事务管理固定流程
TransactionStatus status = this.transactionManager.getTransaction(this);
T result;
try {
// 可变部分:业务逻辑
result = action.doInTransaction(status);
} catch (RuntimeException | Error ex) {
// 回滚(固定步骤)
rollbackOnException(status, ex);
throw ex;
} catch (Throwable ex) {
// 回滚(固定步骤)
rollbackOnException(status, ex);
throw new UndeclaredThrowableException(ex,
"TransactionCallback threw undeclared checked exception");
}
// 提交事务(固定步骤)
this.transactionManager.commit(status);
return result;
}
}
五、实际应用案例
5.1 支付流程模板
// 支付流程的模板方法模式实现
public abstract class PaymentProcessor {
// 模板方法:支付流程
public final PaymentResult processPayment(PaymentRequest request) {
// 1. 验证支付参数
validateRequest(request);
// 2. 检查账户状态
checkAccountStatus(request);
// 3. 执行支付(抽象方法)
PaymentResponse response = executePayment(request);
// 4. 记录日志
logPayment(request, response);
// 5. 发送通知
sendNotification(request, response);
return buildResult(response);
}
// 具体方法
protected void validateRequest(PaymentRequest request) {
if (request.getAmount() == null || request.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("无效的支付金额");
}
// 其他验证逻辑...
}
protected void checkAccountStatus(PaymentRequest request) {
// 检查账户状态逻辑
System.out.println("检查账户状态...");
}
// 抽象方法:不同支付方式有不同的实现
protected abstract PaymentResponse executePayment(PaymentRequest request);
// 钩子方法:默认发送通知,子类可重写
protected void sendNotification(PaymentRequest request, PaymentResponse response) {
System.out.println("发送支付成功通知");
}
protected void logPayment(PaymentRequest request, PaymentResponse response) {
System.out.println("记录支付日志: " + request + ", " + response);
}
protected PaymentResult buildResult(PaymentResponse response) {
return new PaymentResult(response.isSuccess(), response.getMessage());
}
}
// 支付宝支付实现
@Service
public class AlipayProcessor extends PaymentProcessor {
@Override
protected PaymentResponse executePayment(PaymentRequest request) {
System.out.println("使用支付宝支付: " + request.getAmount());
// 调用支付宝API
return new PaymentResponse(true, "支付宝支付成功");
}
@Override
protected void sendNotification(PaymentRequest request, PaymentResponse response) {
// 支付宝特有的通知方式
System.out.println("发送支付宝支付成功通知");
}
}
// 微信支付实现
@Service
public class WechatPayProcessor extends PaymentProcessor {
@Override
protected PaymentResponse executePayment(PaymentRequest request) {
System.out.println("使用微信支付: " + request.getAmount());
// 调用微信支付API
return new PaymentResponse(true, "微信支付成功");
}
}
// 使用示例
@RestController
@RequestMapping("/payment")
public class PaymentController {
@Autowired
private Map<String, PaymentProcessor> paymentProcessors;
@PostMapping("/{type}")
public PaymentResult pay(@PathVariable String type,
@RequestBody PaymentRequest request) {
PaymentProcessor processor = paymentProcessors.get(type + "Processor");
if (processor == null) {
throw new IllegalArgumentException("不支持的支付方式");
}
return processor.processPayment(request);
}
}
5.2 数据导出模板
// 数据导出模板
public abstract class DataExporter<T> {
public final void export(List<T> data, String filePath) throws IOException {
// 1. 准备导出
prepareExport();
try (OutputStream outputStream = new FileOutputStream(filePath);
Writer writer = new OutputStreamWriter(outputStream, "UTF-8")) {
// 2. 写入文件头
writeHeader(writer);
// 3. 写入数据
for (T item : data) {
writeItem(writer, item);
}
// 4. 写入文件尾
writeFooter(writer);
// 5. 导出完成处理
onExportComplete(filePath, data.size());
} catch (Exception e) {
// 6. 异常处理
handleExportError(e);
throw e;
}
}
protected void prepareExport() {
System.out.println("准备导出数据...");
}
protected abstract void writeHeader(Writer writer) throws IOException;
protected abstract void writeItem(Writer writer, T item) throws IOException;
protected abstract void writeFooter(Writer writer) throws IOException;
protected void onExportComplete(String filePath, int recordCount) {
System.out.println("导出完成,文件: " + filePath + ", 记录数: " + recordCount);
}
protected void handleExportError(Exception e) {
System.err.println("导出失败: " + e.getMessage());
}
}
// CSV导出实现
public class CsvExporter<T> extends DataExporter<T> {
private final Function<T, String[]> converter;
public CsvExporter(Function<T, String[]> converter) {
this.converter = converter;
}
@Override
protected void writeHeader(Writer writer) throws IOException {
// CSV不需要特殊头部
}
@Override
protected void writeItem(Writer writer, T item) throws IOException {
String[] fields = converter.apply(item);
String line = String.join(",",
Arrays.stream(fields)
.map(this::escapeCsv)
.toArray(String[]::new));
writer.write(line + "\n");
}
@Override
protected void writeFooter(Writer writer) throws IOException {
// CSV不需要特殊尾部
}
private String escapeCsv(String field) {
if (field.contains(",") || field.contains("\"") || field.contains("\n")) {
return "\"" + field.replace("\"", "\"\"") + "\"";
}
return field;
}
}
六、模式优缺点
6.1 优点
-
代码复用:将不变的行为移到父类,避免代码重复
-
扩展性好:通过子类扩展新的行为,符合开闭原则
-
封装不变部分:将算法骨架固定,细节由子类实现
-
便于维护:算法的修改只需在父类中进行
-
反向控制:父类调用子类操作,实现"好莱坞原则"
6.2 缺点
-
类的数量增加:每个实现都需要一个具体子类
-
骨架固定:限制了子类的灵活性,只能改变某些步骤
-
可能违反里氏替换原则:如果子类修改了模板方法的步骤顺序
6.3 适用场景
-
多个类有相同的方法,但具体实现不同
-
需要控制子类的扩展点
-
重要、复杂的算法,希望将核心逻辑与具体实现分离
-
重构时发现多个类有相似的流程
-
多个子类有公共行为,但某些步骤实现不同
-
框架设计:定义框架骨架,让用户实现具体业务逻辑
七、最佳实践
7.1 Spring中的最佳实践
// 1. 使用Callback接口作为可变部分
@FunctionalInterface
public interface TaskCallback<T> {
T doInTask();
}
// 2. 模板类
@Component
public class RetryableTemplate {
public <T> T executeWithRetry(Supplier<T> task, int maxRetries) {
int retryCount = 0;
while (retryCount <= maxRetries) {
try {
return task.get();
} catch (Exception e) {
retryCount++;
if (retryCount > maxRetries) {
throw e;
}
waitForRetry(retryCount);
}
}
throw new IllegalStateException("不应该执行到这里");
}
private void waitForRetry(int retryCount) {
try {
Thread.sleep(1000L * retryCount);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
// 3. 使用示例
@Service
public class OrderService {
@Autowired
private RetryableTemplate retryableTemplate;
@Autowired
private OrderRepository orderRepository;
public Order createOrder(Order order) {
return retryableTemplate.executeWithRetry(
() -> orderRepository.save(order),
3
);
}
}
7.2 结合策略模式
// 模板方法模式 + 策略模式
public abstract class ReportGenerator {
private ExportStrategy exportStrategy;
public final void generateAndExport() {
// 1. 准备数据
List<ReportData> data = prepareData();
// 2. 生成报告
String report = generateReport(data);
// 3. 使用策略导出
exportStrategy.export(report);
// 4. 清理资源
cleanup();
}
protected abstract List<ReportData> prepareData();
protected abstract String generateReport(List<ReportData> data);
protected abstract void cleanup();
public void setExportStrategy(ExportStrategy exportStrategy) {
this.exportStrategy = exportStrategy;
}
}
// 策略接口
public interface ExportStrategy {
void export(String content);
}
7.3 注意事项
-
避免过度设计:如果子类数量很少或变化不大,不要滥用模板方法模式
-
控制抽象层级:模板方法中的步骤不宜过多,否则维护困难
-
命名规范:模板方法通常以"do"、"execute"、"process"等动词开头
-
钩子方法使用:合理使用钩子方法,避免子类被迫实现不需要的方法
-
识别变化点:当算法的变化点过多时,可能需要考虑其他模式如策略模式
八、总结
模板方法模式是Spring框架中广泛应用的核心模式之一,它体现了"好莱坞原则"的思想。通过将算法骨架固定,将具体实现延迟到子类,实现了代码复用和扩展性的平衡。
在Spring中,JdbcTemplate、RestTemplate、TransactionTemplate等都是模板方法模式的经典实现。在实际开发中,合理使用模板方法模式可以有效减少重复代码,提高系统可维护性,特别是在处理具有固定流程但具体实现不同的业务场景时。
正确识别和应用模板方法模式,可以让代码结构更加清晰,维护更加容易。同时要注意避免过度设计,根据实际业务需求选择合适的模式组合,如结合策略模式等,以构建更加灵活、可扩展的系统架构。

公众号同步,欢迎关注!