在面向对象软件设计中,对象的创建是最基础也最容易产生耦合的环节。当业务代码中散落着大量 new 关键字时,每新增一类产品就需要修改多处业务逻辑,代码的维护成本会随功能迭代指数级上升。
工厂模式作为最经典的创建型设计模式,核心思想是将对象的创建与使用分离:把易变的对象创建逻辑封装在工厂类内部,上层业务代码仅依赖抽象接口,不感知具体产品的实现细节,从而大幅提升代码的扩展性、可维护性与可读性。
本文将从最简单的简单工厂讲起,逐步深入工厂方法、抽象工厂两种经典形态,结合现代 C++ 特性给出工程级最佳实现,并拆解不同场景下的选型原则。
一、简单工厂模式:入门级封装
1.1 核心思想
用一个统一的工厂类,根据传入的参数动态决定创建哪一个具体产品的实例。所有产品的创建逻辑都集中在这一个工厂中,客户端无需关心对象创建细节,只需传入类型标识即可获取对应产品。
1.2 代码实现
以图形创建为例,基于现代 C++ 智能指针实现:
cpp
#include <iostream>
#include <memory>
#include <string>
// 抽象产品:定义所有图形的公共接口
class Shape {
public:
virtual ~Shape() = default; // 虚析构函数,避免内存泄漏
virtual void draw() const = 0;
};
// 具体产品:圆形
class Circle : public Shape {
public:
void draw() const override {
std::cout << "绘制圆形" << std::endl;
}
};
// 具体产品:矩形
class Rectangle : public Shape {
public:
void draw() const override {
std::cout << "绘制矩形" << std::endl;
}
};
// 简单工厂:统一创建所有图形
class ShapeFactory {
public:
// 静态方法,直接通过类名调用
static std::unique_ptr<Shape> createShape(const std::string& type) {
if (type == "circle") {
return std::make_unique<Circle>();
} else if (type == "rectangle") {
return std::make_unique<Rectangle>();
} else {
return nullptr;
}
}
};
// 客户端代码
int main() {
auto circle = ShapeFactory::createShape("circle");
if (circle) circle->draw();
auto rect = ShapeFactory::createShape("rectangle");
if (rect) rect->draw();
return 0;
}
1.3 优缺点分析
✅ 优点
- 实现简单,代码量少,上手成本低
- 实现了对象创建与使用的初步分离
- 客户端无需记忆所有具体产品类名
❌ 缺点
- 违反开闭原则:新增产品必须修改工厂类的创建逻辑
- 工厂类职责过重,产品类型增多后会演变为 "上帝类"
- 扩展性差,不适合产品频繁迭代的场景
1.4 适用场景
- 产品类型较少且长期稳定,不会频繁新增
- 业务逻辑简单,对扩展性要求不高
- 快速原型开发、工具类项目
二、工厂方法模式:符合开闭原则的标准实现
2.1 核心思想
将单一工厂拆分为抽象工厂接口 + 多个具体工厂,每个具体产品对应一个专属工厂。抽象工厂定义产品创建的统一接口,具体工厂负责创建对应的产品实例。
这是 GoF 设计模式中正式定义的工厂模式,完全符合开闭原则:新增产品只需新增对应的产品类和工厂类,无需修改任何原有代码。
2.2 代码实现
cpp
#include <iostream>
#include <memory>
// 抽象产品
class Shape {
public:
virtual ~Shape() = default;
virtual void draw() const = 0;
};
// 具体产品:圆形
class Circle : public Shape {
public:
void draw() const override {
std::cout << "绘制圆形" << std::endl;
}
};
// 具体产品:矩形
class Rectangle : public Shape {
public:
void draw() const override {
std::cout << "绘制矩形" << std::endl;
}
};
// 抽象工厂:定义创建产品的统一接口
class ShapeFactory {
public:
virtual ~ShapeFactory() = default;
virtual std::unique_ptr<Shape> createShape() const = 0;
};
// 具体工厂:圆形工厂
class CircleFactory : public ShapeFactory {
public:
std::unique_ptr<Shape> createShape() const override {
return std::make_unique<Circle>();
}
};
// 具体工厂:矩形工厂
class RectangleFactory : public ShapeFactory {
public:
std::unique_ptr<Shape> createShape() const override {
return std::make_unique<Rectangle>();
}
};
// 客户端代码
int main() {
// 客户端仅依赖抽象接口
std::unique_ptr<ShapeFactory> factory = std::make_unique<CircleFactory>();
auto shape = factory->createShape();
shape->draw();
// 更换产品仅需更换工厂,业务代码零修改
factory = std::make_unique<RectangleFactory>();
shape = factory->createShape();
shape->draw();
return 0;
}
2.3 优缺点分析
✅ 优点
- 完全符合开闭原则:新增产品仅需扩展,无需修改原有代码
- 职责单一,每个工厂仅负责一类产品的创建
- 扩展性极强,支持产品的无限迭代
- 可对产品创建过程做精细化控制(初始化、参数校验等)
❌ 缺点
- 类的数量成倍增长:每新增一个产品,就要对应新增一个工厂类
- 系统复杂度和理解成本上升
- 客户端需要感知不同工厂类的存在
2.4 适用场景
- 产品类型较多,且会频繁新增迭代
- 客户端不关心产品创建细节,仅依赖产品抽象接口
- 对代码扩展性、可维护性有较高要求的中大型项目
三、抽象工厂模式:产品族的创建方案
3.1 核心思想
用于创建一系列相互关联、相互依赖的产品对象(产品族),而非单一产品。核心是保证同一系列的产品被配套使用,避免不同系列产品混用。
例如 UI 主题系统:浅色主题对应浅色按钮、浅色输入框;深色主题对应深色按钮、深色输入框。抽象工厂可以保证同一主题下的所有组件风格统一。
3.2 代码实现
cpp
#include <iostream>
#include <memory>
// 第一类产品:按钮
class Button {
public:
virtual ~Button() = default;
virtual void render() const = 0;
};
class LightButton : public Button {
public:
void render() const override {
std::cout << "渲染浅色按钮" << std::endl;
}
};
class DarkButton : public Button {
public:
void render() const override {
std::cout << "渲染深色按钮" << std::endl;
}
};
// 第二类产品:输入框
class InputBox {
public:
virtual ~InputBox() = default;
virtual void render() const = 0;
};
class LightInputBox : public InputBox {
public:
void render() const override {
std::cout << "渲染浅色输入框" << std::endl;
}
};
class DarkInputBox : public InputBox {
public:
void render() const override {
std::cout << "渲染深色输入框" << std::endl;
}
};
// 抽象工厂:定义创建整个产品族的接口
class UIFactory {
public:
virtual ~UIFactory() = default;
virtual std::unique_ptr<Button> createButton() const = 0;
virtual std::unique_ptr<InputBox> createInputBox() const = 0;
};
// 具体工厂:浅色主题工厂
class LightThemeFactory : public UIFactory {
public:
std::unique_ptr<Button> createButton() const override {
return std::make_unique<LightButton>();
}
std::unique_ptr<InputBox> createInputBox() const override {
return std::make_unique<LightInputBox>();
}
};
// 具体工厂:深色主题工厂
class DarkThemeFactory : public UIFactory {
public:
std::unique_ptr<Button> createButton() const override {
return std::make_unique<DarkButton>();
}
std::unique_ptr<InputBox> createInputBox() const override {
return std::make_unique<DarkInputBox>();
}
};
// 客户端代码
int main() {
// 切换主题仅需更换工厂,保证所有组件风格统一
std::unique_ptr<UIFactory> uiFactory = std::make_unique<DarkThemeFactory>();
auto btn = uiFactory->createButton();
auto input = uiFactory->createInputBox();
btn->render();
input->render();
return 0;
}
3.3 优缺点分析
✅ 优点
- 保证产品族内的组件相互兼容、风格统一
- 将产品族的创建逻辑集中管理,替换整个产品族非常方便
- 产品族扩展符合开闭原则
❌ 缺点
- 产品等级扩展困难:若新增一类产品(如下拉菜单),需要修改所有抽象工厂和具体工厂
- 系统结构最复杂,类数量最多,理解成本最高
3.4 适用场景
- 存在多个相互关联的产品系列,且需要保证系列内产品配套使用
- 系统结构稳定,不会频繁新增产品等级
- 典型场景:UI 主题系统、数据库访问层、跨平台组件库
四、三种经典工厂模式横向对比
表格
| 对比维度 | 简单工厂 | 工厂方法 | 抽象工厂 |
|---|---|---|---|
| 核心逻辑 | 一个工厂创建所有产品 | 一个产品对应一个工厂 | 一个工厂创建一个产品族 |
| 开闭原则 | 不符合(新增产品需改工厂) | 完全符合 | 产品族扩展符合,产品等级扩展不符合 |
| 代码复杂度 | 低 | 中 | 高 |
| 产品维度 | 单一产品 | 单一产品 | 多系列产品族 |
| 适用场景 | 产品少、稳定 | 产品多、频繁迭代 | 多系列关联产品 |
五、现代 C++ 工厂模式进阶实践
在工业级 C++ 项目中,经典工厂模式通常会结合现代语言特性做优化,解决类爆炸、if-else 膨胀等问题。
5.1 注册式工厂:彻底消除 if-else
这是大型项目最推荐的实现方式,核心是通过注册表 + 回调函数实现产品的自动注册,新增产品完全无需修改工厂代码,真正做到开闭原则。
cpp
#include <iostream>
#include <memory>
#include <unordered_map>
#include <functional>
#include <string>
// 抽象产品
class Shape {
public:
virtual ~Shape() = default;
virtual void draw() const = 0;
};
// 注册式工厂(单例)
class ShapeFactory {
public:
using Creator = std::function<std::unique_ptr<Shape>()>;
// 获取全局唯一工厂实例
static ShapeFactory& instance() {
static ShapeFactory factory;
return factory;
}
// 注册产品
void registerShape(const std::string& key, Creator creator) {
creators_[key] = std::move(creator);
}
// 创建产品
std::unique_ptr<Shape> create(const std::string& key) {
auto it = creators_.find(key);
if (it != creators_.end()) {
return it->second();
}
return nullptr;
}
private:
ShapeFactory() = default;
std::unordered_map<std::string, Creator> creators_;
};
// 辅助宏:简化产品注册
#define REGISTER_SHAPE(Class, Key) \
static bool _reg_##Class = []() { \
ShapeFactory::instance().registerShape(Key, []() { \
return std::make_unique<Class>(); \
}); \
return true; \
}()
// ====== 具体产品:仅需定义类 + 一行注册,无需修改工厂 ======
class Circle : public Shape {
public:
void draw() const override {
std::cout << "绘制圆形" << std::endl;
}
};
REGISTER_SHAPE(Circle, "circle");
class Rectangle : public Shape {
public:
void draw() const override {
std::cout << "绘制矩形" << std::endl;
}
};
REGISTER_SHAPE(Rectangle, "rectangle");
// 客户端代码
int main() {
auto circle = ShapeFactory::instance().create("circle");
if (circle) circle->draw();
auto rect = ShapeFactory::instance().create("rectangle");
if (rect) rect->draw();
return 0;
}
这种模式天然支持插件化扩展:新增产品只需在独立的源文件中定义类并注册,工厂代码永远无需改动。
5.2 模板工厂:消除重复代码
针对工厂方法模式的 "类爆炸" 问题,可以通过模板泛化具体工厂,减少冗余代码:
cpp
// 模板通用工厂
template <typename ProductT>
class TemplateShapeFactory : public ShapeFactory {
public:
std::unique_ptr<Shape> createShape() const override {
return std::make_unique<ProductT>();
}
};
// 使用方式:无需为每个产品单独写工厂类
std::unique_ptr<ShapeFactory> circleFactory =
std::make_unique<TemplateShapeFactory<Circle>>();
5.3 内存管理最佳实践
- 优先使用
std::unique_ptr作为返回值,明确所有权,无额外引用计数开销 - 抽象基类必须定义虚析构函数,避免通过父类指针销毁对象时发生内存泄漏
- 禁止返回裸指针,杜绝手动
delete导致的泄漏和重复释放问题
六、工业界典型应用场景
工厂模式在 C++ 软件开发中无处不在,典型应用包括:
- 日志系统:根据配置创建控制台日志、文件日志、远程日志等不同输出渠道
- 数据库访问层:创建 MySQL、PostgreSQL、SQLite 等不同数据库的连接对象
- 插件框架:动态加载并创建第三方插件实例,实现功能的热扩展
- 游戏开发:创建不同类型的敌人、道具、技能等游戏对象
- GUI 框架:创建跨平台的窗口、按钮、菜单等 UI 组件
七、避坑指南与选型原则
7.1 常见误区
- 过度设计 :如果只有 1-2 种产品且长期稳定,直接
new对象即可,无需强行引入工厂模式 - 工厂职责膨胀:不要在工厂中掺杂业务逻辑,工厂仅负责对象创建与初始化
- 忽略生命周期管理:返回裸指针极易导致内存泄漏,现代 C++ 项目必须使用智能指针
7.2 选型原则
- 产品少、迭代慢 → 简单工厂,用最少的代码满足需求
- 产品多、迭代快 → 工厂方法,保证扩展性的同时控制复杂度
- 存在多系列关联产品 → 抽象工厂,保证产品族的一致性
- 大型项目、插件化架构 → 注册式工厂,实现完全的开闭原则
总结
工厂模式的本质是封装变化:将对象创建这个最易变的环节隔离封装,让稳定的业务逻辑不再依赖易变的创建细节。设计模式没有绝对的优劣,选择哪种形态,始终取决于业务的复杂度、迭代频率和团队的维护成本。
对于绝大多数 C++ 项目,工厂方法模式是扩展性与复杂度的最佳平衡点;而对于中大型系统,注册式工厂是工业界的主流选择,它兼顾了灵活性与可维护性,是真正能支撑项目长期迭代的方案。