工厂模式
工厂模式
Rif设计模式笔记:工厂方法模式 (Factory Method Pattern)
一、一句话概括
定义一个用于创建对象的接口(工厂方法),但将 “究竟创建哪种具体对象” 的决定权推迟到子类去实现。
二、为什么需要它 (Why - The Pain Point)
想象一下,你正在开发一个应用,需要根据不同的场景创建不同的对象。比如一个文档处理程序,它可以创建和操作不同类型的文档(Word, PDF, Txt)。
最初的简单实现(糟糕的设计):
1 | class DocumentProcessor { |
这种设计存在严重问题:
- 违反开闭原则 (Open/Closed Principle):每当需要支持一种新的文档类型(如
MarkdownDocument),你都必须修改DocumentProcessor类的createDocument方法,增加一个新的else if分支。这使得类变得不稳定、难以维护。 - 耦合度高:
DocumentProcessor这个高层组件,直接依赖于WordDocument,PdfDocument等低层具体类。它知道了太多不该知道的实现细节。 - 职责不单一:
DocumentProcessor不仅要负责处理文档的通用逻辑,还要负责“如何创建”具体文档的逻辑,职责混杂。
核心问题:如何让一个类在不知道具体要创建哪个子类的情况下完成对象的创建,并将这种不确定性隔离出去,以便系统能灵活地扩展?
三、它是什么 (What - The Solution)
工厂方法模式通过引入一个抽象的创建过程来解决这个问题。它将对象的创建逻辑从使用者(DocumentProcessor)中剥离出来,并封装到一个专门的“工厂”结构中。
核心思想和角色:
- **产品接口 (Product)**:定义了工厂方法所创建的对象的共同接口。例如,
Document接口,包含open(),save()等方法。 - **具体产品 (Concrete Product)**:实现了产品接口的具体类。例如,
WordDocument,PdfDocument。 - **创建者类 (Creator)**:
- 它通常是一个抽象类或接口。
- 它声明了一个抽象的**工厂方法 (
factoryMethod)**,该方法的返回类型是产品接口(Product)。 - 它可以包含一些依赖于产品对象的业务逻辑(例如,一个
processDocument()方法,它会先调用工厂方法创建产品,然后再使用该产品)。
- **具体创建者 (Concrete Creator)**:
- 继承或实现创建者类。
- 它重写(实现) 了工厂方法,返回一个具体的、特定的产品实例。例如,
WordDocumentFactory的工厂方法返回new WordDocument()。
C++ 背景对比 & Java 关键点:
- 与C++的联系:这与 C++ 中基类定义一个纯虚函数(Pure Virtual Function) 来创建对象,然后由派生类重写该函数以返回具体派生类实例的思想完全一致。这个纯虚函数就是“工厂方法”。
- Java 的实现精髓:
- 抽象 (
abstract class或interface): 是实现该模式的基石,完美体现了 “面向接口编程,而非面向实现编程” 的原则。 - **依赖倒置原则 (Dependency Inversion Principle)**:高层模块(如客户端代码)不依赖于低层模块(具体产品、具体工厂),而是两者都依赖于抽象(产品接口、创建者接口)。工厂方法是这一原则的经典实践。
- 抽象 (
四、怎么做 (How - The Blueprint)
基本步骤(以重构文档处理器为例):
- 定义产品接口
Document和具体产品WordDocument,PdfDocument。 - **定义抽象创建者
DocumentFactory**。它包含一个抽象方法createDocument()。 - 创建具体创建者,如
WordDocumentFactory和PdfDocumentFactory。每个类都重写createDocument()方法,返回自己负责的产品实例。 - 客户端代码根据需要选择一个具体的工厂来使用。它只与抽象的
DocumentFactory和Document交互,完全不知道具体产品的存在。
Java 示例代码
1 | // 1. 产品接口 (Product) |
五、UML 类图
下面是上述例子的 PlantUML 代码。
1 | @startuml |
图示解读:
- 顶层是产品 (
Document) 和创建者 (DocumentFactory) 的抽象。 - 底层是平行的两组具体实现:
WordDocument对应WordDocumentFactory,PdfDocument对应PdfDocumentFactory。 - 客户端 (
FactoryMethodDemo) 只和顶层的抽象打交道。
六、优缺点分析
优点 (Pros):
- 完美的解耦:将产品的创建逻辑从客户端代码中分离出来,客户端只关心产品接口,不关心具体实现。
- 符合开闭原则:增加新产品时,只需要增加对应的具体产品类和具体工厂类,无需修改现有代码,系统的可扩展性非常好。
- 单一职责原则:创建产品的代码被集中到具体的工厂类中,使得代码结构更清晰,职责更明确。
- 灵活性:子类可以自由决定创建哪个具体产品,甚至可以有更复杂的创建逻辑(比如从缓存中获取,或者创建一个配置好的对象)。
缺点 (Cons):
- 类的数量增多:每增加一个产品,通常就需要增加一个对应的具体工厂类。这会使得系统中的类的个数成倍增加,增加了系统的复杂度和理解成本。
- 优化:如果工厂的创建逻辑很简单(就是
new一下),可以使用反射(Reflection) 或 Lambda 表达式来简化,避免创建大量的具体工厂类。
- 优化:如果工厂的创建逻辑很简单(就是
七、在 Java 中的应用
工厂方法模式在各种框架和库中无处不在,是解耦的利器。
java.util.Collection接口中的iterator()方法:Collection是抽象创建者,iterator()就是工厂方法,Iterator是产品接口。每个具体的集合类(ArrayList,LinkedList)都实现了自己的iterator()方法,返回一个适合自身数据结构的具体迭代器(ArrayListIterator,LinkedListIterator)。- JDBC (Java Database Connectivity):
java.sql.DriverManager.getConnection(...)背后就用了类似工厂方法的思想来根据不同的数据库URL返回不同厂商的Connection实现。 - SLF4J (Simple Logging Facade for Java):
LoggerFactory.getLogger(...)会根据 classpath 下的日志实现(Logback, Log4j2等)来返回一个具体的Logger实例。
工厂方法模式是理解更复杂的抽象工厂模式的基础。掌握它,你就掌握了面向对象设计中“延迟决策”和“依赖倒置”的精髓。