设计模式与支付系统:策略模式选择支付方式、工厂模式创建支付对象、状态模式管理支付状态
支付系统,说白了就是跟钱打交道的系统。我做了十几年后端,经手的支付模块少说也有七八个。每次重构支付代码,我都会想起刚入行时写的那一坨if-else——嗯,那代码现在看简直不忍直视。
今天咱们聊聊支付系统里最常用的三种设计模式:策略模式、工厂模式、状态模式。这三个模式配合起来,能把支付系统的代码写得既清晰又灵活。
为什么支付系统需要设计模式?
你想想看,一个支付系统要面对多少种支付方式?微信支付、支付宝、银联、PayPal、信用卡……每种支付方式的接口、参数、签名算法都不一样。如果不用设计模式,代码会变成什么样?
典型反例:
public void pay(String type, double amount) {
if ("wechat".equals(type)) {
// 微信支付逻辑,50行
} else if ("alipay".equals(type)) {
// 支付宝逻辑,50行
} else if ("unionpay".equals(type)) {
// 银联逻辑,50行
}
// 每新增一种支付方式,就要加一个else if
}
我在项目中遇到过这种代码,一个支付方法写了800多行。后来要加PayPal,改代码的人手都在抖——怕改出bug,怕影响线上交易。
设计模式就是来解决这个问题的。咱们一个一个来看。
策略模式:让支付方式可插拔
策略模式的核心思想很简单:把算法(这里就是支付逻辑)封装成独立的策略类,让它们可以互相替换。
我个人习惯先定义策略接口:
// 支付策略接口
public interface PaymentStrategy {
PayResult pay(PayRequest request);
boolean supports(String paymentType);
}
然后每种支付方式实现这个接口:
public class WechatPayStrategy implements PaymentStrategy {
@Override
public PayResult pay(PayRequest request) {
// 调用微信支付SDK
System.out.println("调用微信支付,金额:" + request.getAmount());
return new PayResult(true, "支付成功");
}
@Override
public boolean supports(String paymentType) {
return "wechat".equals(paymentType);
}
}
public class AlipayStrategy implements PaymentStrategy {
@Override
public PayResult pay(PayRequest request) {
// 调用支付宝SDK
System.out.println("调用支付宝,金额:" + request.getAmount());
return new PayResult(true, "支付成功");
}
@Override
public boolean supports(String paymentType) {
return "alipay".equals(paymentType);
}
}
调用方就简单了:
public class PaymentContext {
private List<PaymentStrategy> strategies;
public PaymentContext(List<PaymentStrategy> strategies) {
this.strategies = strategies;
}
public PayResult executePay(String type, double amount) {
PayRequest request = new PayRequest(type, amount);
for (PaymentStrategy strategy : strategies) {
if (strategy.supports(type)) {
return strategy.pay(request);
}
}
throw new UnsupportedOperationException("不支持的支付方式:" + type);
}
}
我的经验:策略模式最爽的地方在于,新增支付方式完全不需要改现有代码。你只需要新建一个类实现接口,然后注册进去就行。这就是开闭原则——对扩展开放,对修改关闭。
工厂模式:统一创建支付对象
策略模式解决了「怎么支付」的问题,但「支付对象从哪来」呢?这就是工厂模式登场的时候了。
我建议用简单工厂来管理支付策略的创建:
public class PaymentStrategyFactory {
private static final Map<String, PaymentStrategy> strategyMap = new HashMap<>();
static {
// 初始化时注册所有策略
registerStrategy("wechat", new WechatPayStrategy());
registerStrategy("alipay", new AlipayStrategy());
registerStrategy("unionpay", new UnionpayStrategy());
}
public static void registerStrategy(String type, PaymentStrategy strategy) {
strategyMap.put(type, strategy);
}
public static PaymentStrategy getStrategy(String type) {
PaymentStrategy strategy = strategyMap.get(type);
if (strategy == null) {
throw new IllegalArgumentException("不支持的支付类型:" + type);
}
return strategy;
}
}
调用方就变成了:
public PayResult pay(String type, double amount) {
PaymentStrategy strategy = PaymentStrategyFactory.getStrategy(type);
return strategy.pay(new PayRequest(type, amount));
}
你看,代码是不是清爽多了?
避坑指南:我曾经在工厂里用反射来动态加载策略类,结果线上出了ClassNotFoundException。后来我改成注册式工厂,在启动时显式注册所有策略。虽然多写几行代码,但稳定多了。工厂模式别玩太花,简单可靠才是王道。
状态模式:管理支付状态的流转
支付不是一锤子买卖。一笔订单要经历:待支付 → 支付中 → 支付成功 / 支付失败 → 退款中 → 已退款……状态多了,if-else就多了。
状态模式就是专门处理这种状态流转的。它把每个状态封装成一个类,状态之间的转换由状态类自己决定。
先看状态接口:
public interface PaymentState {
void pay(PaymentContext context);
void refund(PaymentContext context);
void timeout(PaymentContext context);
String getStateName();
}
然后实现各个状态:
public class PendingState implements PaymentState {
@Override
public void pay(PaymentContext context) {
System.out.println("开始支付...");
// 调用支付接口
boolean success = context.doPay();
if (success) {
context.setState(new SuccessState());
} else {
context.setState(new FailedState());
}
}
@Override
public void refund(PaymentContext context) {
System.out.println("未支付,无法退款");
}
@Override
public void timeout(PaymentContext context) {
System.out.println("订单超时,取消支付");
context.setState(new CancelledState());
}
@Override
public String getStateName() {
return "待支付";
}
}
public class SuccessState implements PaymentState {
@Override
public void pay(PaymentContext context) {
System.out.println("已支付成功,请勿重复支付");
}
@Override
public void refund(PaymentContext context) {
System.out.println("发起退款...");
boolean success = context.doRefund();
if (success) {
context.setState(new RefundingState());
}
}
@Override
public void timeout(PaymentContext context) {
System.out.println("已支付成功,不处理超时");
}
@Override
public String getStateName() {
return "支付成功";
}
}
上下文类持有当前状态:
public class PaymentContext {
private PaymentState currentState;
public PaymentContext() {
this.currentState = new PendingState();
}
public void setState(PaymentState state) {
this.currentState = state;
System.out.println("状态变更:" + state.getStateName());
}
public void pay() {
currentState.pay(this);
}
public void refund() {
currentState.refund(this);
}
public void timeout() {
currentState.timeout(this);
}
// 实际的支付、退款逻辑
public boolean doPay() { return true; }
public boolean doRefund() { return true; }
}
状态模式的核心价值:每个状态类只关心自己状态下的行为逻辑。新增一个状态(比如「部分退款」),只需要新建一个状态类,完全不影响其他状态。状态之间的转换逻辑清晰可见,不会散落在各个if-else里。
三种模式如何协同工作?
在实际的支付系统中,这三种模式是配合使用的。我画了一张图来说明它们的关系:
流程是这样的:
- 用户发起支付请求,传入支付类型(如"wechat")
- 工厂模式根据类型创建对应的支付策略对象
- 策略模式执行具体的支付逻辑(调用微信/支付宝SDK)
- 状态模式管理这笔订单的支付状态(待支付→支付中→成功/失败)
实际项目中的注意事项
| 模式 | 常见坑 | 我的建议 |
|---|---|---|
| 策略模式 | 策略类太多,难以管理 | 用注册式工厂统一管理,不要用反射 |
| 工厂模式 | 工厂越来越臃肿 | 按支付渠道分组,每个组一个子工厂 |
| 状态模式 | 状态转换逻辑分散 | 画状态转换图,每个状态只允许有限转换 |
曾经踩过的坑:有一次我在状态模式里允许了「已退款」状态直接转到「支付成功」,结果线上出现了退款后还能支付成功的bug。后来我加了一个状态转换校验表,每个状态只允许预定义的转换路径。嗯,从那以后我再也不敢让状态随便转了。
总结一下
这三种模式在支付系统里各司其职:
- 策略模式:解决「怎么做」的问题,把支付算法封装成可替换的策略
- 工厂模式:解决「谁来创建」的问题,统一管理支付对象的创建
- 状态模式:解决「状态怎么变」的问题,把状态流转逻辑封装到状态类中
说白了,设计模式就是让代码更好维护、更容易扩展。你想想看,如果不用这些模式,每加一种支付方式就要改好几个文件,每改一个状态就要梳理所有if-else——那代码迟早变成一团乱麻。
我个人习惯在项目初期就引入这些模式。虽然前期多写几个类,但后期维护起来真的省心。记住一句话:写代码的时候多想想「三个月后的自己」能不能看懂。
最后一个小建议:别为了用设计模式而用设计模式。如果你的支付系统只有一种支付方式,那策略模式就是过度设计。模式是工具,不是目的。用对地方才是高手。
公众号:蓝海资料掘金营,微信deep3321