🌟 告别支付系统"屎山"!我用3种设计模式写出优雅如诗的后台
面试官:如果让你设计一个支持 微信支付、支付宝、快捷支付、余额支付,并且要处理 会员开通、物流邮费、余额充值 等多种业务场景的支付系统,你会怎么设计?
🎯 从“面试噩梦”到“设计艺术品”
支付模块——这个让无数后端开发者又爱又恨的“业务核心区”。爱的是它的重要性,恨的是它那令人窒息的复杂度:
- 每加一个支付方式,代码就膨胀一圈
- 每新增一个业务场景,if-else就多一层
- 每改动一处逻辑,就害怕牵一发而动全身
- 看着日渐臃肿的PaymentService,内心OS:这真的不是“屎山”吗?
今天,我要分享的不是又一个平庸的解决方案,而是一个融合了模板方法、策略模式、工厂模式三位一体的优雅设计。它让支付系统的扩展变得像搭积木一样简单,维护起来如同阅读优美的诗篇。
🔥 第一幕:传统设计的“悲剧”
先看看我们想避免的“反面教材”:
// ❌ 传说中的“万能支付类”(5000+行代码预警)
public class PaymentService {
public PayResult pay(String payType, String bizType, Object param) {
if ("WECHAT".equals(payType)) {
if ("MEMBER".equals(bizType)) {
// 微信会员支付逻辑(300行)
} else if ("SHIPPING".equals(bizType)) {
// 微信物流支付逻辑(300行)
} // ... 还有10个else if
} else if ("ALIPAY".equals(payType)) {
// 再来一套类似的嵌套地狱
} else if ("BALANCE".equals(payType)) {
// 再来一套...
}
// 方法继续膨胀到1000行...
}
}
这种代码的“七宗罪”:
- 改一处而动全身:修微信支付可能影响支付宝
- 新人不敢碰:几千行的类谁看谁头疼
- 测试噩梦:分支路径多到测不完
- 扩展成本高:新增支付方式要改N个地方
✨ 第二幕:我们的“优雅革命”
🎩 设计思路:像点餐一样设计支付系统
想象一下你去餐厅吃饭:
- 服务员(工厂) 问:您要什么菜系?(支付方式)
- 您选择:川菜(微信支付)、粤菜(支付宝)、家常菜(余额支付)
- 厨师(模板) 按固定流程:备菜→烹饪→装盘
- 具体菜品(策略) :宫保鸡丁、水煮鱼...
我们的设计正是如此:
🛠️ 三大模式,各司其职:
🔸 模板方法模式 → 固定“烹饪流程”
所有支付必须走:校验→创建订单→调用通道→处理结果
🔸 策略模式 → 定义“不同菜系的做法”
微信支付、支付宝、余额支付...每个都有自己的实现
🔸 工厂模式 → 智能“服务员”
“客人要微信支付?好的,马上安排微信支付策略”
💻 第三幕:代码里的“优雅舞蹈”
🌈 核心骨架:支付流程的“宪法”
// 🎯 AbstractTradePayment.java - 支付界的“宪法”
public abstract class AbstractTradePayment {
// 📜 这是所有支付必须遵守的“基本法”
public final BasePayResult processPay(BasePayParam order) {
// 第1步:基础校验(身份证检查)
this.baseParamCheck(order);
// 第2步:业务校验(钱包余额够吗?)
this.businessParamValid(order);
// 第3步:留痕(创建交易记录)
TraderOrder traderOrder = this.createTradeOrder(order);
// 第4步:执行(真正付钱)
BasePayResult result = this.requestBankChannel(traderOrder);
// 第5步:善后(更新状态,通知业务)
tradeOrderCompleteManage.completePayTrade(result, traderOrder);
return result;
}
// ⚡️ 这三个方法由“各省市自治区”自行决定
protected abstract void businessParamValid(BasePayParam order);
protected abstract BasePayResult requestBankChannel(TraderOrder order);
public abstract boolean isSupport(String payWay);
}
设计哲学:把不变的固化(宪法),把变化的抽象(地方性法规)。
🎨 具体实现:每个支付方式的“个性”
// 💚 微信支付 - 像小程序一样轻巧
@Service
public class WechatPayWayProcess extends AbstractTradePayment {
@Override
protected BasePayResult requestBankChannel(TraderOrder order) {
// 🎪 这里是微信支付的“魔法舞台”
WechatPayResult result = new WechatPayResult();
result.setPayMessage("生成小程序支付参数...");
result.setTradeStatus(TradeStatusEnum.PAYING);
return result;
}
@Override
public boolean isSupport(String payWay) {
// 🎯 精准识别:“我是微信支付专家”
return PayWay.WECHAT.name().equals(payWay);
}
}
// 💳 余额支付 - 像钱包一样直接
@Service
public class BalancePayWayProcess extends AbstractTradePayment {
@Override
protected BasePayResult requestBankChannel(TraderOrder order) {
// 💰 扣余额,就是这么直接
BalancePayResult result = new BalancePayResult();
result.setBalance(queryUserBalance(order.getUserId()));
return result;
}
@Override
public boolean isSupport(String payWay) {
return PayWay.BALANCE.name().equals(payWay);
}
}
扩展性体现:明天老板说要加“刷脸支付”,我只需要:
- 新建
FacePayWayProcess类 - 实现3个抽象方法
- 完事! 不碰任何现有代码
🏭 智能工厂:系统的“最强大脑”
// 🤖 TradeOrderFactory.java - 支付界的“智能调度中心”
@Component
public class TradeOrderFactory {
@Autowired
private List<AbstractTradePayment> allPayments; // Spring自动收集所有支付方式
public AbstractTradePayment getPayment(String payWay) {
// 🎯 精准匹配:你要微信,绝不给你支付宝
return allPayments.stream()
.filter(payment -> payment.isSupport(payWay))
.findFirst()
.orElseThrow(() -> new RuntimeException("暂不支持该支付方式"));
}
}
Spring的魔法:@Autowired private List<...> 自动收集所有实现类,工厂只负责“查找”,不负责“管理”。
🚀 第四幕:业务方的“极致体验”
使用起来,简单到哭
// 🎪 业务层代码 - 干净得像刚擦过的玻璃
@Service
public class TradeOrderManageImpl implements ITradeOrderManage {
private final TradeOrderFactory factory; // 注入智能工厂
public WechatPayResult wechatPay(WechatPayParam param) {
// ✨ 一行代码调用,底层自动走完整流程
return (WechatPayResult) factory
.getPayment(PayWay.WECHAT.name())
.processPay(param);
}
public BalancePayResult balancePay(BalancePayParam param) {
// 🔄 换支付方式?只是换个枚举值!
return (BalancePayResult) factory
.getPayment(PayWay.BALANCE.name())
.processPay(param);
}
}
测试代码,清晰明了
@Test
public void testWechatMemberPayment() {
// 🎨 准备测试数据
WechatPayParam param = WechatPayParam.builder()
.userId(123L)
.amount(new BigDecimal("199.00"))
.orderName("年度VIP会员")
.tradeBizType(TradeBizType.MEMBER)
.payWay(PayWay.WECHAT)
.wxOpenId("openid_123")
.build();
// 🚀 执行支付
WechatPayResult result = tradeOrderManage.wechatPay(param);
// ✅ 验证结果
assertNotNull(result.getPayMessage());
assertEquals("PAYING", result.getTradeStatus().name());
log.info("微信会员支付测试通过!返回:{}", result);
}
📊 第五幕:架构的“降维打击”
传统方式 vs 我们的设计
| 维度 | 传统if-else大法 | 我们的三模式融合 |
|---|---|---|
| 新增支付方式 | 修改巨型类,风险高 | 新增类,零风险 |
| 代码行数/类 | 3000+行 | 50-100行 |
| 可读性 | 需要通读全文 | 看抽象类知流程 |
| 可测试性 | 难以Mock所有分支 | 每个策略独立测试 |
| 团队协作 | 容易冲突 | 每人负责不同策略 |
| 新人上手 | 需要一周熟悉 | 一小时看懂架构 |
🏗️ 系统的“弹性架构”
📦 支付系统架构图
┌─────────────────┐
│ 业务调用层 │ ← 简单API,开箱即用
└────────┬────────┘
│
┌────────▼────────┐
│ 智能工厂 🏭 │ ← 自动路由,精准匹配
└────────┬────────┘
│
┌──────────────┼──────────────┐
│ │ │
┌───▼────┐ ┌────▼────┐ ┌─────▼───┐
│微信支付 │ │ 支付宝 │ │余额支付 │ ← 独立策略,互不干扰
│策略类💚 │ │策略类💙 │ │策略类💛 │
└────────┘ └─────────┘ └─────────┘
│ │ │
└──────────────┼──────────────┘
│
┌────────▼────────┐
│ 模板方法基类 📜 │ ← 统一流程,确保规范
└─────────────────┘
🌈 第六幕:还能更优雅吗?当然!
生产级增强建议
// 1️⃣ 添加分布式锁,防止重复支付
@Slf4j
@Service
public class TradeOrderCompleteManageImpl {
// 🔐 从本地锁升级为分布式锁
public void completePayTrade(BasePayResult result, TraderOrder order) {
String lockKey = "PAY_LOCK:" + order.getTradeOrderNo();
try {
// 使用Redis分布式锁
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "LOCKED", 5, TimeUnit.MINUTES);
if (Boolean.TRUE.equals(locked)) {
// 安全的处理逻辑...
}
} finally {
redisTemplate.delete(lockKey);
}
}
}
// 2️⃣ 添加监控埋点
public abstract class AbstractTradePayment {
public BasePayResult processPay(BasePayParam order) {
String metricName = "payment." + this.getClass().getSimpleName();
Timer.Sample sample = Timer.start();
try {
// ... 原有逻辑
sample.stop(Metrics.timer(metricName, "status", "success"));
return result;
} catch (Exception e) {
sample.stop(Metrics.timer(metricName, "status", "error"));
throw e;
}
}
}
🎭 处理支付回调的“优雅舞步”
// 支付成功后的业务处理也遵循同样模式
@Service
public class MemberTradeComplete extends AbstractTradeComplete<MemberOrder> {
@Override
public void successComplete(TraderOrder order, MemberOrder memberOrder) {
// 🎁 支付成功:开通会员权益
memberService.activeVip(memberOrder.getUserId(),
order.getSuccessTime());
notifyService.sendSuccessMsg(order.getUserId(), "会员开通成功!");
log.info("用户{}会员开通完成,订单:{}",
order.getUserId(), order.getTradeOrderNo());
}
@Override
public void failComplete(TraderOrder order, MemberOrder memberOrder) {
// 😢 支付失败:释放占用的会员名额
memberService.releaseReservation(memberOrder.getUserId());
notifyService.sendFailMsg(order.getUserId(), "支付失败,请重试");
}
}
🎯 第七幕:为什么这个设计如此迷人?
💡 核心优势总结
-
开闭原则的典范
对扩展开放:新增支付方式?加个类就行
对修改关闭:绝不修改现有代码 -
各司其职的优雅
- 模板类:我只关心流程
- 策略类:我只专注实现
- 工厂类:我只负责调度
-
新人友好的设计
新同事第一天就能看懂:“哦,原来支付系统是这样工作的!” -
测试的便利性
每个支付方式可以单独测试,Mock成本极低 -
演化的可持续性
系统会随着业务增长而“自然生长”,而不是“野蛮堆砌”
📚 最后:给你的“设计工具箱”
这个支付系统设计给了我们一个重要的启示:复杂度不是天生的,而是设计出来的。好的设计不是消灭复杂度,而是管理复杂度。
适用场景 checklist ✅
- 你有多种相似但不同的算法/策略
- 未来可能会有新的策略加入
- 你希望避免庞大的条件语句
- 团队需要清晰的职责划分
- 系统需要长期维护和演进
🎁 彩蛋:完整源码获取
如果你想要:
- ✅ 完整可运行的Spring Boot项目
- ✅ 所有支付策略的实现代码
- ✅ 数据库设计脚本
- ✅ Postman测试集合
- ✅ 详细的部署文档
欢迎关注我的公众号 SilkyStarter,回复关键词 「优雅支付」 获取完整源码。
在公众号里,我还会分享:
- 🚀 微服务架构深度解析
- 🎯 高并发系统设计实战
- 🔧 代码重构的“艺术与科学”
- 📈 从程序员到架构师的成长路径
源码 + 深度解析 + 持续更新 = 你的技术加速器
💬 灵魂提问
如果你来设计:
- 这个架构还能在哪些业务场景中复刻?
- 如果支付方式达到50种,这个设计还撑得住吗?
- 如何在这个基础上加入“支付路由”智能选择最优惠渠道?
欢迎在评论区分享你的高见! 让我们一起把代码写成诗 ✨