1. 什么是工厂模式?为什么需要它?

核心思想:工厂模式是一种创建型设计模式,其核心思想是将对象的创建过程封装起来。客户端(使用对象的代码)不再直接使用 new 关键字来创建具体类的实例,而是向一个“工厂”请求一个对象,由工厂来决定到底创建哪个具体类的实例。

解决的问题
想象一下,如果没有工厂模式,你的代码可能会是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 客户端代码
void some_function(const std::string& shapeType) {
Shape* shape = nullptr;
if (shapeType == "Circle") {
shape = new Circle(); // 直接依赖具体类 Circle
} else if (shapeType == "Square") {
shape = new Square(); // 直接依赖具体类 Square
} else if (shapeType == "Triangle") {
shape = new Triangle(); // 直接依赖具体类 Triangle
}
// ...
if (shape) {
shape->draw();
delete shape; // 还需要手动管理内存
}
}

这段代码有几个严重的问题:

  1. 紧密耦合:客户端代码 some_function 与具体的 Circle, Square, Triangle 类紧密地耦合在一起。如果未来要增加一个新的形状,比如 Rectangle,你必须修改 some_function 函数,在 if-else 链中增加一个分支。这违反了开闭原则(对扩展开放,对修改关闭)。
  2. 创建逻辑重复:如果系统中有多个地方需要根据类型创建形状,那么这段 if-else 逻辑就会在各处重复出现,难以维护。
  3. 创建过程复杂:如果创建某个对象的过程非常复杂(例如,需要读取配置文件、进行复杂的初始化等),这些复杂的逻辑会散布在客户端代码中,使其变得混乱。

工厂模式的目标就是将这种对象的创建逻辑从客户端代码中分离出来,集中到一个地方进行管理,从而实现解耦封装可维护性


2. 工厂模式的分类

通常,我们将工厂模式分为三种类型,它们在复杂度和灵活性上层层递进:

  1. 简单工厂模式 (Simple Factory Pattern) - 最简单,但不属于 GoF(《设计模式》)的 23 种标准设计模式之一,但非常常用。
  2. 工厂方法模式 (Factory Method Pattern) - GoF 标准模式之一,解决了简单工厂的扩展性问题。
  3. 抽象工厂模式 (Abstract Factory Pattern) - GoF 标准模式之一,用于创建一系列相关的对象(产品族)。

下面我们逐一详细讲解。


3. 简单工厂模式 (Simple Factory Pattern)

定义:定义一个工厂类,它可以根据传入的参数来动态决定应该创建哪一个产品类的实例。

结构

  • Factory (工厂类):负责实现创建所有实例的内部逻辑。
  • Product (抽象产品):定义了所有具体产品需要实现的接口(在 C++ 中通常是抽象基类)。
  • ConcreteProduct (具体产品):实现了 Product 接口的具体类,是工厂创建的目标。

C++ 示例

我们用一个制作不同类型咖啡的例子来说明。

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
#include <iostream>
#include <string>
#include <memory>

// 1. 抽象产品 (Product)
class Coffee {
public:
virtual ~Coffee() = default;
virtual void show() const = 0;
};

// 2. 具体产品 (ConcreteProduct)
class Americano : public Coffee {
public:
void show() const override {
std::cout << "一杯美式咖啡" << std::endl;
}
};

class Latte : public Coffee {
public:
void show() const override {
std::cout << "一杯拿铁咖啡" << std::endl;
}
};

// 3. 工厂类 (Factory)
class CoffeeFactory {
public:
// 使用 C++11 的智能指针 std::unique_ptr 来管理内存
static std::unique_ptr<Coffee> createCoffee(const std::string& type) {
if (type == "Americano") {
return std::make_unique<Americano>();
} else if (type == "Latte") {
return std::make_unique<Latte>();
}
// 如果没有匹配的类型,可以返回 nullptr 或抛出异常
return nullptr;
}
};

// 客户端代码
int main() {
// 客户不需要知道 Americano 和 Latte 的具体实现
// 只需要通过工厂和类型字符串来获取对象
auto americano = CoffeeFactory::createCoffee("Americano");
if (americano) {
americano->show();
}

auto latte = CoffeeFactory::createCoffee("Latte");
if (latte) {
latte->show();
}

return 0;
}

优点

  • 封装:将对象的创建逻辑封装在工厂类中,客户端代码只与工厂和抽象产品接口打交道,实现了客户端与具体产品的解耦。
  • 简单直观:结构简单,易于理解和实现。

缺点

  • 违反开闭原则:当需要增加一种新的咖啡类型时(例如 Cappuccino),必须修改 CoffeeFactory 类的 createCoffee 方法,在 if-else 中增加一个新的分支。这不符合“对修改关闭”的原则。
  • 职责过重:所有的产品创建逻辑都集中在一个工厂类中,如果产品种类非常多,这个工厂类会变得非常庞大和臃肿。

应用场合

  • 当需要创建的对象较少,且不经常增加新产品时。
  • 客户端不关心对象的创建过程,只关心如何使用。

4. 工厂方法模式 (Factory Method Pattern)

定义:定义一个用于创建对象的接口(工厂方法),但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

结构

  • Product (抽象产品):与简单工厂中的定义相同。
  • ConcreteProduct (具体产品):与简单工厂中的定义相同。
  • Creator (抽象工厂):声明一个 FactoryMethod,该方法返回一个 Product 类型的对象。
  • ConcreteCreator (具体工厂):实现 FactoryMethod,负责创建并返回具体的 ConcreteProduct 实例。

C++ 示例

这次,我们让每个咖啡店(具体工厂)自己决定制作哪种咖啡。

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
#include <iostream>
#include <string>
#include <memory>

// 1. 抽象产品 (Product) - 和上面一样
class Coffee {
public:
virtual ~Coffee() = default;
virtual void show() const = 0;
};

// 2. 具体产品 (ConcreteProduct) - 和上面一样
class Americano : public Coffee {
public:
void show() const override {
std::cout << "一杯美式咖啡" << std::endl;
}
};

class Latte : public Coffee {
public:
void show() const override {
std::cout << "一杯拿铁咖啡" << std::endl;
}
};


// 3. 抽象工厂 (Creator)
class CoffeeFactory {
public:
virtual ~CoffeeFactory() = default;
// 这是工厂方法,它是一个纯虚函数,由子类实现
virtual std::unique_ptr<Coffee> createCoffee() const = 0;

void serveCoffee() const {
auto coffee = createCoffee();
std::cout << "为您准备好了: ";
coffee->show();
}
};

// 4. 具体工厂 (ConcreteCreator)
class AmericanoFactory : public CoffeeFactory {
public:
std::unique_ptr<Coffee> createCoffee() const override {
return std::make_unique<Americano>();
}
};

class LatteFactory : public CoffeeFactory {
public:
std::unique_ptr<Coffee> createCoffee() const override {
return std::make_unique<Latte>();
}
};


// 客户端代码
int main() {
// 想要美式咖啡,就找美式咖啡工厂
auto americano_factory = std::make_unique<AmericanoFactory>();
americano_factory->serveCoffee();

// 想要拿铁,就找拿铁工厂
auto latte_factory = std::make_unique<LatteFactory>();
latte_factory->serveCoffee();

// ======== 扩展性展示 ========
// 如果要新增一种卡布奇诺咖啡
// 1. 创建新产品 Cappuccino
class Cappuccino : public Coffee {
public:
void show() const override { std::cout << "一杯卡布奇诺" << std::endl; }
};
// 2. 创建新工厂 CappuccinoFactory
class CappuccinoFactory : public CoffeeFactory {
public:
std::unique_ptr<Coffee> createCoffee() const override {
return std::make_unique<Cappuccino>();
}
};

// 客户端代码完全不需要修改,只需要使用新的工厂即可
auto cappuccino_factory = std::make_unique<CappuccinoFactory>();
cappuccino_factory->serveCoffee();

return 0;
}

优点

  • 遵循开闭原则:当需要添加新产品时,只需添加一个新的具体产品类和一个新的具体工厂类,而无需修改任何现有代码。这使得系统扩展性极佳。
  • 职责单一:每个具体工厂只负责创建一个具体产品,符合单一职责原则。
  • 解耦:客户端代码只依赖于抽象工厂和抽象产品,与具体实现无关。

缺点

  • 类的数量增加:每增加一个产品,就需要增加一个对应的具体工厂类,这会使得系统中的类数量成倍增加,增加了系统的复杂度和代码量。

应用场合

  • 当一个类不知道它所需要的对象的具体类型时(例如,一个框架需要创建由其用户自定义的组件)。
  • 当一个类希望由它的子类来指定它所创建的对象时。
  • 当你想为用户提供一个创建对象的接口,但又不希望暴露创建对象的具体细节时(例如,在库或框架开发中)。

5. 抽象工厂模式 (Abstract Factory Pattern)

定义:提供一个接口,用于创建一系列相关或相互依赖的对象(产品族),而无需指定它们具体的类。

核心区别:工厂方法模式关注的是一个产品的创建,而抽象工厂模式关注的是一个产品族(多个不同类型但相互关联的产品)的创建。

结构

  • AbstractFactory (抽象工厂):声明一组用于创建抽象产品的接口(例如 createProductA(), createProductB())。
  • ConcreteFactory (具体工厂):实现 AbstractFactory 的接口,负责创建一组具体的产品。
  • AbstractProduct (抽象产品):为一类产品对象声明一个接口(例如 ProductA, ProductB)。
  • ConcreteProduct (具体产品):定义了由相应的具体工厂创建的具体产品对象。

C++ 示例

假设我们要创建一个跨平台的 GUI 库,它需要提供按钮(Button)和复选框(Checkbox)。我们希望它能同时支持 Windows 和 macOS 风格。

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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#include <iostream>
#include <string>
#include <memory>

// ---- 抽象产品 ----
// 1. 抽象产品 A: Button
class Button {
public:
virtual ~Button() = default;
virtual void paint() const = 0;
};

// 2. 抽象产品 B: Checkbox
class Checkbox {
public:
virtual ~Checkbox() = default;
virtual void check() const = 0;
};

// ---- Windows 产品族 ----
class WindowsButton : public Button {
public:
void paint() const override {
std::cout << "渲染一个 Windows 风格的按钮" << std::endl;
}
};
class WindowsCheckbox : public Checkbox {
public:
void check() const override {
std::cout << "勾选一个 Windows 风格的复选框" << std::endl;
}
};

// ---- macOS 产品族 ----
class MacOSButton : public Button {
public:
void paint() const override {
std::cout << "渲染一个 macOS 风格的按钮" << std::endl;
}
};
class MacOSCheckbox : public Checkbox {
public:
void check() const override {
std::cout << "勾选一个 macOS 风格的复选框" << std::endl;
}
};

// ---- 抽象工厂 ----
// 定义了创建一族产品 (Button 和 Checkbox) 的接口
class GUIFactory {
public:
virtual ~GUIFactory() = default;
virtual std::unique_ptr<Button> createButton() const = 0;
virtual std::unique_ptr<Checkbox> createCheckbox() const = 0;
};

// ---- 具体工厂 ----
// 1. Windows 工厂,负责创建所有 Windows 风格的组件
class WindowsFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() const override {
return std::make_unique<WindowsButton>();
}
std::unique_ptr<Checkbox> createCheckbox() const override {
return std::make_unique<WindowsCheckbox>();
}
};

// 2. macOS 工厂,负责创建所有 macOS 风格的组件
class MacOSFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() const override {
return std::make_unique<MacOSButton>();
}
std::unique_ptr<Checkbox> createCheckbox() const override {
return std::make_unique<MacOSCheckbox>();
}
};

// 客户端代码
class Application {
private:
std::unique_ptr<GUIFactory> factory;
std::unique_ptr<Button> button;
std::unique_ptr<Checkbox> checkbox;
public:
explicit Application(std::unique_ptr<GUIFactory> f) : factory(std::move(f)) {}

void createUI() {
button = factory->createButton();
checkbox = factory->createCheckbox();
}

void run() {
button->paint();
checkbox->check();
}
};

int main() {
// 根据当前环境选择一个工厂
// 假设我们在 Windows 环境
std::cout << "在 Windows 环境下运行:" << std::endl;
auto win_factory = std::make_unique<WindowsFactory>();
Application app_win(std::move(win_factory));
app_win.createUI();
app_win.run();

std::cout << "\n切换到 macOS 环境运行:" << std::endl;
// 假设切换到了 macOS 环境
auto mac_factory = std::make_unique<MacOSFactory>();
Application app_mac(std::move(mac_factory));
app_mac.createUI();
app_mac.run();

return 0;
}

优点

  • 隔离具体类:客户端代码完全与具体产品实现分离,只依赖于抽象工厂和抽象产品接口。
  • 易于切换产品族:只需要改变具体工厂的实例,就可以轻松地切换整个产品族。在上面的例子中,从 Windows 风格切换到 macOS 风格只需要更换工厂对象。
  • 保证产品兼容性:由于一个具体工厂只创建属于同一产品族的产品,因此可以保证这些产品之间是相互兼容的。

缺点

  • 难以扩展新的产品类型:如果要在产品族中增加一个新的产品(例如,增加一个 TextField),那么需要修改抽象工厂 GUIFactory 的接口,这会导致所有具体工厂子类都需要进行修改,违反了开闭原则。

应用场合

  • 当一个系统需要与多个产品系列中的一个系列进行配置时。
  • 当系统要独立于其产品的创建、组合和表示时。
  • 当你需要提供一个产品类库,而只想暴露它们的接口而不是实现时。
  • 例如:更换数据库访问层(一个工厂用于 SQL Server,一个用于 Oracle),更换UI主题,支持不同的操作系统等。

6. 总结与对比

特性 简单工厂模式 工厂方法模式 抽象工厂模式
目的 封装单一产品的创建 封装单一产品的创建,但将决策延迟到子类 封装一个产品族的创建
复杂度
开闭原则 违反 (修改工厂类) 遵守 (添加新工厂) 遵守 (添加新产品族),违反 (添加新产品类型)
代码结构 一个工厂类,多个产品类 抽象工厂+具体工厂,抽象产品+具体产品 抽象工厂+具体工厂,多组抽象产品+具体产品
关注点 如何创建一个产品 如何创建一个产品 如何创建一组产品

7. C++ 特有的考量

  • 智能指针:在 C++11 及以后的版本中,工厂方法返回对象时应优先使用智能指针(std::unique_ptrstd::shared_ptr)来管理对象的生命周期,避免内存泄漏。std::unique_ptr 通常是首选,因为它表达了所有权的唯一性,开销也更低。
  • 模板元编程:对于某些场景,可以使用模板来实现更泛化的工厂,减少手写具体工厂类的需要,但这会增加编译期复杂性。