设计模式综合实战(一):需求分析与架构设计
基于策略+工厂模式实现支付系统
说实话,每次带新人做项目,我最怕听到的一句话就是:「这个支付系统很简单,不就是调个接口嘛。」
嗯,说这话的人,十有八九会在上线前夜被产品经理追着改需求。我经历过太多次了——今天加个微信支付,明天要支持花呗分期,后天又来了个跨境支付。如果你一开始没想好架构,后面就是无尽的加班和重构。
所以这一章,咱们就来聊聊怎么用设计模式,把支付系统做得既灵活又稳定。
需求分析:先搞清楚要做什么
做架构设计之前,我习惯先列需求。不是那种「用户能付款」的废话,而是真正影响技术选型的点。
- 多支付渠道:微信支付、支付宝、银联、PayPal……未来可能还有新的
- 支付流程不同:有的需要扫码,有的需要跳转,有的直接扣款
- 参数差异大:每个渠道的请求参数、签名方式、回调格式都不一样
- 扩展性要求:加一个新支付方式,不能改已有代码
- 运行时切换:用户选什么支付方式,系统就执行对应的逻辑
你想想看,如果把这些逻辑全写在一个类里,那会是什么样子?
一个巨大的 switch-case,里面塞满各种 if-else。每次加新渠道,都要改这个类。测试要回归所有分支。我见过一个项目,支付类有 3000 多行,没人敢动。
架构设计:策略模式 + 工厂模式
怎么解决?说白了就两招:
- 策略模式:把每种支付方式封装成独立的策略类
- 工厂模式:根据用户选择的支付方式,创建对应的策略对象
这样,每种支付逻辑互不干扰。加新渠道,就是加一个新类。不改旧代码,符合开闭原则。
先画个图,理清关系
我个人习惯,动手写代码前先画个结构图。下面这个 SVG 就是支付系统的核心架构:
这个图你看懂了吗?客户端不直接依赖具体支付类,它只跟工厂打交道。工厂根据参数,返回对应的策略对象。客户端调用策略对象的 pay() 方法,具体怎么支付,策略自己知道。
代码实现:从接口开始
好,咱们动手写代码。先从策略接口开始:
// 支付策略接口
class IPaymentStrategy {
public:
virtual ~IPaymentStrategy() = default;
// 核心支付方法
virtual bool pay(double amount, const std::string& orderId) = 0;
// 获取支付方式名称
virtual std::string getPaymentName() const = 0;
};
接口很简单,就两个方法。但注意,实际项目中 pay() 的参数会复杂得多——可能包含商品信息、用户信息、回调地址等。这里为了演示,我简化了。
具体策略实现
接下来,实现微信支付和支付宝:
// 微信支付策略
class WeChatPay : public IPaymentStrategy {
public:
bool pay(double amount, const std::string& orderId) override {
// 微信支付的业务逻辑
std::cout << "[微信支付] 订单: " << orderId
<< ", 金额: " << amount << " 元" << std::endl;
std::cout << "生成二维码..." << std::endl;
std::cout << "等待用户扫码..." << std::endl;
// 模拟支付成功
return true;
}
std::string getPaymentName() const override {
return "微信支付";
}
};
// 支付宝策略
class AliPay : public IPaymentStrategy {
public:
bool pay(double amount, const std::string& orderId) override {
std::cout << "[支付宝] 订单: " << orderId
<< ", 金额: " << amount << " 元" << std::endl;
std::cout << "跳转到支付宝收银台..." << std::endl;
return true;
}
std::string getPaymentName() const override {
return "支付宝";
}
};
你看,每个策略只关心自己的逻辑。微信支付要生成二维码,支付宝要跳转页面。互不干扰。
工厂模式:创建策略对象
策略写好了,谁来创建它们?工厂模式上场:
// 支付工厂
class PaymentFactory {
public:
static std::unique_ptr<IPaymentStrategy> createPayment(const std::string& type) {
if (type == "wechat") {
return std::make_unique<WeChatPay>();
} else if (type == "alipay") {
return std::make_unique<AliPay>();
} else if (type == "unionpay") {
return std::make_unique<UnionPay>();
} else {
throw std::invalid_argument("不支持的支付方式: " + type);
}
}
};
客户端使用
最后,看看客户端怎么用:
int main() {
// 用户选择了微信支付
std::string paymentType = "wechat";
// 通过工厂获取策略对象
auto payment = PaymentFactory::createPayment(paymentType);
// 执行支付
bool success = payment->pay(99.9, "ORDER2024001");
if (success) {
std::cout << "支付成功!使用方式: "
<< payment->getPaymentName() << std::endl;
}
return 0;
}
输出结果:
[微信支付] 订单: ORDER2024001, 金额: 99.9 元
生成二维码...
等待用户扫码...
支付成功!使用方式: 微信支付
扩展性验证
现在产品经理说:「我们要加银联支付!」
你只需要做两件事:
- 写一个 UnionPay 类,继承 IPaymentStrategy
- 在工厂的 createPayment 里加一个 else-if 分支
仅此而已。已有的 WeChatPay、AliPay 代码,一行都不用改。测试也只需要测新加的银联部分。
这就是设计模式的力量:
- 策略模式让每种支付逻辑独立
- 工厂模式让对象的创建集中管理
- 两者结合,系统既灵活又稳定
避坑指南
最后,分享几个我踩过的坑:
- 不要用字符串硬编码:工厂里用 "wechat"、"alipay" 这种字符串,容易拼错。我建议用枚举或者 constexpr 常量。
- 注意线程安全:如果工厂在多线程环境下被调用,确保 createPayment 是线程安全的。目前这个实现没问题,因为没共享状态。
- 策略对象不要太大:每个策略只做自己的事。如果某个策略需要大量配置,考虑用建造者模式配合。
嗯,这一章就到这里。代码虽然简单,但背后的思想值得反复琢磨。下一章咱们会在这个基础上,加入更多实战细节——比如异步回调、重试机制、日志埋点等。
公众号:蓝海资料掘金营,微信deep3321