1. 工厂设计模式概述
工厂模式是面向对象编程中最常用的设计模式之一,它属于创建型模式,主要解决对象创建的问题。在实际开发中,我们经常会遇到需要创建大量相似对象的场景,如果直接在代码中new对象,会导致代码耦合度高、难以维护。工厂模式通过将对象的创建过程封装起来,让客户端代码不需要知道具体创建细节,只需要通过工厂获取所需对象即可。
我第一次真正体会到工厂模式的威力是在一个电商系统的开发中。当时需要对接多个第三方物流供应商,每个供应商都有自己独特的API接口和参数格式。如果直接在业务代码中实例化各个物流对象,不仅会使代码变得臃肿,而且每次新增供应商都需要修改多处代码。引入工厂模式后,所有物流对象的创建都集中在一个地方管理,大大提高了系统的可维护性和扩展性。
工厂模式主要分为三种类型:简单工厂模式、工厂方法模式和抽象工厂模式。这三种模式各有特点,适用于不同的场景。简单工厂模式最简单直接,适合对象创建逻辑不复杂的场景;工厂方法模式通过引入抽象层,支持更灵活的对象创建;抽象工厂模式则更进一步,可以创建一系列相关或依赖的对象。
2. 简单工厂模式详解
2.1 基本结构与实现
简单工厂模式的核心是一个工厂类,它根据传入的参数决定创建哪种产品类的实例。下面是一个典型的简单工厂模式结构:
// 产品接口 interface Product { void use(); } // 具体产品A class ConcreteProductA implements Product { @Override public void use() { System.out.println("Using Product A"); } } // 具体产品B class ConcreteProductB implements Product { @Override public void use() { System.out.println("Using Product B"); } } // 简单工厂 class SimpleFactory { public static Product createProduct(String type) { switch (type) { case "A": return new ConcreteProductA(); case "B": return new ConcreteProductB(); default: throw new IllegalArgumentException("Unknown product type"); } } } // 客户端代码 public class Client { public static void main(String[] args) { Product product = SimpleFactory.createProduct("A"); product.use(); } }在这个例子中,客户端只需要知道产品类型"A"或"B",而不需要关心具体产品的创建细节。工厂类集中了所有产品的创建逻辑,当需要新增产品类型时,只需要修改工厂类即可。
2.2 适用场景与优缺点
简单工厂模式最适合以下场景:
- 需要创建的对象较少,且创建逻辑不复杂
- 客户端不关心对象的创建细节,只需要获取可用的对象
- 对象的创建过程需要统一管理和控制
优点:
- 将对象的创建和使用分离,降低耦合度
- 客户端无需知道具体产品类名,只需要知道参数
- 可以通过配置文件等方式实现不修改代码就更换具体产品
缺点:
- 工厂类集中了所有产品的创建逻辑,职责过重
- 增加新产品需要修改工厂类,违反了开闭原则
- 难以扩展复杂的产品创建逻辑
提示:简单工厂模式虽然简单,但在小型项目或创建逻辑不复杂的场景中非常实用。不要因为它的简单而忽视它的价值。
3. 工厂方法模式深入解析
3.1 模式结构与实现
工厂方法模式是对简单工厂模式的进一步抽象,它定义了一个创建对象的接口,但让子类决定实例化哪个类。工厂方法让类的实例化推迟到子类进行。
// 产品接口 interface Product { void use(); } // 具体产品A class ConcreteProductA implements Product { @Override public void use() { System.out.println("Using Product A"); } } // 具体产品B class ConcreteProductB implements Product { @Override public void use() { System.out.println("Using Product B"); } } // 工厂接口 interface Factory { Product createProduct(); } // 具体工厂A class ConcreteFactoryA implements Factory { @Override public Product createProduct() { return new ConcreteProductA(); } } // 具体工厂B class ConcreteFactoryB implements Factory { @Override public Product createProduct() { return new ConcreteProductB(); } } // 客户端代码 public class Client { public static void main(String[] args) { Factory factory = new ConcreteFactoryA(); Product product = factory.createProduct(); product.use(); } }工厂方法模式通过引入抽象工厂接口,将具体产品的创建延迟到具体工厂类中实现。这样当需要新增产品时,只需要新增对应的工厂类,而不需要修改现有代码,符合开闭原则。
3.2 实际应用案例
在开发一个跨平台UI框架时,工厂方法模式特别有用。假设我们需要支持Windows和Mac两种风格的按钮:
// 按钮接口 interface Button { void render(); void onClick(); } // Windows风格按钮 class WindowsButton implements Button { @Override public void render() { System.out.println("Rendering a Windows style button"); } @Override public void onClick() { System.out.println("Windows button clicked"); } } // Mac风格按钮 class MacButton implements Button { @Override public void render() { System.out.println("Rendering a Mac style button"); } @Override public void onClick() { System.out.println("Mac button clicked"); } } // 对话框抽象类 abstract class Dialog { public void renderWindow() { Button button = createButton(); button.render(); button.onClick(); } public abstract Button createButton(); } // Windows对话框 class WindowsDialog extends Dialog { @Override public Button createButton() { return new WindowsButton(); } } // Mac对话框 class MacDialog extends Dialog { @Override public Button createButton() { return new MacButton(); } } // 客户端代码 public class Client { public static void main(String[] args) { Dialog dialog; String osName = System.getProperty("os.name").toLowerCase(); if (osName.contains("windows")) { dialog = new WindowsDialog(); } else { dialog = new MacDialog(); } dialog.renderWindow(); } }在这个例子中,Dialog类并不知道它创建的按钮的具体类型,它只关心按钮的接口。具体的按钮创建由子类决定,这样当需要新增Linux风格的按钮时,只需要新增LinuxDialog和LinuxButton类即可,现有代码完全不需要修改。
4. 抽象工厂模式全面剖析
4.1 模式概念与结构
抽象工厂模式是工厂方法模式的扩展,它提供了一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂模式适用于产品族的概念,即一系列相关的产品需要一起使用。
// 抽象产品A interface AbstractProductA { void use(); } // 抽象产品B interface AbstractProductB { void consume(); } // 具体产品A1 class ProductA1 implements AbstractProductA { @Override public void use() { System.out.println("Using Product A1"); } } // 具体产品A2 class ProductA2 implements AbstractProductA { @Override public void use() { System.out.println("Using Product A2"); } } // 具体产品B1 class ProductB1 implements AbstractProductB { @Override public void consume() { System.out.println("Consuming Product B1"); } } // 具体产品B2 class ProductB2 implements AbstractProductB { @Override public void consume() { System.out.println("Consuming Product B2"); } } // 抽象工厂 interface AbstractFactory { AbstractProductA createProductA(); AbstractProductB createProductB(); } // 具体工厂1 class ConcreteFactory1 implements AbstractFactory { @Override public AbstractProductA createProductA() { return new ProductA1(); } @Override public AbstractProductB createProductB() { return new ProductB1(); } } // 具体工厂2 class ConcreteFactory2 implements AbstractFactory { @Override public AbstractProductA createProductA() { return new ProductA2(); } @Override public AbstractProductB createProductB() { return new ProductB2(); } } // 客户端代码 public class Client { public static void main(String[] args) { AbstractFactory factory = new ConcreteFactory1(); AbstractProductA productA = factory.createProductA(); AbstractProductB productB = factory.createProductB(); productA.use(); productB.consume(); } }4.2 实际应用场景
抽象工厂模式特别适合GUI库、跨平台应用、游戏开发等场景。例如,在开发一个跨平台的UI组件库时:
// GUI组件接口 interface Button { void paint(); } interface Checkbox { void paint(); } // Windows组件 class WindowsButton implements Button { @Override public void paint() { System.out.println("Painting a Windows button"); } } class WindowsCheckbox implements Checkbox { @Override public void paint() { System.out.println("Painting a Windows checkbox"); } } // Mac组件 class MacButton implements Button { @Override public void paint() { System.out.println("Painting a Mac button"); } } class MacCheckbox implements Checkbox { @Override public void paint() { System.out.println("Painting a Mac checkbox"); } } // 抽象工厂 interface GUIFactory { Button createButton(); Checkbox createCheckbox(); } // 具体工厂 class WindowsFactory implements GUIFactory { @Override public Button createButton() { return new WindowsButton(); } @Override public Checkbox createCheckbox() { return new WindowsCheckbox(); } } class MacFactory implements GUIFactory { @Override public Button createButton() { return new MacButton(); } @Override public Checkbox createCheckbox() { return new MacCheckbox(); } } // 客户端代码 public class Application { private Button button; private Checkbox checkbox; public Application(GUIFactory factory) { button = factory.createButton(); checkbox = factory.createCheckbox(); } public void paint() { button.paint(); checkbox.paint(); } public static void main(String[] args) { GUIFactory factory; String osName = System.getProperty("os.name").toLowerCase(); if (osName.contains("windows")) { factory = new WindowsFactory(); } else { factory = new MacFactory(); } Application app = new Application(factory); app.paint(); } }在这个例子中,Application类不关心具体的按钮和复选框实现,它只通过抽象接口与GUI组件交互。这使得我们可以轻松支持新的操作系统风格,只需添加新的工厂类和产品类即可。
5. 三种工厂模式的对比与选择
5.1 模式对比
| 特性 | 简单工厂模式 | 工厂方法模式 | 抽象工厂模式 |
|---|---|---|---|
| 复杂度 | 低 | 中 | 高 |
| 适用场景 | 对象创建逻辑简单 | 单一产品族的创建 | 多个产品族的创建 |
| 扩展性 | 差(需修改工厂类) | 好(新增具体工厂) | 好(新增具体工厂) |
| 符合开闭原则 | 否 | 是 | 是 |
| 产品间约束 | 无 | 无 | 有(产品族约束) |
| 典型应用 | 工具类、简单对象创建 | 框架扩展、插件系统 | 跨平台UI、主题系统 |
5.2 选择指南
在实际项目中如何选择合适的工厂模式?以下是一些经验法则:
选择简单工厂模式:
- 当对象的创建逻辑简单,不太可能频繁变化
- 项目规模较小,不需要复杂的扩展性
- 需要快速实现一个简单的对象创建封装
选择工厂方法模式:
- 当需要创建的对象类型可能会扩展
- 希望将对象的创建延迟到子类
- 需要遵循开闭原则,避免修改已有代码
- 创建的是单一类型的产品
选择抽象工厂模式:
- 当需要创建一系列相关或依赖的对象
- 系统需要独立于产品的创建、组合和表示
- 需要提供一个产品类库,且只暴露接口而非实现
- 产品族中的对象需要一起使用,有约束关系
注意:不要过度设计。如果简单工厂能满足需求,就不要使用更复杂的工厂方法或抽象工厂。随着需求变化,可以逐步重构到更复杂的模式。
6. 工厂模式的高级应用与最佳实践
6.1 结合依赖注入
在现代框架中,工厂模式常与依赖注入(DI)结合使用。例如Spring框架中的BeanFactory就是一个高级工厂模式的实现:
@Service class OrderService { private final PaymentProcessor paymentProcessor; @Autowired public OrderService(PaymentProcessor paymentProcessor) { this.paymentProcessor = paymentProcessor; } public void processOrder(Order order) { paymentProcessor.process(order.getAmount()); } } interface PaymentProcessor { void process(double amount); } @Component("creditCardProcessor") class CreditCardProcessor implements PaymentProcessor { @Override public void process(double amount) { System.out.println("Processing credit card payment: " + amount); } } @Component("paypalProcessor") class PayPalProcessor implements PaymentProcessor { @Override public void process(double amount) { System.out.println("Processing PayPal payment: " + amount); } }Spring的依赖注入容器实际上是一个超级工厂,它负责创建和管理所有Bean的生命周期。通过@Autowired注解,我们可以轻松获取所需的依赖对象,而不需要关心具体的创建过程。
6.2 使用静态工厂方法
静态工厂方法是另一种常见的工厂模式实现方式,它在JDK中有广泛应用:
// JDK中的静态工厂方法示例 public final class LocalDateTime { // 私有构造器 private LocalDateTime() {} // 静态工厂方法 public static LocalDateTime now() { // 实现细节... } public static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute) { // 实现细节... } } // 使用示例 LocalDateTime timePoint = LocalDateTime.now(); LocalDateTime.of(2012, Month.DECEMBER, 12, 21, 30);静态工厂方法的优点:
- 方法名可以更有意义(相比构造函数)
- 不必每次调用都创建新对象(可以缓存)
- 可以返回子类型对象
- 创建参数化类型实例更简洁
6.3 工厂模式与单例模式结合
有时我们需要确保工厂类本身是单例的:
class SingletonFactory { private static volatile SingletonFactory instance; private SingletonFactory() {} public static SingletonFactory getInstance() { if (instance == null) { synchronized (SingletonFactory.class) { if (instance == null) { instance = new SingletonFactory(); } } } return instance; } public Product createProduct() { return new ConcreteProduct(); } }这种组合模式在需要全局唯一的工厂实例时非常有用,比如数据库连接池、线程池等资源的创建。
7. 常见问题与解决方案
7.1 工厂模式会增加多少性能开销?
工厂模式确实会引入一些额外的抽象层,但通常这种开销可以忽略不计。现代JVM对虚方法调用有很好的优化,而且工厂模式带来的灵活性和可维护性优势远大于微小的性能开销。
7.2 如何处理工厂中的错误情况?
在工厂方法中处理错误有几种常见方式:
- 返回null:简单但不推荐,容易导致NPE
- 抛出异常:明确但需要客户端处理
- 返回特殊对象:如NullObject模式
- 使用Optional:Java 8+推荐方式
public Optional<Product> createProduct(String type) { switch (type) { case "A": return Optional.of(new ProductA()); case "B": return Optional.of(new ProductB()); default: return Optional.empty(); } }7.3 如何避免工厂类成为上帝类?
简单工厂模式容易导致工厂类职责过重。解决方法:
- 拆分为多个工厂类
- 升级为工厂方法模式
- 使用依赖注入框架
- 结合策略模式等动态创建对象
7.4 工厂模式与建造者模式的区别?
工厂模式关注的是对象的创建,而建造者模式关注的是对象的组装过程。工厂模式返回一个完整的对象,建造者模式允许逐步构建复杂对象。
8. 实际项目中的经验分享
在我参与的一个电商平台项目中,我们使用抽象工厂模式来处理不同支付渠道的集成。每个支付渠道(支付宝、微信、银联等)都有自己的订单创建、查询、退款等接口。通过抽象工厂模式,我们能够:
- 统一所有支付渠道的接口
- 轻松新增支付渠道而不影响现有代码
- 方便进行支付渠道的切换和组合
- 集中管理支付相关的配置和异常处理
具体实现中,我们定义了PaymentFactory接口和对应的产品接口(PaymentOrder、PaymentQuery等)。每个支付渠道实现自己的工厂和产品类。在Spring框架下,这些工厂可以通过配置动态注入,使得支付渠道的切换只需要修改配置即可。
另一个经验是,在大型项目中,可以考虑使用工厂模式配合配置文件来实现插件式架构。通过读取配置文件决定实例化哪些具体类,可以大大提高系统的灵活性和可扩展性。