重构案例实战(中):支付网关集成重构

好,咱们接着聊支付网关的重构。上一章我们把问题摊开了——硬编码、if-else 堆成山、每次加新支付方式都要改老代码。今天我就带你亲手拆掉这座“屎山”,用策略模式加工厂模式,把它改造成一个可扩展、好维护的支付网关。

现状回顾:那段让人头疼的代码

先看一眼我们当时的“杰作”:

public class PaymentService {
    public void pay(String type, double amount) {
        if ("alipay".equals(type)) {
            // 调用支付宝SDK
            AlipayApi alipay = new AlipayApi();
            alipay.setAppId("xxx");
            alipay.setSecret("yyy");
            alipay.pay(amount);
        } else if ("wechat".equals(type)) {
            // 调用微信支付SDK
            WechatPayApi wechat = new WechatPayApi();
            wechat.setMchId("zzz");
            wechat.setKey("kkk");
            wechat.transfer(amount);
        } else if ("unionpay".equals(type)) {
            // 调用银联支付SDK
            UnionPayApi union = new UnionPayApi();
            union.setMerchant("mmm");
            union.pay(amount);
        } else {
            throw new UnsupportedOperationException("不支持的支付方式");
        }
    }
}

这段代码的问题,我总结过三点:

  • 违背开闭原则——加一种支付方式,就得改这个类
  • 代码重复——每个分支都在做“初始化SDK + 调用支付”
  • 测试困难——想测支付宝逻辑,得把整个if-else跑一遍

我在一个电商项目里见过类似的代码,当时线上出了个bug——微信支付接口升级,需要改签名方式。结果改完微信,不小心把支付宝的配置也带偏了。嗯,这种耦合的痛,经历过的人都懂。

重构第一步:定义支付策略接口

策略模式的核心,就是把“怎么做”和“谁来做”分开。我们先定义一个统一的支付策略接口:

public interface PaymentStrategy {
    void pay(double amount);
}

然后每个支付方式实现这个接口:

public class AlipayStrategy implements PaymentStrategy {
    private AlipayApi alipay;

    public AlipayStrategy() {
        this.alipay = new AlipayApi();
        alipay.setAppId("xxx");
        alipay.setSecret("yyy");
    }

    @Override
    public void pay(double amount) {
        alipay.pay(amount);
    }
}

public class WechatPayStrategy implements PaymentStrategy {
    private WechatPayApi wechat;

    public WechatPayStrategy() {
        this.wechat = new WechatPayApi();
        wechat.setMchId("zzz");
        wechat.setKey("kkk");
    }

    @Override
    public void pay(double amount) {
        wechat.transfer(amount);
    }
}

public class UnionPayStrategy implements PaymentStrategy {
    private UnionPayApi union;

    public UnionPayStrategy() {
        this.union = new UnionPayApi();
        union.setMerchant("mmm");
    }

    @Override
    public void pay(double amount) {
        union.pay(amount);
    }
}

你看,每个策略类只关心自己的初始化逻辑和支付调用。测试的时候,我可以单独 mock 一个策略来测,不用再担心牵一发动全身。

重构第二步:引入工厂模式

策略定义好了,但客户端怎么拿到正确的策略?你可能会想:再写个 if-else 工厂?那不就换汤不换药了吗?

我个人习惯用注册式工厂,配合一个 Map 来管理策略实例:

public class PaymentStrategyFactory {
    private static final Map<String, PaymentStrategy> strategies = new HashMap<>();

    static {
        strategies.put("alipay", new AlipayStrategy());
        strategies.put("wechat", new WechatPayStrategy());
        strategies.put("unionpay", new UnionPayStrategy());
    }

    public static PaymentStrategy getStrategy(String type) {
        PaymentStrategy strategy = strategies.get(type);
        if (strategy == null) {
            throw new IllegalArgumentException("不支持的支付方式: " + type);
        }
        return strategy;
    }
}

这样一来,客户端代码就变成了:

public class PaymentService {
    public void pay(String type, double amount) {
        PaymentStrategy strategy = PaymentStrategyFactory.getStrategy(type);
        strategy.pay(amount);
    }
}

是不是清爽多了?原来那个几十行的 if-else,现在只剩两行。而且,加一种新支付方式,只需要:

  1. 新建一个策略类实现 PaymentStrategy
  2. 在工厂的 static 块里注册一下

你想想看,这改动完全不会影响已有的支付逻辑。开闭原则,就这么实现了。

核心变化对比

维度 重构前 重构后
代码行数 30+ 行 if-else 每个策略类约10行 + 工厂类15行
扩展方式 修改 PaymentService 新增策略类 + 注册
测试难度 需要模拟整个流程 可单独测试每个策略
耦合度 高(支付逻辑与业务逻辑耦合) 低(策略独立)

重构第三步:用 SVG 看清整体结构

光说可能不够直观,我画了一张图,把重构后的架构展示出来:

支付网关重构架构图(策略模式 + 工厂模式) 客户端(PaymentService) 调用 getStrategy(type) 策略工厂(Factory) 返回策略实例 <<接口>> PaymentStrategy AlipayStrategy WechatPayStrategy UnionPayStrategy

从图上能清楚看到:客户端只依赖工厂,工厂负责创建策略,每个策略独立实现支付逻辑。这就是“面向接口编程”的典型实践。

避坑指南:我曾经踩过的几个坑

重构不是一蹴而就的,有些细节不注意,反而会引入新问题。我分享几个亲身经历:

坑1:策略实例的线程安全问题

我曾经在工厂里直接 new 策略对象,然后多个线程共用同一个实例。结果支付宝SDK内部有状态,导致并发支付时数据错乱。后来我改成每次调用都创建新实例,或者确保策略类是无状态的。

建议:策略类尽量设计成无状态

把支付参数(比如金额、订单号)通过方法参数传入,而不是存在策略对象的字段里。这样同一个实例可以安全地被多个线程复用。

坑2:工厂注册方式太死板

一开始我用 static 块硬编码注册,后来发现每次加支付方式都要改工厂类。虽然比改 if-else 好,但还是不够灵活。更好的做法是用 Spring 的依赖注入,或者用 SPI 机制自动发现策略实现。

进阶方案:结合 Spring 的自动注册

如果你在用 Spring,可以让每个策略类实现 InitializingBean,在 afterPropertiesSet() 里把自己注册到工厂的 Map 中。这样加新支付方式时,连工厂类都不用改。

总结一下今天的核心收获

这次重构,我们做了三件事:

  • 用策略模式——把每种支付方式封装成独立的策略类
  • 用工厂模式——统一管理策略的创建和获取
  • 解耦了业务逻辑和支付逻辑——加新支付方式不再需要改老代码

说白了,重构不是为了炫技,而是为了让代码更容易理解和修改。你想想看,如果半年后有个新同事接手这个模块,他看到的是清晰的策略类和工厂类,而不是一坨 if-else,是不是会感激你?

嗯,下一章我们继续深入,看看如何用单元测试来验证这次重构的正确性。到时候我会分享一些“测试替身”的小技巧,保证你学了就能用。


公众号:蓝海资料掘金营,微信deep3321