在业务系统开发中,我们经常会遇到这样的场景:多个业务流程的骨架结构高度相似,但某些具体步骤的实现却各不相同。以支付系统为例,无论是微信支付、支付宝支付还是银联支付,其核心流程都可以抽象为"校验订单→调用支付渠道→处理结果"这三个步骤。如果为每种支付方式都编写一套完整代码,不仅会产生大量重复代码,还会导致流程不统一、维护困难等问题。
模板方法模式正是为解决这类问题而生。它通过抽象类定义算法骨架(模板方法),将可变步骤延迟到子类实现,既保证了流程的统一性,又保留了具体实现的灵活性。在SpringBoot项目中,这种模式尤其适合以下场景:
实际项目经验表明,合理使用模板方法模式可以减少40%以上的重复代码量,同时使核心业务流程的维护和扩展变得更加简单可控。
模板方法模式的核心在于"抽象定义骨架,具体实现细节"。其典型结构包含以下关键组件:
抽象模板类(AbstractClass):
具体子类(ConcreteClass):
在SpringBoot环境下实现模板方法模式时,需要注意以下几点:
依赖注入支持:
模板方法可见性:
异常处理策略:
java复制/**
* 支付流程抽象模板
* 使用@Component确保能被Spring管理
*/
@Slf4j
@Component
public abstract class PaymentAbstractTemplate {
/**
* 模板方法 - 定义支付流程骨架
* @param orderId 订单ID
* @param amount 支付金额
* @return 统一格式的支付结果
*/
public final PaymentResult executePayment(String orderId, BigDecimal amount) {
// 步骤1:前置校验(固定)
validateParameters(orderId, amount);
// 步骤2:执行支付(可变)
String channelResponse = processPayment(orderId, amount);
// 步骤3:后置处理(固定)
handlePostPayment(orderId, channelResponse);
// 步骤4:返回结果(固定)
return buildPaymentResult(orderId, channelResponse);
}
// 固定步骤实现
private void validateParameters(String orderId, BigDecimal amount) {
if (StringUtils.isEmpty(orderId) || amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Invalid payment parameters");
}
log.info("Payment validation passed for order: {}", orderId);
}
// 抽象方法 - 由子类实现
protected abstract String processPayment(String orderId, BigDecimal amount);
// 固定步骤实现
private void handlePostPayment(String orderId, String response) {
log.info("Processing payment result for order: {}", orderId);
// 实际项目中会更新订单状态、记录支付日志等
}
// 结果构建方法
private PaymentResult buildPaymentResult(String orderId, String response) {
return PaymentResult.success(orderId, response);
}
// 钩子方法示例
protected boolean needPaymentConfirm() {
return false;
}
}
java复制/**
* 微信支付实现
*/
@Service
public class WechatPayment extends PaymentAbstractTemplate {
@Override
protected String processPayment(String orderId, BigDecimal amount) {
log.info("Processing WeChat payment for order: {}", orderId);
// 实际调用微信支付API
return "WeChat_Payment_Success";
}
// 重写钩子方法
@Override
protected boolean needPaymentConfirm() {
return true;
}
}
/**
* 支付宝支付实现
*/
@Service
public class Alipayment extends PaymentAbstractTemplate {
@Override
protected String processPayment(String orderId, BigDecimal amount) {
log.info("Processing Ali payment for order: {}", orderId);
// 实际调用支付宝API
return "Ali_Payment_Success";
}
}
java复制@RestController
@RequestMapping("/payments")
public class PaymentController {
@Autowired
private Map<String, PaymentAbstractTemplate> paymentStrategies;
@PostMapping("/{channel}")
public ResponseEntity<PaymentResult> makePayment(
@PathVariable String channel,
@RequestBody PaymentRequest request) {
PaymentAbstractTemplate processor = paymentStrategies.get(channel + "Payment");
if (processor == null) {
throw new UnsupportedOperationException("Unsupported payment channel");
}
PaymentResult result = processor.executePayment(
request.getOrderId(),
request.getAmount()
);
return ResponseEntity.ok(result);
}
}
钩子方法(Hook Method)为模板方法模式提供了额外的灵活性。它们是在抽象类中声明并提供默认实现的方法,子类可以选择性地覆盖这些方法以改变模板的行为。常见的应用场景包括:
java复制protected boolean shouldLogDetails() {
return false;
}
java复制protected void preProcess() {
// 默认空实现
}
protected void postProcess() {
// 默认空实现
}
java复制protected Map<String, String> prepareHeaders() {
return Collections.emptyMap();
}
在多线程环境下使用模板方法模式时,需要注意:
无状态设计:
同步控制:
java复制public final synchronized PaymentResult executePayment(...) {
// 线程安全的模板方法
}
java复制@Retryable(maxAttempts = 3, backoff = @Backoff(delay = 1000))
protected abstract String processPayment(...);
java复制@Transactional
public final PaymentResult executePayment(...) {
// 包含数据库操作的流程
}
java复制@Validated
public abstract class PaymentAbstractTemplate {
protected abstract String processPayment(
@NotBlank String orderId,
@DecimalMin("0.01") BigDecimal amount
);
}
虽然两者都用于算法封装,但存在本质区别:
| 特性 | 模板方法模式 | 策略模式 |
|---|---|---|
| 实现方式 | 继承 | 组合 |
| 算法结构 | 固定骨架 | 完全可变 |
| 运行时变化 | 编译时确定 | 运行时动态切换 |
| 适用场景 | 流程固定步骤可变 | 完全不同的算法实现 |
子类无法注入问题:
流程步骤被修改:
循环依赖问题:
java复制private final Map<String, PaymentResult> cache = new ConcurrentHashMap<>();
public final PaymentResult executePayment(...) {
return cache.computeIfAbsent(orderId, id -> doExecutePayment(id, amount));
}
java复制@Async
public CompletableFuture<PaymentResult> executePaymentAsync(...) {
return CompletableFuture.completedFuture(executePayment(orderId, amount));
}
java复制public final List<PaymentResult> batchExecute(List<PaymentRequest> requests) {
return requests.stream()
.map(req -> executePayment(req.getOrderId(), req.getAmount()))
.collect(Collectors.toList());
}
在金融支付系统的开发实践中,我们发现模板方法模式特别适合处理以下场景:
多渠道支付网关:
订单状态机:
报表生成引擎:
特别需要注意的是,随着业务复杂度的增加,可以考虑将模板方法模式与工厂方法模式结合使用,通过支付工厂来创建具体的模板实例,进一步解耦系统组件。同时,对于特别复杂的流程,可以引入责任链模式来拆分模板方法的各个步骤,使每个步骤的职责更加单一明确。