抽象工厂模式(Abstract Factory)是一种创建型设计模式 ,其核心思想是:为一组相关或相互依赖的对象提供一个创建接口,而无需指定它们具体的类。简单来说,就是一个工厂可以生产一系列相关的对象。
我们接下来通过逐步拆解、举例说明和详细代码注释来理解这个模式。
1. 什么是抽象工厂模式?
举个简单例子:
想象你是个游戏开发者,需要为游戏开发不同风格的用户界面(UI)。
- 如果玩家在 Windows 上玩,游戏需要提供 Windows 风格的按钮、文本框。
- 如果玩家在 Mac 上玩,则需要提供 Mac 风格的按钮、文本框。
这里的问题是:
- 我们需要根据不同的操作系统 来生成一套成对的 UI 组件(按钮 + 文本框)。
- 不同的 UI 组件风格相互独立,代码却不能依赖具体的组件实现。
解决这个问题的方案就是使用抽象工厂模式。它允许我们将"创建对象"的逻辑与对象的具体实现分离开。
2. 模式的核心要素
抽象工厂模式包含以下几部分:
- 抽象工厂:定义创建一系列对象的接口。
- 具体工厂:实现创建特定风格对象的工厂。
- 抽象产品:定义产品的公共接口(如按钮和文本框的接口)。
- 具体产品:特定风格的产品实现。
- 客户端:通过抽象工厂创建产品,而不关心具体产品的实现。
下面用代码一步步实现这个设计模式。
3. 详细代码实现与讲解
Step 1: 定义抽象产品
按钮和文本框是两种产品。我们需要定义它们的接口,让不同风格的具体产品实现这些接口。
cpp
#include <iostream>
#include <memory> // 用于智能指针
// 抽象产品 A: 按钮
class Button {
public:
virtual void render() const = 0; // 渲染按钮
virtual ~Button() = default; // 虚析构,避免内存泄漏
};
// 抽象产品 B: 文本框
class TextBox {
public:
virtual void render() const = 0; // 渲染文本框
virtual ~TextBox() = default;
};
解释:
Button
和TextBox
是两个抽象接口,定义了按钮和文本框的行为(render()
方法)。- 使用抽象类的好处是,客户端代码可以依赖于接口,而不是具体的实现。
Step 2: 定义具体产品
实现两种风格(Windows 和 Mac)的具体按钮和文本框。
cpp
// 具体产品 A1: Windows 风格按钮
class WindowsButton : public Button {
public:
void render() const override {
std::cout << "Rendering Windows-style Button\n";
}
};
// 具体产品 B1: Windows 风格文本框
class WindowsTextBox : public TextBox {
public:
void render() const override {
std::cout << "Rendering Windows-style TextBox\n";
}
};
// 具体产品 A2: Mac 风格按钮
class MacButton : public Button {
public:
void render() const override {
std::cout << "Rendering Mac-style Button\n";
}
};
// 具体产品 B2: Mac 风格文本框
class MacTextBox : public TextBox {
public:
void render() const override {
std::cout << "Rendering Mac-style TextBox\n";
}
};
解释:
WindowsButton
和MacButton
分别实现了Button
接口。WindowsTextBox
和MacTextBox
分别实现了TextBox
接口。- 每个具体类实现了它们特定风格的
render()
方法,输出不同的效果。
Step 3: 定义抽象工厂
工厂负责创建相关产品(按钮 + 文本框)。我们定义一个抽象工厂接口,声明创建按钮和文本框的方法。
cpp
// 抽象工厂:定义创建一组产品的方法
class GUIFactory {
public:
virtual std::unique_ptr<Button> createButton() const = 0; // 创建按钮
virtual std::unique_ptr<TextBox> createTextBox() const = 0; // 创建文本框
virtual ~GUIFactory() = default;
};
解释:
GUIFactory
是抽象工厂,定义了创建按钮和文本框的接口。- 返回值使用了
std::unique_ptr
,可以自动管理对象生命周期,防止内存泄漏。
Step 4: 定义具体工厂
实现两个具体工厂,用于创建 Windows 和 Mac 风格的产品。
cpp
// 具体工厂 1: Windows 工厂
class WindowsFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() const override {
return std::make_unique<WindowsButton>(); // 创建 Windows 按钮
}
std::unique_ptr<TextBox> createTextBox() const override {
return std::make_unique<WindowsTextBox>(); // 创建 Windows 文本框
}
};
// 具体工厂 2: Mac 工厂
class MacFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() const override {
return std::make_unique<MacButton>(); // 创建 Mac 按钮
}
std::unique_ptr<TextBox> createTextBox() const override {
return std::make_unique<MacTextBox>(); // 创建 Mac 文本框
}
};
解释:
WindowsFactory
和MacFactory
分别实现了GUIFactory
,负责生成各自风格的产品。- 每个工厂实现了
createButton()
和createTextBox()
。
Step 5: 编写客户端代码
客户端通过抽象工厂使用产品,而不关心具体实现。
cpp
// 客户端代码:通过工厂创建和使用产品
void renderUI(const GUIFactory& factory) {
auto button = factory.createButton(); // 创建按钮
auto textBox = factory.createTextBox(); // 创建文本框
button->render(); // 渲染按钮
textBox->render(); // 渲染文本框
}
int main() {
std::cout << "Windows GUI:\n";
WindowsFactory windowsFactory;
renderUI(windowsFactory); // 使用 Windows 工厂
std::cout << "\nMac GUI:\n";
MacFactory macFactory;
renderUI(macFactory); // 使用 Mac 工厂
return 0;
}
解释:
renderUI
是客户端函数,通过GUIFactory
创建产品。- 客户端无需关心具体工厂或产品的实现,只依赖于抽象接口。
4. 输出结果
运行上述代码,将输出以下结果:
Windows GUI:
Rendering Windows-style Button
Rendering Windows-style TextBox
Mac GUI:
Rendering Mac-style Button
Rendering Mac-style TextBox
5. 模式的优缺点
优点
- 分离具体类:客户端与具体产品的实现解耦。
- 确保产品一致性:某个具体工厂生产的所有产品风格一致。
- 易于扩展:可以新增工厂和产品系列,而无需修改客户端代码。
缺点
- 增加复杂性:需要定义多组接口和类,代码量较多。
- 扩展产品族困难:如果需要新增产品(比如菜单),需要修改所有工厂接口和实现。
总结
抽象工厂模式通过工厂接口屏蔽了具体产品的创建细节,使代码更加灵活和可扩展。它非常适合需要生成一组相关对象 且不希望客户端代码依赖于具体实现的场景。在实际开发中,抽象工厂模式广泛应用于跨平台工具、插件系统等领域。
完整代码实现
以下是抽象工厂模式的完整实现,并将所有输出改为中文形式,便于理解。
cpp
#include <iostream>
#include <memory> // 用于智能指针管理
// 抽象产品 A: 按钮
class Button {
public:
virtual void render() const = 0; // 渲染按钮(纯虚函数)
virtual ~Button() = default; // 虚析构,防止内存泄漏
};
// 抽象产品 B: 文本框
class TextBox {
public:
virtual void render() const = 0; // 渲染文本框(纯虚函数)
virtual ~TextBox() = default;
};
// 具体产品 A1: Windows 风格按钮
class WindowsButton : public Button {
public:
void render() const override {
std::cout << "渲染 Windows 风格按钮" << "\n"; // 中文输出
}
};
// 具体产品 B1: Windows 风格文本框
class WindowsTextBox : public TextBox {
public:
void render() const override {
std::cout << "渲染 Windows 风格文本框" << "\n"; // 中文输出
}
};
// 具体产品 A2: Mac 风格按钮
class MacButton : public Button {
public:
void render() const override {
std::cout << "渲染 Mac 风格按钮" << "\n"; // 中文输出
}
};
// 具体产品 B2: Mac 风格文本框
class MacTextBox : public TextBox {
public:
void render() const override {
std::cout << "渲染 Mac 风格文本框" << "\n"; // 中文输出
}
};
// 抽象工厂:定义创建一组产品的方法
class GUIFactory {
public:
virtual std::unique_ptr<Button> createButton() const = 0; // 创建按钮
virtual std::unique_ptr<TextBox> createTextBox() const = 0; // 创建文本框
virtual ~GUIFactory() = default;
};
// 具体工厂 1: Windows 工厂
class WindowsFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() const override {
return std::make_unique<WindowsButton>(); // 创建 Windows 按钮
}
std::unique_ptr<TextBox> createTextBox() const override {
return std::make_unique<WindowsTextBox>(); // 创建 Windows 文本框
}
};
// 具体工厂 2: Mac 工厂
class MacFactory : public GUIFactory {
public:
std::unique_ptr<Button> createButton() const override {
return std::make_unique<MacButton>(); // 创建 Mac 按钮
}
std::unique_ptr<TextBox> createTextBox() const override {
return std::make_unique<MacTextBox>(); // 创建 Mac 文本框
}
};
// 客户端代码:通过工厂创建和使用产品
void renderUI(const GUIFactory& factory) {
// 使用工厂创建按钮和文本框
auto button = factory.createButton(); // 创建按钮
auto textBox = factory.createTextBox(); // 创建文本框
// 渲染创建的产品
button->render();
textBox->render();
}
int main() {
// 使用 Windows 工厂创建产品
std::cout << "Windows 界面:" << "\n"; // 中文输出
WindowsFactory windowsFactory;
renderUI(windowsFactory);
// 使用 Mac 工厂创建产品
std::cout << "\nMac 界面:" << "\n"; // 中文输出
MacFactory macFactory;
renderUI(macFactory);
return 0;
}
输出结果
运行该程序,输出如下:
Windows 界面:
渲染 Windows 风格按钮
渲染 Windows 风格文本框
Mac 界面:
渲染 Mac 风格按钮
渲染 Mac 风格文本框
本文由mdnice多平台发布