3、工厂方法模式:简单工厂 vs 工厂方法、PHP实现、在支付网关对接中的应用、开闭原则的体现
3.1 从简单工厂说起——我踩过的第一个坑
先聊聊简单工厂。说白了,它就是一个“调度中心”。你告诉它要什么,它给你造什么。
我记得刚入行那会儿,写支付对接就用的简单工厂。代码大概长这样:
class PaymentFactory
{
public static function create($type)
{
switch ($type) {
case 'alipay':
return new AlipayService();
case 'wechat':
return new WechatPayService();
case 'unionpay':
return new UnionPayService();
default:
throw new \InvalidArgumentException('不支持的支付方式');
}
}
}
看起来挺清爽,对吧?但问题很快就来了。
有一次,产品经理跑过来说:“老王,我们要接入银联云闪付。”我打开代码,找到 switch 语句,加了一个 case。改完测试通过,上线了。
过了一个月,又说:“再加个京东支付。”我又改 switch。再后来是“美团支付”、“抖音支付”……每次都要动这个工厂类。
你想想看,每次加一种支付方式,就要改一次 PaymentFactory。这违反了哪个原则?开闭原则——对扩展开放,对修改关闭。我每次都在修改,这哪叫关闭?
3.2 工厂方法模式——把“创建”这件事交给子类
工厂方法模式怎么解决这个问题?
核心思想就一句话:定义一个创建对象的接口,让子类决定实例化哪个类。
说白了,就是把 switch 拆掉,每个支付方式自己建一个工厂。这样加新支付方式,不用改老代码,只要写新类就行。
来看 PHP 实现:
// 1. 支付接口(产品)
interface PaymentInterface
{
public function pay($order);
}
// 2. 具体支付服务
class AlipayService implements PaymentInterface
{
public function pay($order)
{
echo "调用支付宝接口,订单:{$order}\n";
}
}
class WechatPayService implements PaymentInterface
{
public function pay($order)
{
echo "调用微信支付接口,订单:{$order}\n";
}
}
// 3. 工厂接口(Creator)
interface PaymentFactoryInterface
{
public function create(): PaymentInterface;
}
// 4. 具体工厂
class AlipayFactory implements PaymentFactoryInterface
{
public function create(): PaymentInterface
{
return new AlipayService();
}
}
class WechatPayFactory implements PaymentFactoryInterface
{
public function create(): PaymentInterface
{
return new WechatPayService();
}
}
// 5. 客户端使用
$factory = new AlipayFactory();
$payment = $factory->create();
$payment->pay('ORDER2024001');
嗯,这里要注意:代码量确实比简单工厂多了。但换来的是什么?扩展性。
3.3 在支付网关对接中的实际应用
我在做支付网关项目时,对接了 10 多家支付渠道。如果用简单工厂,那个 switch 会写到让人崩溃。
用工厂方法后,结构变成了这样:
| 支付渠道 | 产品类 | 工厂类 |
|---|---|---|
| 支付宝 | AlipayService | AlipayFactory |
| 微信支付 | WechatPayService | WechatPayFactory |
| 银联 | UnionPayService | UnionPayFactory |
| 京东支付 | JdPayService | JdPayFactory |
每次新增渠道,我只需要:
- 写一个
XxxPayService实现PaymentInterface - 写一个
XxxPayFactory实现PaymentFactoryInterface - 在配置里注册一下
老代码一行都不用动。测试也只需要测新渠道,老渠道不受影响。
3.4 开闭原则的体现——为什么工厂方法更“干净”
开闭原则说:对扩展开放,对修改关闭。
简单工厂:加新支付方式 → 修改 PaymentFactory 的 switch → 违反开闭原则。
工厂方法:加新支付方式 → 新增 XxxFactory 和 XxxService → 符合开闭原则。
你想想看,哪个更安全?肯定是后者。因为修改老代码的风险,永远比写新代码大。
我曾经接手过一个项目,简单工厂里 20 多个 case,每次上线都提心吊胆。生怕改了一个 case 影响到另一个。后来重构成了工厂方法,世界清净了。
3.5 简单工厂 vs 工厂方法——一张表说清楚
| 对比维度 | 简单工厂 | 工厂方法 |
|---|---|---|
| 代码量 | 少,集中管理 | 多,每个产品对应一个工厂 |
| 扩展性 | 差,改 switch | 好,加新类即可 |
| 开闭原则 | 违反 | 符合 |
| 适用场景 | 产品种类少、变化少 | 产品种类多、频繁扩展 |
| 维护成本 | 低(初期)→ 高(后期) | 高(初期)→ 低(后期) |
我个人习惯:新项目如果预判支付方式会超过 3 种,直接上工厂方法。如果只有 1-2 种,简单工厂先顶着,等需要扩展时再重构。别一开始就搞复杂了。
最后说一句:设计模式是工具,不是教条。理解背后的思想——开闭原则、单一职责——比记住代码模板重要得多。