设计模式--工厂模式 (Factory)
本文系统讲解了工厂设计模式,其核心是将对象创建与使用分离。文章详细介绍了简单工厂、工厂方法和抽象工厂三种类型的思想、结构与优缺点,并对比了各自的应用场景。
我们来详细、系统地讲解设计模式中的工厂模式(Factory Pattern)。
工厂模式是一种创建型设计模式,其核心思想是将对象的创建过程与使用过程分离。客户端不需要知道具体要创建哪个类的实例,也无需关心创建的复杂细节,只需要向一个“工厂”索要一个“产品”,工厂会根据你的需求返回相应的产品实例。
这就像去快餐店点餐:你只需要告诉收银员你要“汉堡”还是“鸡翅”(这是你的需求),而不需要关心厨房里是如何用面粉、肉、蔬菜等原材料制作出这些食物的(这是创建细节)。收银员和厨房就扮演了“工厂”的角色。
工厂模式主要分为三种类型,由简单到复杂,解决的问题也越来越抽象:
- 简单工厂模式(Simple Factory Pattern) - 又称静态工厂方法模式。
- 工厂方法模式(Factory Method Pattern) - 符合设计原则的经典工厂模式。
- 抽象工厂模式(Abstract Factory Pattern) - 工厂的工厂,用于创建产品族。
1. 简单工厂模式 (Simple Factory Pattern)
简单工厂模式虽然不属于 GoF(《设计模式》四人组)定义的 23 种经典设计模式之一,但它是理解工厂模式的绝佳入门。
核心思想
创建一个专门的工厂类,该类有一个静态方法,根据传入的参数来决定创建并返回哪种产品类的实例。
结构角色
- Factory (工厂类):核心角色,负责实现创建所有产品实例的内部逻辑。它提供一个静态方法,根据客户端请求(参数)返回不同的产品对象。
- Product (抽象产品接口):定义了所有产品需要实现的公共接口。
- ConcreteProduct (具体产品类):实现了产品接口,是工厂类创建的目标。
示例:支付方式选择
假设一个电商系统需要支持多种支付方式,如支付宝、微信支付。
代码实现 (Java):
// 1. 抽象产品接口 (Payment)
public interface Payment {
void pay();
}
// 2. 具体产品类 (Alipay, WeChatPay)
public class Alipay implements Payment {
@Override
public void pay() {
System.out.println("使用支付宝支付...");
}
}
public class WeChatPay implements Payment {
@Override
public void pay() {
System.out.println("使用微信支付...");
}
}
// 3. 工厂类 (PaymentFactory)
public class PaymentFactory {
// 静态方法,根据类型创建产品
public static Payment createPayment(String type) {
if ("alipay".equalsIgnoreCase(type)) {
return new Alipay();
} else if ("wechat".equalsIgnoreCase(type)) {
return new WeChatPay();
} else {
return null; // 或者抛出异常
}
}
}
// 4. 客户端调用
public class Client {
public static void main(String[] args) {
Payment payment = PaymentFactory.createPayment("wechat");
if (payment != null) {
payment.pay(); // 输出: 使用微信支付...
}
}
}
优缺点
- 优点:
- 结构简单,易于理解和实现。
- 将对象的创建和使用分离,客户端只依赖工厂和产品接口,不依赖具体产品类。
- 缺点:
- 违反了开闭原则(Open/Closed Principle)。每当需要增加一种新的支付方式(如银联支付),就必须修改
PaymentFactory类的createPayment方法,增加else if分支。 - 工厂类职责过重,包含了所有产品的创建逻辑,一旦工厂类出问题,整个系统都会受影响。
- 违反了开闭原则(Open/Closed Principle)。每当需要增加一种新的支付方式(如银联支付),就必须修改
2. 工厂方法模式 (Factory Method Pattern)
工厂方法模式是 GoF 的经典模式之一,它解决了简单工厂模式违反开闭原则的问题。
核心思想
定义一个用于创建对象的接口(或抽象类),但让子类来决定到底要实例化哪一个类。工厂方法将一个类的实例化延迟到其子类中进行。
结构角色
- Product (抽象产品接口):与简单工厂模式相同。
- ConcreteProduct (具体产品类):与简单工厂模式相同。
- Factory (抽象工厂):定义一个创建产品的抽象方法(工厂方法),返回一个产品对象。
- ConcreteFactory (具体工厂):实现抽象工厂接口,重写工厂方法,返回一个具体的的产品实例。
示例:支付方式选择(重构版)
代码实现 (Java):
// 产品接口和具体产品类 (Payment, Alipay, WeChatPay) 保持不变
// 3. 抽象工厂 (PaymentFactory)
public interface PaymentFactory {
Payment createPayment();
}
// 4. 具体工厂 (AlipayFactory, WeChatPayFactory)
public class AlipayFactory implements PaymentFactory {
@Override
public Payment createPayment() {
return new Alipay();
}
}
public class WeChatPayFactory implements PaymentFactory {
@Override
public Payment createPayment() {
return new WeChatPay();
}
}
// 5. 客户端调用
public class Client {
public static void main(String[] args) {
// 需要支付宝支付,就创建支付宝工厂
PaymentFactory alipayFactory = new AlipayFactory();
Payment alipay = alipayFactory.createPayment();
alipay.pay(); // 输出: 使用支付宝支付...
// 需要微信支付,就创建微信工厂
PaymentFactory wechatFactory = new WeChatPayFactory();
Payment wechatPay = wechatFactory.createPayment();
wechatPay.pay(); // 输出: 使用微信支付...
}
}
优缺点
- 优点:
- 完美遵循开闭原则。如果需要增加一种新的支付方式(如银联支付),只需增加一个
UnionPay类和一个UnionPayFactory类即可,无需修改任何现有代码。 - 将产品创建的责任下放到具体的工厂子类,实现了更好的解耦。
- 每个具体工厂只负责创建一个具体产品,职责单一。
- 完美遵循开闭原则。如果需要增加一种新的支付方式(如银联支付),只需增加一个
- 缺点:
- 每增加一个产品,就需要增加一个对应的工厂类,导致系统中的类数量成倍增加,增加了系统的复杂度和代码量。
3. 抽象工厂模式 (Abstract Factory Pattern)
抽象工厂模式是工厂模式中最复杂的一种,它处理的是“产品族(Product Family)”的创建问题。
核心思想
提供一个接口,用于创建一系列相关或相互依赖的对象(即一个产品族),而无需指定它们具体的类。
场景解释
想象一下为不同操作系统(Windows, macOS)开发一套 UI 组件(按钮、文本框)。
- 产品族1(Windows 风格):
WindowsButton,WindowsTextField - 产品族2(macOS 风格):
MacButton,MacTextField
一个工厂需要能同时生产属于同一个风格(产品族)的所有组件。比如,Windows 工厂就应该生产 Windows 风格的按钮和文本框,而不能混搭。
结构角色
- AbstractFactory (抽象工厂):声明了一组用于创建不同类型抽象产品的方法。
- ConcreteFactory (具体工厂):实现了抽象工厂的接口,负责创建某个具体产品族的全部产品。
- AbstractProduct (抽象产品):为一类产品对象声明一个接口。
- ConcreteProduct (具体产品):定义了具体工厂要创建的产品对象。
示例:跨平台 UI 组件库
代码实现 (Java):
// 1. 抽象产品接口 (Button, TextField)
public interface Button { void display(); }
public interface TextField { void display(); }
// 2. 具体产品类 (WindowsButton, MacButton, etc.)
public class WindowsButton implements Button {
public void display() { System.out.println("显示 Windows 风格按钮"); }
}
public class WindowsTextField implements TextField {
public void display() { System.out.println("显示 Windows 风格文本框"); }
}
public class MacButton implements Button {
public void display() { System.out.println("显示 macOS 风格按钮"); }
}
public class MacTextField implements TextField {
public void display() { System.out.println("显示 macOS 风格文本框"); }
}
// 3. 抽象工厂 (GUIFactory)
public interface GUIFactory {
Button createButton();
TextField createTextField();
}
// 4. 具体工厂 (WindowsFactory, MacFactory)
public class WindowsFactory implements GUIFactory {
public Button createButton() { return new WindowsButton(); }
public TextField createTextField() { return new WindowsTextField(); }
}
public class MacFactory implements GUIFactory {
public Button createButton() { return new MacButton(); }
public TextField createTextField() { return new MacTextField(); }
}
// 5. 客户端调用
public class Application {
private Button button;
private TextField textField;
public Application(GUIFactory factory) {
button = factory.createButton();
textField = factory.createTextField();
}
public void renderUI() {
button.display();
textField.display();
}
public static void main(String[] args) {
// 根据当前操作系统环境选择合适的工厂
String osName = System.getProperty("os.name").toLowerCase();
GUIFactory factory;
if (osName.contains("win")) {
factory = new WindowsFactory();
} else {
factory = new MacFactory();
}
Application app = new Application(factory);
app.renderUI();
}
}
优缺点
- 优点:
- 隔离了具体类的实现。客户端只与抽象工厂和抽象产品接口打交道。
- 易于切换产品族。只需要改变具体工厂的实例,就可以改变整个应用的行为。
- 保证了产品之间的一致性。当使用一个具体工厂时,它创建的产品一定是属于同一个产品族的,不会出现混用(如 Windows 按钮配 macOS 文本框)的情况。
- 缺点:
- 难以扩展新的产品类型。如果想给产品族增加一个新的产品(如
Checkbox),就需要修改GUIFactory抽象工厂接口,这会导致所有已有的具体工厂子类都需要进行修改,违反了开闭原则。
- 难以扩展新的产品类型。如果想给产品族增加一个新的产品(如
三种工厂模式的总结与对比
| 特性 | 简单工厂模式 | 工厂方法模式 | 抽象工厂模式 |
|---|---|---|---|
| 目的 | 创建一种产品 | 创建一种产品 | 创建一族(多个相关)产品 |
| 复杂性 | 低 | 中 | 高 |
| 关注点 | 如何创建对象 | 将创建过程延迟到子类 | 创建一个产品家族,确保产品间的兼容性 |
| 开闭原则 | 违反(增加新产品需修改工厂) | 遵循(增加新产品只需增加新工厂) | 对于新产品族遵循,对于新产品类型违反 |
| 抽象层次 | 只有一个工厂类,没有抽象层 | 有抽象工厂和具体工厂 | 有抽象工厂、具体工厂、抽象产品、具体产品 |
| 适用场景 | 创建的对象较少,且不经常变化 | 一个类不知道它所需要的对象的类 | 系统需要与多个产品系列中的一个系列进行交互 |
总而言之,工厂模式是解耦对象创建与使用的利器。从简单工厂到工厂方法,再到抽象工厂,模式的复杂度和抽象度不断提升,以应对更加复杂的对象创建场景。在实际开发中,应根据具体需求选择最合适的工厂模式。