简单工厂案例

wen java案例 3

从电商订单到支付渠道的代码重构全解析

目录导读

  1. 什么是简单工厂模式 – 定义、角色划分与核心思想
  2. 真实业务场景痛点 – 以电商多支付渠道为例
  3. 简单工厂案例逐步落地 – 从糟糕代码到优雅工厂
  4. 模式优缺点与适用边界 – 别在错误场景滥用
  5. 常见问题问答(FAQ) – 面试与实战高频疑问
  6. 总结与延伸 – 与工厂方法、抽象工厂的对比

什么是简单工厂模式

简单工厂模式(Simple Factory Pattern)属于创建型设计模式,它并不在 GoF 23 种经典模式之列,却是最常用、最易理解的“入门级”工厂,其核心思想是:用一个工厂对象决定创建出哪一种产品类的实例,它把客户端的 new 操作集中起来,客户端只需传入一个参数(如类型、枚举、字符串),工厂内部通过 if-elseswitch 返回对应的具体产品对象。

简单工厂案例

三个核心角色:

  • 工厂(Factory):负责创建对象的中心逻辑,通常包含静态方法。
  • 产品(Product):抽象接口或基类,定义公共行为。
  • 具体产品(ConcreteProduct):实现产品接口的不同子类。

真实业务场景痛点:电商订单支付渠道

假设你正在开发一个电商系统,订单结算时需要支持 微信支付(WeChatPay)、支付宝(AliPay)、银联(UnionPay) 三种渠道,最直观的写法是这样的:

// 客户端代码
if (payType.equals("WECHAT")) {
    PayService service = new WeChatPayService();
    service.pay(orderAmount);
} else if (payType.equals("ALI")) {
    PayService service = new AliPayService();
    service.pay(orderAmount);
} else if (payType.equals("UNION")) {
    PayService service = new UnionPayService();
    service.pay(orderAmount);
}

问题很快暴露:

  • 每新增一种支付方式(如 PayPal),必须修改客户端代码,违反开闭原则。
  • 客户端与具体实现类硬耦合,测试时需要 mock 所有分支。
  • 如果创建逻辑复杂(如依赖配置、初始化连接池),代码会迅速臃肿。

简单工厂案例逐步落地

我们通过重构上述支付示例,展示简单工厂的标准实现。

步骤 1:定义产品接口

public interface PayService {
    void pay(BigDecimal amount);
}

步骤 2:实现三个具体产品

public class WeChatPayService implements PayService {
    @Override
    public void pay(BigDecimal amount) {
        System.out.println("微信支付:" + amount + " 元");
    }
}
public class AliPayService implements PayService {
    @Override
    public void pay(BigDecimal amount) {
        System.out.println("支付宝支付:" + amount + " 元");
    }
}
public class UnionPayService implements PayService {
    @Override
    public void pay(BigDecimal amount) {
        System.out.println("银联支付:" + amount + " 元");
    }
}

步骤 3:创建工厂类(核心)

public class PayServiceFactory {
    // 静态方法,根据类型返回对应产品
    public static PayService create(String payType) {
        if ("WECHAT".equalsIgnoreCase(payType)) {
            return new WeChatPayService();
        } else if ("ALI".equalsIgnoreCase(payType)) {
            return new AliPayService();
        } else if ("UNION".equalsIgnoreCase(payType)) {
            return new UnionPayService();
        }
        throw new IllegalArgumentException("未知支付类型:" + payType);
    }
}

步骤 4:客户端改造

PayService service = PayServiceFactory.create("ALI");
service.pay(new BigDecimal("199.99"));

效果立竿见影:

  • 客户端不再直接依赖任何具体实现,只依赖 PayService 接口和工厂。
  • 新增支付方式时,只需在工厂中加一个分支,客户端零修改。
  • 工厂可统一处理空值校验、日志、默认策略。

模式优缺点与适用边界

优点:

  • 解除客户端与具体产品的耦合,提升可维护性。
  • 将创建逻辑集中,便于统一管理和扩展(虽然要改工厂,但只改一处)。
  • 非常适合产品类较少、且创建逻辑简单的场景。

缺点:

  • 违背开闭原则:每次新增产品必须修改工厂类,不符合“对扩展开放,对修改关闭”的严格定义。
  • 工厂类职责过重,如果产品很多,会变成“万能工厂”。
  • 不适用于产品结构复杂、依赖多层初始化的情况。

适用边界:

  • 产品类型数量少(少于 5~10 个)。
  • 创建逻辑相对简单(无复杂参数、无依赖注入)。
  • 客户端只需要通过一个参数即可决定创建哪个对象。

常见问题问答(FAQ)

Q1:简单工厂和工厂方法模式有什么区别? A:简单工厂只有一个工厂类,通过传参返回不同产品;工厂方法定义一个抽象工厂接口,每个具体产品对应一个具体工厂子类,扩展时新增工厂子类即可,不需要修改原有类,严格满足开闭原则。

Q2:简单工厂可以结合反射或枚举来优化吗? A:可以,例如用 Map<String, Class<?>> 存储类型与类的映射,通过反射 newInstance() 创建对象,减少 if-else,但反射会牺牲少量性能,并且引入类名配置的维护问题。

Q3:静态工厂方法就是简单工厂吗? A:不完全是,Java 中如 Integer.valueOf() 是静态工厂方法,但它可能是简单工厂的变种(如缓存对象),也可能返回同一类型的子类,简单工厂强调“根据参数创建不同子类”,而静态工厂方法更宽泛。

Q4:如果支付前需要做参数校验或前置初始化,简单工厂还合适吗? A:适合,但建议把初始化逻辑封装在具体产品的构造方法或工厂内的 create 方法里,如果初始化太重,建议改为工厂方法模式,将创建过程子类化。

Q5:在 Spring 中还用得着写简单工厂吗? A:Spring 的 IOC 容器本身就是一个更强大的工厂,你可以直接注入 Map<String, PayService>,结合策略模式实现类似效果,无需手写工厂类,但理解简单工厂原理,有助于读懂框架源码。


总结与延伸

简单工厂是设计模式入门的“第一课”,它用最小的代价解决了对象创建耦合问题,在真实项目中,它常与策略模式联手(工厂负责创建,策略负责算法替换),或与单例模式结合(返回缓存的单例对象)。

但请记住:没有万能的银弹,如果产品未来会持续扩张,请直接使用工厂方法或抽象工厂,如果你的业务对象本身只有一种类型,只是参数不同,直接用构造器或建造者模式更合适。

行动建议:

  • 动手重构你现有代码中最混乱的 if-else 创建逻辑。
  • 尝试在工厂中增加日志记录、统一异常处理。
  • 对比你使用 Spring 时,@Service 注入多实现类的写法与手动工厂的区别。

延伸阅读关键词: 工厂方法模式、抽象工厂模式、策略模式、依赖注入与解耦、开闭原则实战。

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