SpringBoot中模板方法模式的实现方式
·
在实际业务开发中,很多业务流程的整体骨架是固定不变的,但其中某一步或某几步的具体实现会不一样。
比如:
- 支付流程:校验订单 → 调用渠道 → 处理结果
- 审核流程:提交 → 校验 → 审核 → 通知
- 导出流程:查询数据 → 生成文件 → 上传 → 通知
如果每个实现都写一套完整代码:
- 大量重复代码(校验、日志、异常、结果处理)
- 流程不统一,容易写错、漏步骤
- 维护成本极高,改一次流程要改所有实现
模板方法模式就是为了解决这个问题:
- 父类定义固定流程骨架(模板方法)
- 子类只实现变化的那一步
- 流程统一、代码复用、易扩展、易维护
定义
- 模板方法模式属于行为型设计模式,核心是:在一个抽象类中定义一个算法的骨架(模板),将算法中可变的步骤延迟到子类中实现。这样可以保证算法的整体结构不变,同时让具体细节可以灵活定制。
步骤1:定义抽象类
/**
* 支付模板抽象类(定义支付流程的骨架)
* 真实项目中会放在 service/abstract 包下
*/
@Slf4j
@Component
public abstract class PaymentAbstractTemplate {
/**
* 模板方法:定义支付的整体流程(final 防止子类重写,保证流程不变)
* @param orderId 订单ID
* @param amount 支付金额
* @return 支付结果(统一返回格式)
*/
public final PaymentResult pay(String orderId, BigDecimal amount) {
try {
// 步骤1:校验订单(固定逻辑)
boolean checkResult = checkOrder(orderId, amount);
if (!checkResult) {
return PaymentResult.fail("订单校验失败");
}
// 步骤2:调用具体支付渠道(可变逻辑,子类实现)
String payResponse = callPaymentChannel(orderId, amount);
// 步骤3:处理支付结果(固定逻辑)
boolean handleResult = handlePayResult(orderId, payResponse);
if (!handleResult) {
return PaymentResult.fail("支付结果处理失败");
}
// 步骤4:返回统一结果
return PaymentResult.success("支付成功", payResponse);
} catch (Exception e) {
log.error("支付异常,订单ID:{}", orderId, e);
return PaymentResult.fail("支付异常:" + e.getMessage());
}
}
/**
* 固定步骤:校验订单
*/
private boolean checkOrder(String orderId, BigDecimal amount) {
// 真实项目中会查询数据库校验订单状态、金额、用户是否匹配等
log.info("校验订单,订单ID:{},金额:{}", orderId, amount);
if (orderId == null || orderId.isEmpty() || amount.compareTo(BigDecimal.ZERO) <= 0) {
return false;
}
return true;
}
/**
* 可变步骤:调用具体支付渠道(抽象方法,子类实现)
*/
protected abstract String callPaymentChannel(String orderId, BigDecimal amount);
/**
* 固定步骤:处理支付结果
*/
private boolean handlePayResult(String orderId, String payResponse) {
// 真实项目中会更新订单状态为“已支付”、记录支付日志、扣减库存等
log.info("处理支付结果,订单ID:{},支付响应:{}", orderId, payResponse);
if (payResponse == null || payResponse.isEmpty()) {
return false;
}
return true;
}
/**
* 支付结果封装类(真实项目中会单独定义,这里简化)
*/
@Data
@AllArgsConstructor
public static class PaymentResult {
private boolean success;
private String message;
private String data;
public static PaymentResult success(String message, String data) {
return new PaymentResult(true, message, data);
}
public static PaymentResult fail(String message) {
return new PaymentResult(false, message, null);
}
}
}
步骤2:实现具体子类
/**
* 微信支付实现类(具体支付渠道)
*/
@Slf4j
@Service
public class WxPayTemplate extends PaymentAbstractTemplate {
@Override
protected String callPaymentChannel(String orderId, BigDecimal amount) {
// 真实项目中会调用微信支付的SDK/API,比如统一下单接口
log.info("调用微信支付接口,订单ID:{},金额:{}", orderId, amount);
// 模拟微信支付API调用(真实场景会拼接参数、签名、调用HTTP接口)
String prepayId = "wx" + System.currentTimeMillis(); // 模拟预支付ID
String wxResponse = String.format("{\"out_trade_no\":\"%s\",\"total_fee\":%d,\"prepay_id\":\"%s\",\"return_code\":\"SUCCESS\"}",
orderId, amount.multiply(new BigDecimal(100)).intValue(), prepayId);
log.info("【微信支付】接口返回结果:{}", wxResponse);
return wxResponse;
}
}
/**
* 支付宝支付实现类(具体支付渠道)
*/
@Slf4j
@Service
public class AliPayTemplate extends PaymentAbstractTemplate {
@Override
protected String callPaymentChannel(String orderId, BigDecimal amount) {
// 真实项目中会调用支付宝支付的SDK/API,比如创建交易接口
log.info("调用支付宝支付接口,订单ID:{},金额:{}", orderId, amount);
// 模拟支付宝支付API调用
String tradeNo = "ali" + System.currentTimeMillis(); // 模拟支付宝交易号
String aliResponse = String.format("{\"out_trade_no\":\"%s\",\"total_amount\":\"%s\",\"trade_no\":\"%s\",\"code\":\"10000\"}",
orderId, amount, tradeNo);
log.info("【支付宝支付】接口返回结果:{}", aliResponse);
return aliResponse;
}
}
步骤3:定义controller
@Slf4j
@RestController
@RequestMapping("/api/pay")
public class PaymentController {
@Resource
private Map<String, PaymentAbstractTemplate> paymentTemplateMap;
/**
* 统一支付接口
* @param payRequestDTO 支付请求参数
* {
* "orderId": "ORDER_20260305001",
* "amount": 99.99,
* "payType": "wxpay"
* }
* @return 统一返回结果
*/
@PostMapping("/unifiedPay")
public String unifiedPay(@RequestBody PayRequestDTO payRequestDTO) {
try {
log.info("接收支付请求,参数:{}", payRequestDTO);
// 1. 校验支付类型
String payType = payRequestDTO.getPayType();
PaymentAbstractTemplate paymentTemplate;
if ("wxpay".equals(payType)) {
paymentTemplate = paymentTemplateMap.get("wxPayTemplate");
} else if ("alipay".equals(payType)) {
paymentTemplate = paymentTemplateMap.get("aliPayTemplate");
} else {
return "不支持的支付类型:" + payType;
}
// 2. 调用模板方法执行支付
PaymentAbstractTemplate.PaymentResult payResult =
paymentTemplate.pay(payRequestDTO.getOrderId(), payRequestDTO.getAmount());
// 3. 返回结果
if (payResult.isSuccess()) {
return payResult.toString();
} else {
return payResult.getMessage();
}
} catch (Exception e) {
log.error("支付接口异常", e);
return "支付接口异常:" + e.getMessage();
}
}
}
扩展
- 钩子方法(Hook Method):如果流程中某些步骤是 “可选” 的(比如部分支付渠道需要签名验证,部分不需要),可以在抽象类中定义默认实现的钩子方法,子类可选择重写。例如:
// 钩子方法:是否需要签名验证(默认需要)
protected boolean needSign() {
return true;
}
更多推荐

所有评论(0)