工厂方法模式:解耦对象创建的设计模式实践
1. 工厂方法模式:解耦对象创建的利器
第一次接触工厂方法模式是在一个电商系统的开发中。当时我们需要支持多种支付方式(支付宝、微信、银联),每个支付接口的初始化参数和流程都不相同。直接在业务代码里写new Alipay()、new WechatPay()导致代码高度耦合,每次新增支付方式都要修改核心业务逻辑。直到团队里的架构师引入了工厂方法模式,才真正体会到面向对象设计中"针对接口编程,而非实现编程"的精髓。
工厂方法模式(Factory Method Pattern)是GoF 23种设计模式中最常用的创建型模式之一。它定义了一个用于创建对象的接口,但让子类决定实例化哪一个类。简单来说,就是把new操作符从业务代码中抽离出来,交给专门的工厂类来处理。这种看似简单的调整,却能带来惊人的灵活性——当需要新增产品类型时,你只需要扩展新的工厂子类,而不用修改任何现有代码。
2. 工厂方法模式的核心结构
2.1 标准UML类图解析
让我们先看一个典型的工厂方法模式类图(这里用文字描述替代图形):
抽象产品(Product) ↑ 具体产品(ConcreteProduct) ↑ 抽象创建者(Creator) → 创建工厂方法() ↑ 具体创建者(ConcreteCreator) → 实现工厂方法()这个结构中包含四个关键角色:
抽象产品(Product):定义产品的接口,是所有具体产品类的父类。在我们的支付例子中,就是所有支付方式都要实现的IPayment接口。
具体产品(ConcreteProduct):实现抽象产品接口的具体类。比如AlipayPayment、WechatPayment这些具体的支付实现类。
抽象创建者(Creator):声明工厂方法的抽象类/接口,它不关心具体创建什么产品,只定义创建产品的规范。
具体创建者(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(); } // 支付逻辑... } }这种实现存在几个严重问题:
- 违反开闭原则:新增支付方式必须修改OrderService
- 业务逻辑与对象创建耦合
- 难以单元测试(因为直接依赖具体实现)
应用工厂方法模式重构后:
// 抽象产品 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); } }重构后的优势显而易见:
- 符合开闭原则:新增支付方式只需添加新工厂,不修改现有代码
- 业务逻辑与对象创建解耦
- 易于测试:可以通过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 性能优化考量
在性能敏感的场景下,工厂方法的实现需要考虑以下优化点:
- 对象池技术:对于创建成本高的对象(如数据库连接),工厂可以维护一个对象池。
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); } }缓存机制:对于无状态的对象,工厂可以缓存实例避免重复创建。
原型模式:对于初始化成本高的对象,可以克隆原型而非每次都新建。
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 何时不该使用工厂方法
虽然工厂方法模式很强大,但并非所有场景都适用:
简单对象创建:如果对象的创建逻辑非常简单,直接new可能更清晰。
产品类型固定:如果系统不太可能扩展新的产品类型,引入工厂可能过度设计。
性能极端敏感:工厂方法调用比直接new多一层间接性,在纳秒级优化的场景可能有影响。
5.2 相关模式对比
与简单工厂对比:
- 简单工厂把创建逻辑集中在一个工厂类中
- 工厂方法将创建逻辑分散到各个子工厂
- 工厂方法更符合开闭原则
与抽象工厂对比:
- 工厂方法创建单一产品
- 抽象工厂创建产品族(多个相关产品)
- 抽象工厂通常使用工厂方法来实现
与依赖注入对比:
- 工厂方法是主动获取依赖
- DI是被动接收依赖
- 现代框架更倾向于使用DI容器
5.3 实际项目中的取舍建议
根据多年项目经验,我总结出以下实践建议:
渐进式设计:初期可以用简单工厂,等产品类型增多再重构为工厂方法。
结合DI框架:在Spring等框架中,可以结合@Bean方法实现工厂方法。
@Configuration public class PaymentConfig { @Bean @Scope("prototype") public Payment alipayPayment() { return new AlipayPayment(); } }文档很重要:为工厂类和方法添加清晰的文档说明,特别是参数化工厂。
考虑可测试性:设计工厂接口时要便于mock,方便单元测试。
在最近的一个微服务项目中,我们使用工厂方法模式来创建不同协议(HTTP/gRPC)的客户端代理。随着协议增加,这种设计的优势愈发明显——新增协议实现时,业务代码完全不用修改,只需要添加新的工厂实现。这大大降低了代码的维护成本,也使得团队能够并行开发不同的协议适配器。