怎样用Java的工厂模式重构一个多支付方式的支付系统

wen java案例 43

用Java工厂模式重构多支付方式系统:从混乱到优雅的架构蜕变

📚 目录导读

  1. 问题背景:为什么需要重构支付系统?
  2. 传统支付实现的痛点:if-else地狱与扩展困境
  3. 工厂模式核心思想:解耦创建与使用
  4. 实战重构全过程:从接口定义到工厂实现(含代码)
  5. 高级扩展:结合枚举与反射的自动注册工厂
  6. 常见问答:开发中最纠结的5个问题
  7. 工厂模式在支付场景的最佳实践

问题背景

大多数电商或SaaS系统在初期,支付方式往往只有微信和支付宝,但随着业务发展,你可能需要接入银联、PayPal、Stripe、甚至加密货币支付,如果此时代码还停留在 if (type.equals("wechat")) 的层次,那维护成本会像滚雪球一样增长。工厂模式(Factory Pattern) 正是解决这类“创建对象逻辑复杂且频繁变化”问题的利器。

怎样用Java的工厂模式重构一个多支付方式的支付系统

传统支付实现的痛点

我们先看一个典型的“坏代码”示例:

public class PaymentService {
    public void pay(String type, double amount) {
        if ("WECHAT".equals(type)) {
            WechatPay wechat = new WechatPay();
            wechat.pay(amount);
        } else if ("ALIPAY".equals(type)) {
            Alipay alipay = new Alipay();
            alipay.pay(amount);
        } else if ("UNION".equals(type)) {
            // 每新增一种支付,就要加一个else if
            UnionPay union = new UnionPay();
            union.pay(amount);
        } else {
            throw new UnsupportedOperationException("不支持的支付方式");
        }
    }
}

问题清单

  • ❌ 违反开闭原则:新增支付需要修改核心业务代码
  • ❌ 代码臃肿:支付逻辑与创建逻辑耦合
  • ❌ 测试困难:每个分支都需要模拟
  • ❌ 无法复用:支付对象的创建散落在各处

工厂模式核心思想

工厂模式本质是将“对象创建”的职责从业务代码中剥离,在支付系统中,我们创建一个“支付工厂”,它根据传入的支付类型,返回对应的支付处理对象。核心三要素

  • 产品接口:所有支付方式共同遵守的契约(如 Payment 接口)
  • 具体产品:如 WechatPay, Alipay 等实现类
  • 工厂类:封装创建逻辑,返回正确的产品实例

实战重构全过程

1 定义支付接口

public interface Payment {
    boolean pay(BigDecimal amount);
    void refund(String orderId, BigDecimal amount);
    String getChannelName();
}

2 实现具体支付类

public class WechatPay implements Payment {
    @Override
    public boolean pay(BigDecimal amount) {
        // 调用微信支付SDK
        System.out.println("微信支付:" + amount + "元");
        return true;
    }
    @Override public void refund(String orderId, BigDecimal amount) { /*...*/ }
    @Override public String getChannelName() { return "微信支付"; }
}
public class Alipay implements Payment {
    // 类似实现...
}

3 建立支付工厂

public class PaymentFactory {
    public static Payment createPayment(String channel) {
        switch (channel.toUpperCase()) {
            case "WECHAT": return new WechatPay();
            case "ALIPAY": return new Alipay();
            case "UNION":  return new UnionPay();
            default: throw new IllegalArgumentException("未知支付渠道: " + channel);
        }
    }
}

4 业务调用变得极致简洁

public class OrderService {
    public void processOrder(String channel, BigDecimal amount) {
        Payment payment = PaymentFactory.createPayment(channel);
        payment.pay(amount);
        // 如果需要退款:payment.refund(orderId, amount);
    }
}

重构后的变化

  • 新增支付方式只需:①新建实现类 ②在工厂的switch加一行
  • 原有业务代码零改动
  • 每个支付类可独立测试

高级扩展:自动注册工厂

如果支付方式暴增至20+种,手动维护switch依然痛苦,更优雅的方式是利用反射+注解实现自动注册

// 注解定义
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface PaymentType {
    String value();
}
// 使用方式
@PaymentType("WECHAT")
public class WechatPay implements Payment { ... }
// 自动扫描工厂
public class AutoPaymentFactory {
    private static final Map<String, Payment> PAYMENT_MAP = new HashMap<>();
    static {
        // 扫描指定包下所有@PaymentType注解的类
        Reflections reflections = new Reflections("com.example.payment.impl");
        Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(PaymentType.class);
        for (Class<?> clazz : annotated) {
            PaymentType annotation = clazz.getAnnotation(PaymentType.class);
            try {
                PAYMENT_MAP.put(annotation.value(), 
                    (Payment) clazz.getDeclaredConstructor().newInstance());
            } catch (Exception e) { /* 处理异常 */ }
        }
    }
    public static Payment createPayment(String channel) {
        Payment payment = PAYMENT_MAP.get(channel);
        if (payment == null) throw new IllegalArgumentException();
        return payment;
    }
}

优势:新增支付方式时,只需添加带注解的类,工厂完全不需要修改

常见问答

Q1: 工厂模式和策略模式有什么区别?

A: 工厂模式关注对象的创建(如何拿到支付对象);策略模式关注算法的封装(如何执行支付),实际项目中往往同时使用:工厂创建策略对象。

Q2: 是否一定要用静态工厂?需要Spring怎么办?

A: 可以注入Spring容器,让工厂成为Bean并管理依赖,推荐方式:

@Component
public class SpringPaymentFactory {
    @Autowired private Map<String, Payment> paymentMap; // Spring会自动注入所有Payment实现
    public Payment getPayment(String type) {
        return paymentMap.get(type);
    }
}

Q3: 如果支付对象创建时需要额外参数(如API密钥)?

A: 使用抽象工厂模式,例如创建 WechatPay 时需要 appId 和 mchId,可抽象出工厂接口 PaymentFactory { Payment create(Config config); }

Q4: 工厂模式会不会导致类数量爆炸?

A: 每个支付方式一个类,这是符合单一职责的,相比在原有类中堆砌if-else,灵活性和可读性大幅提升,可以用内部类+枚举简化少量支付场景。

Q5: 如何保证线程安全?

A: 如果是无状态的支付处理器(不保存用户状态),可以复用单例,工厂中缓存已创建的支付实例:

public class SingletonPaymentFactory {
    private static final Map<String, Payment> CACHE = new ConcurrentHashMap<>();
    public static Payment create(String channel) {
        return CACHE.computeIfAbsent(channel, ch -> {
            // 创建逻辑...
        });
    }
}

用Java工厂模式重构支付系统的核心收益:

维度 重构前 重构后
扩展性 新增支付需修改核心代码 只需添加新类
可读性 大量if-else 业务代码一行调用
测试性 所有分支耦合 每个支付类可独立Mock
维护成本 随时间指数增长 线性增长

最佳实践建议

  1. 支付接口设计要合理:预留 support(String type) 方法可让工厂更智能
  2. 结合Spring的IoC容器:让工厂成为Bean,自动注入所有支付实现
  3. 日志与监控:在工厂层统一记录创建日志
  4. 降级与兜底:工厂返回默认支付或抛出明确异常

重构不是炫技,而是用设计模式对抗软件熵增,当你的业务方说“下周要接入银联云闪付”时,你只需要微笑回答:“知道了,我会新建一个类。”——这就是工厂模式带给你的底气。

抱歉,评论功能暂时关闭!