目录
一、业务背景
二、整体架构分层
三、完整实战代码(SpringBoot + Java)
1. 渠道编码常量(统一标识渠道)
2. 统一入参 / 出参 DTO(屏蔽渠道差异)
3. 策略顶层接口 IPayChannel(策略模式核心抽象)
4. 各渠道具体策略实现(ConcreteStrategy)
4.1 微信支付实现
4.2 支付宝支付实现
4.3 银联支付实现
5. 渠道工厂 PayChannelFactory(路由核心:渠道编码匹配实现类)
6. 支付上下文服务 PayService(对外统一业务层)
7. Controller 测试入口
四、测试效果
请求 1:微信支付
请求 2:支付宝支付
五、策略模式核心优势(支付系统落地价值)
1. 完美符合开闭原则
2. 消除巨型 if-else 分支
3. 职责单一,代码隔离
4. 易于扩展通用能力
5. 支持动态切换渠道
六、生产环境进阶优化方案
1. 渠道配置抽离(数据库动态配置)
2. 增加渠道开关控制
3. 渠道缓存 + 懒加载
4. 统一异常捕获
5. 结合模板方法模式优化重复逻辑
七、模式角色对应总结
八、对比 if-else 方案的致命缺陷
一、业务背景
支付系统对接多家支付渠道:微信支付、支付宝、银联、云闪付、抖音支付等。 每家渠道的下单、退款、查询订单、回调验签逻辑完全不同:
- 支付宝:RSA2 签名、公钥验签、统一收单接口
- 微信支付:V3 证书、AES 加解密、商户号 + 子商户
- 银联:国密 SM2、报文 XML 格式 如果用大量
if/else if(channelCode == "WECHAT")硬编码:
- 新增渠道要改核心支付服务,违反开闭原则
- 代码臃肿、分支几十条,维护困难
- 单元测试、渠道单独灰度发布无法隔离
解决方案:策略模式
- 策略接口:统一支付操作标准
- 具体策略:每个渠道单独实现一套逻辑
- 策略工厂:根据渠道编码
channelCode匹配对应实现类 - 上下文服务:对外统一入口,屏蔽渠道差异
二、整体架构分层
支付入口 Controller ↓ PayService(上下文,调用工厂获取策略) ↓ PayChannelFactory 渠道工厂(核心路由:channelCode → 实现类) ↓ IPayChannel 策略顶层接口(定义统一支付能力) ├─ WechatPayChannel 微信策略实现 ├─ AlipayChannel 支付宝策略实现 ├─ UnionPayChannel 银联策略实现 └─ 新增支付渠道只新增实现类,不改动原有业务代码三、完整实战代码(SpringBoot + Java)
1. 渠道编码常量(统一标识渠道)
/** * 支付渠道编码,工厂路由唯一key */ public class PayChannelCode { // 微信支付 public static final String WECHAT_PAY = "WECHAT_PAY"; // 支付宝 public static final String ALIPAY = "ALIPAY"; // 银联支付 public static final String UNION_PAY = "UNION_PAY"; }2. 统一入参 / 出参 DTO(屏蔽渠道差异)
/** * 统一支付下单请求 */ @Data public class PayOrderDTO { // 渠道编码,工厂路由核心字段 private String channelCode; // 商户订单号 private String outTradeNo; // 支付金额 分 private Long amount; // 商品描述 private String subject; // 支付回调地址 private String notifyUrl; } /** * 统一支付返回结果 */ @Data public class PayResultVO { // 支付跳转链接/二维码/小程序支付参数 private String payInfo; // 渠道交易号 private String channelTradeNo; // 是否成功 private Boolean success; }3. 策略顶层接口 IPayChannel(策略模式核心抽象)
所有支付渠道必须实现该接口,定义统一支付行为
/** * 支付渠道策略接口 * 所有第三方支付渠道实现该接口,统一行为标准 */ public interface IPayChannel { /** * 标识当前渠道编码,工厂根据该值匹配实现类 * @return 渠道编码:WECHAT_PAY / ALIPAY ... */ String getChannelCode(); /** * 统一创建支付单 */ PayResultVO createPayOrder(PayOrderDTO dto); /** * 统一订单退款 */ default void refund() { // 可选默认实现,部分渠道逻辑通用 } /** * 支付回调验签 */ String verifyNotify(String notifyData); }4. 各渠道具体策略实现(ConcreteStrategy)
4.1 微信支付实现
@Component public class WechatPayChannel implements IPayChannel { // 标记当前渠道编码,工厂路由依据 @Override public String getChannelCode() { return PayChannelCode.WECHAT_PAY; } @Override public PayResultVO createPayOrder(PayOrderDTO dto) { System.out.println("=== 执行微信支付下单逻辑 ==="); // 1. 读取微信商户配置(证书、商户号、APIv3密钥) // 2. 组装微信V3请求报文 // 3. 发送http请求调用微信统一下单接口 // 4. 封装小程序/JSAPI支付参数返回 PayResultVO vo = new PayResultVO(); vo.setSuccess(true); vo.setPayInfo("微信支付JSAPI参数json"); vo.setChannelTradeNo("420000288888123456"); return vo; } @Override public String verifyNotify(String notifyData) { // 微信V3 AES解密、证书验签逻辑 return "微信回调验签通过"; } }4.2 支付宝支付实现
@Component public class AlipayChannel implements IPayChannel { @Override public String getChannelCode() { return PayChannelCode.ALIPAY; } @Override public PayResultVO createPayOrder(PayOrderDTO dto) { System.out.println("=== 执行支付宝支付下单逻辑 ==="); // 1. 读取支付宝appId、私钥、支付宝公钥 // 2. RSA2签名组装请求参数 // 3. 调用alipay.trade.page.pay PayResultVO vo = new PayResultVO(); vo.setSuccess(true); vo.setPayInfo("支付宝支付跳转url"); vo.setChannelTradeNo("2026061722001404981412345678"); return vo; } @Override public String verifyNotify(String notifyData) { // 支付宝公钥验签逻辑 return "支付宝回调验签通过"; } }4.3 银联支付实现
@Component public class UnionPayChannel implements IPayChannel { @Override public String getChannelCode() { return PayChannelCode.UNION_PAY; } @Override public PayResultVO createPayOrder(PayOrderDTO dto) { System.out.println("=== 执行银联支付下单逻辑 ==="); // 国密SM2签名、组装XML报文、调用银联网关 PayResultVO vo = new PayResultVO(); vo.setSuccess(true); vo.setPayInfo("银联收银台地址"); vo.setChannelTradeNo("8888001122334455"); return vo; } @Override public String verifyNotify(String notifyData) { return "银联回调验签通过"; } }5. 渠道工厂 PayChannelFactory(路由核心:渠道编码匹配实现类)
核心作用:启动时加载所有IPayChannel实现,存入 Map,key=channelCode,运行时根据编码快速获取对应策略
import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.util.HashMap; import java.util.List; import java.util.Map; @Component public class PayChannelFactory { // Spring自动注入所有实现IPayChannel的Bean @Resource private List<IPayChannel> payChannelList; // 路由缓存Map:key=渠道编码,value=渠道策略实现类 private final Map<String, IPayChannel> channelMap = new HashMap<>(); /** * 项目启动完成后初始化渠道映射关系 */ @PostConstruct public void initChannelMap() { for (IPayChannel channel : payChannelList) { String code = channel.getChannelCode(); // 防重复渠道编码 if (channelMap.containsKey(code)) { throw new RuntimeException("渠道编码重复:" + code); } channelMap.put(code, channel); } System.out.println("支付渠道工厂初始化完成,已加载渠道:" + channelMap.keySet()); } /** * 根据渠道编码获取对应支付策略实现(核心路由方法) * @param channelCode 渠道编码 * @return 渠道实现类 */ public IPayChannel getPayChannel(String channelCode) { IPayChannel payChannel = channelMap.get(channelCode); if (payChannel == null) { throw new RuntimeException("不存在该支付渠道:" + channelCode); } return payChannel; } }6. 支付上下文服务 PayService(对外统一业务层)
上层业务只依赖 Service,不需要关心底层渠道实现,完全解耦
import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class PayService { @Resource private PayChannelFactory channelFactory; /** * 统一创建支付订单入口 * 只传入渠道编码,内部自动路由对应渠道实现 */ public PayResultVO createPayOrder(PayOrderDTO dto) { // 1. 根据渠道编码路由到对应支付策略 IPayChannel payChannel = channelFactory.getPayChannel(dto.getChannelCode()); // 2. 调用对应渠道的下单方法 return payChannel.createPayOrder(dto); } /** * 支付回调统一处理 */ public String handleNotify(String channelCode, String notifyData) { IPayChannel payChannel = channelFactory.getPayChannel(channelCode); return payChannel.verifyNotify(notifyData); } }7. Controller 测试入口
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; @RestController @RequestMapping("/pay") public class PayController { @Resource private PayService payService; @PostMapping("/create") public PayResultVO createPay(@RequestBody PayOrderDTO dto) { return payService.createPayOrder(dto); } }四、测试效果
请求 1:微信支付
POST /pay/create { "channelCode": "WECHAT_PAY", "outTradeNo": "ORDER20260617001", "amount": 9900, "subject": "商品支付", "notifyUrl": "https://xxx.com/pay/notify" }控制台输出:=== 执行微信支付下单逻辑 ===
请求 2:支付宝支付
{ "channelCode": "ALIPAY", "outTradeNo": "ORDER20260617002", "amount": 19900, "subject": "会员充值", "notifyUrl": "https://xxx.com/pay/notify" }控制台输出:=== 执行支付宝支付下单逻辑 ===
五、策略模式核心优势(支付系统落地价值)
1. 完美符合开闭原则
新增渠道(如抖音支付、京东支付)只需要:
- 新增常量
DOUYIN_PAY - 新建
DouyinPayChannel实现IPayChannel - 实现所有接口方法不需要修改工厂、PayService、Controller 任何一行代码
2. 消除巨型 if-else 分支
传统写法随着渠道增多,分支会膨胀到几十行,可读性极差; 策略模式通过 Map 编码映射,时间复杂度 O (1) 路由渠道,性能更高。
3. 职责单一,代码隔离
每个渠道的签名、报文、异常、配置全部隔离在自己实现类, 微信出问题不会影响支付宝逻辑,便于单独排查、单元测试、灰度切换。
4. 易于扩展通用能力
接口可以新增统一方法(如查询订单、关闭订单、转账),所有渠道统一约束。
5. 支持动态切换渠道
运行时只需要修改传入的channelCode,即可无缝切换支付渠道,适合多渠道容灾、渠道比价切换。
六、生产环境进阶优化方案
1. 渠道配置抽离(数据库动态配置)
实际项目不会硬编码商户参数,新增渠道表pay_channel_config: channelCode、商户号、密钥、开关、手续费、优先级,在实现类中通过渠道编码查询对应配置。
2. 增加渠道开关控制
工厂获取渠道时增加校验:渠道是否启用,未启用直接抛出异常。
public IPayChannel getPayChannel(String channelCode) { // 1. 查询渠道配置是否启用 PayChannelConfig config = configService.getByCode(channelCode); if (!config.getEnable()) { throw new RuntimeException("渠道已关闭"); } // 2. 返回策略实现 return channelMap.get(channelCode); }3. 渠道缓存 + 懒加载
渠道非常多时,可改为懒加载策略 Bean,减少启动内存占用。
4. 统一异常捕获
在IPayChannel实现内部捕获各渠道 SDK 异常,统一封装自定义支付异常,上层不需要区分渠道异常类型。
5. 结合模板方法模式优化重复逻辑
部分渠道存在通用逻辑(参数校验、日志打印),可以增加抽象父类AbstractPayChannel:
public abstract class AbstractPayChannel implements IPayChannel { @Override public PayResultVO createPayOrder(PayOrderDTO dto) { // 通用前置:参数校验、打印请求日志 checkParam(dto); // 交给子类实现渠道特有逻辑 return doCreateOrder(dto); } // 子类实现特有下单逻辑 protected abstract PayResultVO doCreateOrder(PayOrderDTO dto); }融合策略模式 + 模板方法,通用逻辑抽离,大幅减少重复代码。
七、模式角色对应总结
| 策略模式角色 | 支付系统对应代码 | 作用 |
|---|---|---|
| Strategy(策略抽象) | IPayChannel | 定义所有支付渠道统一行为规范 |
| ConcreteStrategy(具体策略) | WechatPayChannel/AlipayChannel | 单渠道专属业务实现 |
| Context(上下文) | PayService | 对外业务入口,屏蔽底层路由细节 |
| Factory(策略工厂) | PayChannelFactory | 根据 channelCode 路由匹配对应策略实例 |
八、对比 if-else 方案的致命缺陷
如果不用策略,传统写法:
// PayService 臃肿写法,极度难维护 public PayResultVO create(PayOrderDTO dto){ if("WECHAT_PAY".equals(dto.getChannelCode())){ // 微信几百行代码 }else if("ALIPAY".equals(dto.getChannelCode())){ // 支付宝几百行代码 }else if("UNION_PAY".equals(dto.getChannelCode())){ // 银联代码 } // 新增渠道继续加else if }- 新增渠道必须修改核心业务类,上线需要全量发布
- 代码耦合严重,改微信逻辑容易误改支付宝分支
- 单元测试需要覆盖所有分支,测试成本极高
- 代码行数爆炸,可读性极差