一、工厂方法模式的局限性

工厂方法模式仅能创建单一产品等级结构的对象,当系统需要创建多个相互关联的产品族时,工厂方法模式便显现出明显不足。例如,在开发跨平台 UI 组件时,需要同时创建 Windows 风格的按钮和文本框,以及 macOS 风格的按钮和文本框,这些按钮和文本框分别构成不同的产品族。若使用工厂方法模式,需为每个产品(按钮、文本框)都创建对应的工厂类,会导致类的数量急剧增加,且难以保证同一产品族内产品的一致性。而抽象工厂模式恰好能解决这一问题,它可提供一个接口,用于创建一系列相关或相互依赖的对象,无需指定它们的具体类。

二、抽象工厂模式结构

2.1 结构文字描述

抽象工厂模式包含四个核心角色:

  1. 抽象工厂(Abstract Factory):声明一组用于创建一族产品的方法,每个方法对应一种产品。

  2. 具体工厂(Concrete Factory):实现抽象工厂中声明的创建产品的方法,生成具体的产品对象,一个具体工厂对应一个产品族。

  3. 抽象产品(Abstract Product):为每种产品声明接口,定义产品的公共方法。

  4. 具体产品(Concrete Product):实现抽象产品接口,是具体工厂创建的目标对象,一个具体产品属于某个具体工厂对应的产品族。

2.2 流程图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
+-------------------+
| 抽象工厂 |
| (AbstractFactory)|
+-------------------+
|
| 声明创建产品A、产品B的方法
|
+----------------------+----------------------+
| | |
+-------------------+ +-------------------+ +-------------------+
| 具体工厂1 | | 具体工厂2 | | 具体工厂3 |
| (ConcreteFactory1)| | (ConcreteFactory2)| | (ConcreteFactory3)|
+-------------------+ +-------------------+ +-------------------+
| | |
| 实现创建产品A1、 | 实现创建产品A2、 | 实现创建产品A3、
| 产品B1的方法 | 产品B2的方法 | 产品B3的方法
| | |
+-------------------+ +-------------------+ +-------------------+
| 具体产品A1 | | 具体产品A2 | | 具体产品A3 |
| (ConcreteProductA1)| | (ConcreteProductA2)| | (ConcreteProductA3)|
+-------------------+ +-------------------+ +-------------------+
| | |
| 实现产品A接口 | 实现产品A接口 | 实现产品A接口
| | |
+-------------------+ +-------------------+ +-------------------+
| 具体产品B1 | | 具体产品B2 | | 具体产品B3 |
| (ConcreteProductB1)| | (ConcreteProductB2)| | (ConcreteProductB3)|
+-------------------+ +-------------------+ +-------------------+
| | |
| 实现产品B接口 | 实现产品B接口 | 实现产品B接口

三、完整实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include <iostream>
#include <memory>
#include <string>

// 抽象产品A:按钮
class Button {
public:
virtual ~Button() = default;
virtual void display() const = 0;
};

// 抽象产品B:文本框
class TextBox {
public:
virtual ~TextBox() = default;
virtual void display() const = 0;
};

// 具体产品A1:Windows风格按钮
class WindowsButton : public Button {
public:
void display() const override {
std::cout << "显示Windows风格按钮" << std::endl;
}
};

// 具体产品B1:Windows风格文本框
class WindowsTextBox : public TextBox {
public:
void display() const override {
std::cout << "显示Windows风格文本框" << std::endl;
}
};

// 具体产品A2:macOS风格按钮
class MacOSButton : public Button {
public:
void display() const override {
std::cout << "显示macOS风格按钮" << std::endl;
}
};

// 具体产品B2:macOS风格文本框
class MacOSTextBox : public TextBox {
public:
void display() const override {
std::cout << "显示macOS风格文本框" << std::endl;
}
};

// 抽象工厂:UI组件工厂
class UIFactory {
public:
virtual ~UIFactory() = default;
virtual std::unique_ptr<Button> createButton() = 0;
virtual std::unique_ptr<TextBox> createTextBox() = 0;
};

// 具体工厂1:Windows UI组件工厂
class WindowsUIFactory : public UIFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<WindowsButton>();
}

std::unique_ptr<TextBox> createTextBox() override {
return std::make_unique<WindowsTextBox>();
}
};

// 具体工厂2:macOS UI组件工厂
class MacOSUIFactory : public UIFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<MacOSButton>();
}

std::unique_ptr<TextBox> createTextBox() override {
return std::make_unique<MacOSTextBox>();
}
};

// 客户端代码
void clientCode(const std::unique_ptr<UIFactory>& factory) {
auto button = factory->createButton();
auto textbox = factory->createTextBox();
button->display();
textbox->display();
}

int main() {
std::cout << "客户端使用Windows UI组件工厂:" << std::endl;
auto windowsFactory = std::make_unique<WindowsUIFactory>();
clientCode(windowsFactory);

std::cout << "\n客户端使用macOS UI组件工厂:" << std::endl;
auto macOSFactory = std::make_unique<MacOSUIFactory>();
clientCode(macOSFactory);

return 0;
}

四、模式总结

4.1 适用场景

  1. 系统需要创建多个相互关联的产品族,且需保证同一产品族内产品的一致性。

  2. 系统不依赖于具体产品的创建、组合和表示细节,客户端仅通过抽象接口访问产品。

  3. 系统需动态切换不同的产品族,如跨平台应用中切换不同系统风格的组件。

4.2 常见误区

  1. 过度使用抽象工厂模式:当系统仅需创建单一产品等级结构的对象时,使用工厂方法模式即可,无需使用抽象工厂模式,避免增加系统复杂度。

  2. 产品族扩展困难:抽象工厂模式中,若需新增产品族中的产品类型(如在 UI 组件中新增下拉框),需修改抽象工厂接口及所有具体工厂类,违反开闭原则,因此在设计时需提前明确产品族的范围。

  3. 忽视智能指针的使用:在 C++ 实现中,若未使用智能指针(如 std::unique_ptr)管理产品对象的生命周期,易出现内存泄漏问题,需注重资源的正确管理。