我来分享一个在电商系统中非常实用的策略模式应用场景:多种支付方式的选择与扩展。
场景描述
在电商系统中,用户可以选择不同的支付方式(微信支付、支付宝、银行卡支付等),每种支付方式有不同的处理逻辑,但对外提供统一的支付接口。
实现方案
1. 定义支付策略接口
java
public interface PaymentStrategy {
/**
* 支付
* @param amount 支付金额
* @param paymentRequest 支付请求参数
* @return 支付结果
*/
PaymentResult pay(BigDecimal amount, PaymentRequest paymentRequest);
/**
* 支持的支付类型
*/
PaymentType getPaymentType();
}
// 支付类型枚举
public enum PaymentType {
WECHAT_PAY("微信支付"),
ALIPAY("支付宝"),
BANK_CARD("银行卡支付"),
WALLET("钱包支付");
private final String description;
PaymentType(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
2. 实现具体的支付策略
微信支付策略:
java
@Component
public class WechatPayStrategy implements PaymentStrategy {
@Autowired
private WechatPayService wechatPayService;
@Override
public PaymentResult pay(BigDecimal amount, PaymentRequest paymentRequest) {
// 微信支付的具体逻辑
WechatPayRequest wechatRequest = buildWechatRequest(amount, paymentRequest);
return wechatPayService.pay(wechatRequest);
}
@Override
public PaymentType getPaymentType() {
return PaymentType.WECHAT_PAY;
}
private WechatPayRequest buildWechatRequest(BigDecimal amount, PaymentRequest request) {
// 构建微信支付请求参数
return new WechatPayRequest(amount, request.getOpenId(), request.getOrderNo());
}
}
支付宝策略:
java
@Component
public class AlipayStrategy implements PaymentStrategy {
@Autowired
private AlipayService alipayService;
@Override
public PaymentResult pay(BigDecimal amount, PaymentRequest paymentRequest) {
// 支付宝的具体逻辑
AlipayRequest alipayRequest = buildAlipayRequest(amount, paymentRequest);
return alipayService.pay(alipayRequest);
}
@Override
public PaymentType getPaymentType() {
return PaymentType.ALIPAY;
}
private AlipayRequest buildAlipayRequest(BigDecimal amount, PaymentRequest request) {
return new AlipayRequest(amount, request.getUserId(), request.getOrderNo());
}
}
银行卡支付策略:
java
@Component
public class BankCardPayStrategy implements PaymentStrategy {
@Autowired
private BankCardService bankCardService;
@Override
public PaymentResult pay(BigDecimal amount, PaymentRequest paymentRequest) {
// 银行卡支付的具体逻辑
BankCardRequest bankRequest = buildBankCardRequest(amount, paymentRequest);
return bankCardService.pay(bankRequest);
}
@Override
public PaymentType getPaymentType() {
return PaymentType.BANK_CARD;
}
private BankCardRequest buildBankCardRequest(BigDecimal amount, PaymentRequest request) {
return new BankCardRequest(amount, request.getCardNo(),
request.getCvv(), request.getExpireDate());
}
}
3. 策略工厂管理所有策略
java
@Component
public class PaymentStrategyFactory {
private final Map<PaymentType, PaymentStrategy> strategyMap;
@Autowired
public PaymentStrategyFactory(List<PaymentStrategy> strategies) {
strategyMap = strategies.stream()
.collect(Collectors.toMap(
PaymentStrategy::getPaymentType,
Function.identity()
));
}
/**
* 根据支付类型获取对应的策略
*/
public PaymentStrategy getStrategy(PaymentType paymentType) {
PaymentStrategy strategy = strategyMap.get(paymentType);
if (strategy == null) {
throw new IllegalArgumentException("不支持的支付类型: " + paymentType);
}
return strategy;
}
/**
* 获取所有支持的支付类型
*/
public List<PaymentType> getSupportedPaymentTypes() {
return new ArrayList<>(strategyMap.keySet());
}
}
4. 支付服务类(上下文)
java
@Service
public class PaymentService {
@Autowired
private PaymentStrategyFactory strategyFactory;
@Autowired
private OrderService orderService;
@Autowired
private PaymentRecordService paymentRecordService;
/**
* 执行支付
*/
public PaymentResult executePayment(PaymentRequest request) {
// 1. 验证订单状态
Order order = orderService.validateOrder(request.getOrderNo());
// 2. 根据支付类型获取对应的策略
PaymentStrategy strategy = strategyFactory.getStrategy(request.getPaymentType());
// 3. 执行支付
PaymentResult result = strategy.pay(order.getAmount(), request);
// 4. 记录支付结果
paymentRecordService.recordPayment(request, result);
return result;
}
/**
* 获取支持的支付方式列表
*/
public List<PaymentType> getSupportedPayments() {
return strategyFactory.getSupportedPaymentTypes();
}
}
5. 控制器层
java
@RestController
@RequestMapping("/api/payment")
public class PaymentController {
@Autowired
private PaymentService paymentService;
/**
* 发起支付
*/
@PostMapping("/pay")
public ResponseEntity<PaymentResult> pay(@RequestBody @Valid PaymentRequest request) {
PaymentResult result = paymentService.executePayment(request);
return ResponseEntity.ok(result);
}
/**
* 获取支持的支付方式
*/
@GetMapping("/supported-methods")
public ResponseEntity<List<PaymentType>> getSupportedPayments() {
List<PaymentType> paymentTypes = paymentService.getSupportedPayments();
return ResponseEntity.ok(paymentTypes);
}
}
6. 相关DTO定义
java
// 支付请求参数
@Data
public class PaymentRequest {
@NotBlank
private String orderNo;
@NotNull
private PaymentType paymentType;
private String openId; // 微信支付需要
private String userId; // 支付宝需要
private String cardNo; // 银行卡支付需要
private String cvv; // 银行卡支付需要
private String expireDate; // 银行卡支付需要
}
// 支付结果
@Data
public class PaymentResult {
private boolean success;
private String transactionId;
private String message;
private BigDecimal amount;
private LocalDateTime payTime;
}
策略模式的优势
- 开闭原则:新增支付方式时,只需新增一个策略实现类,无需修改现有代码
- 单一职责:每种支付逻辑独立封装在自己的策略类中
- 易于测试:可以单独测试每个策略
问题:
@Autowired
public PaymentStrategyFactory(List<PaymentStrategy> strategies) {
strategyMap = strategies.stream()
.collect(Collectors.toMap(
PaymentStrategy::getPaymentType,
Function.identity()
));
}
哪里传递参数了
您指出了一个很好的问题!这里确实没有显式传递参数,让我解释一下Spring是如何工作的:
Spring的依赖注入机制
在Spring中,当使用@Autowired注解构造函数时,Spring会自动从IoC容器中查找所有匹配类型的Bean,并作为参数注入。
具体过程:
-
自动收集所有实现:Spring会自动找到所有实现了
PaymentStrategy接口的BeanWechatPayStrategy(被@Component标注)AlipayStrategy(被@Component标注)BankCardPayStrategy(被@Component标注)
-
构建List:Spring将这些Bean组装成一个
List<PaymentStrategy> -
注入构造函数:Spring调用构造函数时自动传递这个List
等价的手动写法:
java
// Spring在底层做的类似于:
List<PaymentStrategy> strategies = Arrays.asList(
new WechatPayStrategy(),
new AlipayStrategy(),
new BankCardPayStrategy()
);
PaymentStrategyFactory factory = new PaymentStrategyFactory(strategies);
验证这个机制:
您可以添加一个调试方法来验证:
java
@Component
public class PaymentStrategyFactory {
private final Map<PaymentType, PaymentStrategy> strategyMap;
@Autowired
public PaymentStrategyFactory(List<PaymentStrategy> strategies) {
// 打印看看Spring自动注入了哪些策略
System.out.println("Spring自动注入的策略列表:");
strategies.forEach(strategy ->
System.out.println("- " + strategy.getClass().getSimpleName())
);
strategyMap = strategies.stream()
.collect(Collectors.toMap(
PaymentStrategy::getPaymentType,
Function.identity()
));
}
// 其他方法...
}
运行时会输出:
text
Spring自动注入的策略列表:
- WechatPayStrategy
- AlipayStrategy
- BankCardPayStrategy
如果还是不清楚,可以显式配置:
java
@Configuration
public class PaymentConfig {
@Bean
@Autowired
public PaymentStrategyFactory paymentStrategyFactory(
WechatPayStrategy wechatPayStrategy,
AlipayStrategy alipayStrategy,
BankCardPayStrategy bankCardPayStrategy) {
List<PaymentStrategy> strategies = Arrays.asList(
wechatPayStrategy,
alipayStrategy,
bankCardPayStrategy
);
return new PaymentStrategyFactory(strategies);
}
}
但第一种方式更简洁,是Spring推荐的写法。
总结
Spring的@Autowired + 构造函数参数机制会自动:
- 扫描所有
PaymentStrategy实现类 - 实例化并注册到IoC容器
- 组装成List注入到构造函数
这就是为什么我们不需要显式传递参数的原因!