工厂方法模式:解耦对象创建的设计模式实践

发布时间:2026/10/26 23:19:59
工厂方法模式:解耦对象创建的设计模式实践

1. 工厂方法模式:解耦对象创建的利器

第一次接触工厂方法模式是在一个电商系统的开发中。当时我们需要支持多种支付方式(支付宝、微信、银联),每个支付接口的初始化参数和流程都不相同。直接在业务代码里写new Alipay()、new WechatPay()导致代码高度耦合,每次新增支付方式都要修改核心业务逻辑。直到团队里的架构师引入了工厂方法模式,才真正体会到面向对象设计中"针对接口编程,而非实现编程"的精髓。

工厂方法模式(Factory Method Pattern)是GoF 23种设计模式中最常用的创建型模式之一。它定义了一个用于创建对象的接口,但让子类决定实例化哪一个类。简单来说,就是把new操作符从业务代码中抽离出来,交给专门的工厂类来处理。这种看似简单的调整,却能带来惊人的灵活性——当需要新增产品类型时,你只需要扩展新的工厂子类,而不用修改任何现有代码。

2. 工厂方法模式的核心结构

2.1 标准UML类图解析

让我们先看一个典型的工厂方法模式类图(这里用文字描述替代图形):

抽象产品(Product) ↑ 具体产品(ConcreteProduct) ↑ 抽象创建者(Creator) → 创建工厂方法() ↑ 具体创建者(ConcreteCreator) → 实现工厂方法()

这个结构中包含四个关键角色:

  1. 抽象产品(Product):定义产品的接口,是所有具体产品类的父类。在我们的支付例子中,就是所有支付方式都要实现的IPayment接口。

  2. 具体产品(ConcreteProduct):实现抽象产品接口的具体类。比如AlipayPayment、WechatPayment这些具体的支付实现类。

  3. 抽象创建者(Creator):声明工厂方法的抽象类/接口,它不关心具体创建什么产品,只定义创建产品的规范。

  4. 具体创建者(ConcreteCreator):实现工厂方法的具体类,决定实例化哪个具体产品。比如AlipayFactory的工厂方法就返回new AlipayPayment()。

提示:在实际开发中,Creator不一定非要是抽象类。当默认实现有意义时,也可以提供基础的产品创建逻辑。

2.2 模式变体与实现方式

工厂方法模式在实际应用中有多种实现变体:

参数化工厂方法:通过传入参数决定创建哪种产品。比如:

public Payment createPayment(String type) { switch(type) { case "alipay": return new AlipayPayment(); case "wechat": return new WechatPayment(); default: throw new IllegalArgumentException(); } }

多态工厂:每个具体产品对应一个具体工厂。这是最标准的实现方式:

// 抽象工厂 interface PaymentFactory { Payment createPayment(); } // 具体工厂 class AlipayFactory implements PaymentFactory { @Override public Payment createPayment() { return new AlipayPayment(); } }

延迟初始化:工厂可以缓存已创建的产品实例,实现对象复用:

class PaymentFactory { private static Map<String, Payment> cache = new HashMap<>(); public static Payment getPayment(String type) { if(!cache.containsKey(type)) { cache.put(type, createPayment(type)); } return cache.get(type); } }

3. 工厂方法模式的典型应用场景

3.1 支付系统案例深度剖析

让我们回到开头的支付系统案例。没有使用工厂方法之前,业务代码可能是这样的:

public class OrderService { public void pay(String paymentType) { Payment payment; if("alipay".equals(paymentType)) { payment = new AlipayPayment(); } else if("wechat".equals(paymentType)) { payment = new WechatPayment(); } // 支付逻辑... } }

这种实现存在几个严重问题:

  1. 违反开闭原则:新增支付方式必须修改OrderService
  2. 业务逻辑与对象创建耦合
  3. 难以单元测试(因为直接依赖具体实现)

应用工厂方法模式重构后:

// 抽象产品 public interface Payment { void pay(BigDecimal amount); } // 具体产品 public class AlipayPayment implements Payment { @Override public void pay(BigDecimal amount) { // 支付宝支付实现 } } // 抽象工厂 public interface PaymentFactory { Payment createPayment(); } // 具体工厂 public class AlipayFactory implements PaymentFactory { @Override public Payment createPayment() { return new AlipayPayment(); } } // 业务代码 public class OrderService { private PaymentFactory paymentFactory; public OrderService(PaymentFactory factory) { this.paymentFactory = factory; } public void pay() { Payment payment = paymentFactory.createPayment(); payment.pay(amount); } }

重构后的优势显而易见:

  1. 符合开闭原则:新增支付方式只需添加新工厂,不修改现有代码
  2. 业务逻辑与对象创建解耦
  3. 易于测试:可以通过Mock工厂注入测试替身

3.2 框架设计中的工厂方法

工厂方法模式在各种框架中都有广泛应用:

Spring框架:BeanFactory就是典型的工厂方法实现。ApplicationContext作为具体工厂,负责创建和管理各种Bean。

// Spring中的工厂方法应用 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); MyService service = context.getBean(MyService.class);

JDK中的案例:

  • java.util.Collection的iterator()方法
  • java.text.NumberFormat的getInstance()方法
  • java.nio.charset.Charset的forName()方法

这些API背后都是工厂方法模式的变体实现。

4. 工厂方法模式的进阶应用与优化

4.1 结合其他设计模式

工厂方法模式经常与其他模式配合使用,产生更强大的效果:

工厂方法+策略模式:当产品有不同的行为变体时,可以用策略模式来配置具体行为。

interface CompressionStrategy { byte[] compress(byte[] data); } class ZipCompression implements CompressionStrategy { /*...*/ } class CompressionFactory { public CompressionStrategy createCompressor(String type) { switch(type) { case "zip": return new ZipCompression(); // 其他压缩算法... } } }

工厂方法+单例模式:确保工厂本身是单例的,避免重复创建工厂实例。

public enum PaymentFactory { INSTANCE; public Payment createPayment(String type) { // 工厂实现... } }

4.2 性能优化考量

在性能敏感的场景下,工厂方法的实现需要考虑以下优化点:

  1. 对象池技术:对于创建成本高的对象(如数据库连接),工厂可以维护一个对象池。
class ConnectionFactory { private static Queue<Connection> pool = new LinkedList<>(); public static Connection getConnection() { if(pool.isEmpty()) { return createNewConnection(); } return pool.poll(); } public static void releaseConnection(Connection conn) { pool.offer(conn); } }
  1. 缓存机制:对于无状态的对象,工厂可以缓存实例避免重复创建。

  2. 原型模式:对于初始化成本高的对象,可以克隆原型而非每次都新建。

4.3 现代语言中的简化实现

在新版Java和其他现代语言中,工厂方法的实现可以更加简洁:

Java 8+的函数式工厂:

Map<String, Supplier<Payment>> paymentFactories = new HashMap<>(); paymentFactories.put("alipay", AlipayPayment::new); paymentFactories.put("wechat", WechatPayment::new); Payment payment = paymentFactories.get(type).get();

Kotlin的伴生对象工厂:

interface Payment { companion object Factory { fun create(type: String): Payment { return when(type) { "alipay" -> AlipayPayment() else -> throw IllegalArgumentException() } } } }

5. 工厂方法模式的局限性与替代方案

5.1 何时不该使用工厂方法

虽然工厂方法模式很强大,但并非所有场景都适用:

  1. 简单对象创建:如果对象的创建逻辑非常简单,直接new可能更清晰。

  2. 产品类型固定:如果系统不太可能扩展新的产品类型,引入工厂可能过度设计。

  3. 性能极端敏感:工厂方法调用比直接new多一层间接性,在纳秒级优化的场景可能有影响。

5.2 相关模式对比

与简单工厂对比:

  • 简单工厂把创建逻辑集中在一个工厂类中
  • 工厂方法将创建逻辑分散到各个子工厂
  • 工厂方法更符合开闭原则

与抽象工厂对比:

  • 工厂方法创建单一产品
  • 抽象工厂创建产品族(多个相关产品)
  • 抽象工厂通常使用工厂方法来实现

与依赖注入对比:

  • 工厂方法是主动获取依赖
  • DI是被动接收依赖
  • 现代框架更倾向于使用DI容器

5.3 实际项目中的取舍建议

根据多年项目经验,我总结出以下实践建议:

  1. 渐进式设计:初期可以用简单工厂,等产品类型增多再重构为工厂方法。

  2. 结合DI框架:在Spring等框架中,可以结合@Bean方法实现工厂方法。

@Configuration public class PaymentConfig { @Bean @Scope("prototype") public Payment alipayPayment() { return new AlipayPayment(); } }
  1. 文档很重要:为工厂类和方法添加清晰的文档说明,特别是参数化工厂。

  2. 考虑可测试性:设计工厂接口时要便于mock,方便单元测试。

在最近的一个微服务项目中,我们使用工厂方法模式来创建不同协议(HTTP/gRPC)的客户端代理。随着协议增加,这种设计的优势愈发明显——新增协议实现时,业务代码完全不用修改,只需要添加新的工厂实现。这大大降低了代码的维护成本,也使得团队能够并行开发不同的协议适配器。