工厂模式是 创建型设计模式 的核心成员,其核心目标是:封装对象的创建过程,将对象创建与使用逻辑解耦,通过统一的"工厂"接口创建不同类型的对象,无需暴露对象的具体创建细节。
在 C++ 开发中,工厂模式广泛应用于对象创建逻辑复杂、产品类型多变、需要统一管理对象生命周期的场景(如框架组件、插件系统、数据库连接池、UI 组件库等)。根据封装程度和适用场景,工厂模式分为三类:简单工厂模式 、工厂方法模式 、抽象工厂模式,三者是"从简单到复杂、从单一到多样"的递进关系。
一、工厂模式的核心目标与设计思想
1.1 核心目标
- 封装创建逻辑:将对象的创建细节(如构造参数、初始化步骤、依赖注入)隐藏在工厂类中,使用者无需关心"如何创建",只需调用工厂接口即可。
- 解耦创建与使用:使用者仅依赖产品的抽象接口(而非具体实现),修改产品实现或新增产品时,无需修改使用代码(符合"开闭原则")。
- 统一管理对象:通过工厂集中控制对象的创建、初始化、销毁,便于添加日志、缓存、权限校验等统一逻辑。
- 支持灵活扩展:新增产品时,只需扩展工厂或新增具体工厂,无需修改现有代码。
1.2 核心设计思想
- 依赖倒置原则:依赖抽象(产品接口、工厂接口),不依赖具体实现(具体产品、具体工厂)。
- 里氏替换原则:具体产品可替换抽象产品,具体工厂可替换抽象工厂,不影响使用逻辑。
- 单一职责原则:工厂类仅负责对象创建,产品类仅负责业务逻辑,各司其职。
二、工厂模式的三种核心类型(含 C++ 实现)
2.1 简单工厂模式(Simple Factory Pattern)
定义
由一个 单一工厂类 负责所有产品的创建,通过传入参数(如类型标识),工厂类动态决定创建哪种具体产品。本质是"集中式创建",将所有产品的创建逻辑封装在一个工厂中。
结构组成
- 抽象产品(Product):所有具体产品的基类(通常是抽象类/接口,含纯虚函数定义核心业务方法)。
- 具体产品(ConcreteProduct):抽象产品的实现类,包含具体业务逻辑。
- 工厂类(Factory):核心类,提供静态或成员方法,根据参数创建并返回具体产品的指针/引用。
C++ 实现代码
cpp
#include <iostream>
#include <string>
#include <memory> // 智能指针,避免内存泄漏
// 1. 抽象产品:定义产品的核心接口
class Shape {
public:
virtual ~Shape() = default; // 虚析构,确保子类析构被调用
virtual void draw() const = 0; // 纯虚函数,子类必须实现
};
// 2. 具体产品:圆形(实现抽象产品接口)
class Circle : public Shape {
public:
void draw() const override {
std::cout << "绘制圆形(Circle)" << std::endl;
}
};
// 具体产品:矩形(实现抽象产品接口)
class Rectangle : public Shape {
public:
void draw() const override {
std::cout << "绘制矩形(Rectangle)" << std::endl;
}
};
// 具体产品:三角形(实现抽象产品接口)
class Triangle : public Shape {
public:
void draw() const override {
std::cout << "绘制三角形(Triangle)" << std::endl;
}
};
// 3. 工厂类:负责所有形状产品的创建
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 if (type == "triangle") {
return std::make_unique<Triangle>();
} else {
throw std::invalid_argument("不支持的形状类型:" + type);
}
}
};
// 测试代码:使用者仅依赖抽象产品和工厂,不关心具体实现
int main() {
try {
// 工厂创建圆形
auto circle = ShapeFactory::createShape("circle");
circle->draw();
// 工厂创建矩形
auto rectangle = ShapeFactory::createShape("rectangle");
rectangle->draw();
// 工厂创建三角形
auto triangle = ShapeFactory::createShape("triangle");
triangle->draw();
// 错误类型(抛出异常)
auto invalid = ShapeFactory::createShape("square");
} catch (const std::exception& e) {
std::cerr << "错误:" << e.what() << std::endl;
}
return 0;
}
输出结果
绘制圆形(Circle)
绘制矩形(Rectangle)
绘制三角形(Triangle)
错误:不支持的形状类型:square
核心优点
- 实现简单:仅需一个工厂类,代码量少,易于理解和维护。
- 封装性好:使用者无需知道产品的创建细节,只需传入类型参数。
- 统一管理:产品创建逻辑集中在工厂,便于添加日志、缓存等扩展逻辑。
核心缺点
- 违反开闭原则 :新增产品时,必须修改工厂类的
createShape方法(添加新的if-else),导致工厂类逐渐臃肿。 - 工厂类职责过重:所有产品的创建逻辑都在一个工厂中,一旦工厂出错,所有产品创建都会受影响。
- 扩展性差 :产品类型过多时,
if-else分支会变得冗长,维护困难。
适用场景
- 产品类型较少(如 3-5 种),且不常新增产品。
- 简单场景,无需复杂的扩展机制(如小型工具类、简单组件创建)。
2.2 工厂方法模式(Factory Method Pattern)
定义
为解决简单工厂的"开闭原则"问题,将 单一工厂拆分为多个具体工厂:每个具体产品对应一个具体工厂,抽象出统一的工厂接口,新增产品时只需新增"具体产品 + 具体工厂",无需修改现有代码。本质是"分拆式创建",将创建职责分散到各个具体工厂。
结构组成
- 抽象产品(Product):所有具体产品的基类(抽象类/接口)。
- 具体产品(ConcreteProduct):抽象产品的实现类。
- 抽象工厂(AbstractFactory) :所有具体工厂的基类(抽象类/接口),含纯虚函数
createProduct()(定义产品创建接口)。 - 具体工厂(ConcreteFactory):抽象工厂的实现类,每个具体工厂仅负责创建一种具体产品。
C++ 实现代码
cpp
#include <iostream>
#include <memory>
// 1. 抽象产品:形状接口
class Shape {
public:
virtual ~Shape() = default;
virtual void draw() const = 0;
};
// 2. 具体产品:圆形
class Circle : public Shape {
public:
void draw() const override {
std::cout << "绘制圆形(Circle)" << std::endl;
}
};
// 具体产品:矩形
class Rectangle : public Shape {
public:
void draw() const override {
std::cout << "绘制矩形(Rectangle)" << std::endl;
}
};
// 3. 抽象工厂:定义工厂的核心接口(创建产品)
class ShapeFactory {
public:
virtual ~ShapeFactory() = default;
virtual std::unique_ptr<Shape> createShape() const = 0; // 纯虚函数,子类实现
};
// 4. 具体工厂:圆形工厂(仅创建圆形)
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> circleFactory = std::make_unique<CircleFactory>();
auto circle = circleFactory->createShape();
circle->draw();
// 矩形工厂创建矩形
std::unique_ptr<ShapeFactory> rectFactory = std::make_unique<RectangleFactory>();
auto rectangle = rectFactory->createShape();
rectangle->draw();
// 新增产品(如三角形):仅需添加 Triangle 类和 TriangleFactory 类,无需修改现有代码
return 0;
}
输出结果
绘制圆形(Circle)
绘制矩形(Rectangle)
核心优点
- 符合开闭原则:新增产品时,只需新增"具体产品 + 具体工厂",无需修改现有工厂和使用代码。
- 职责单一:每个具体工厂仅负责创建一种产品,工厂类职责清晰,维护简单。
- 扩展性强:支持灵活替换具体工厂(如替换不同的产品实现),符合里氏替换原则。
核心缺点
- 类数量膨胀:每新增一个产品,需同时新增一个具体工厂类,导致系统中类的数量增多,复杂度上升。
- 逻辑分散:创建逻辑分散在多个具体工厂中,如需统一修改创建逻辑(如添加日志),需修改所有具体工厂。
- 使用成本高 :使用者需知道具体工厂和具体产品的对应关系(如创建圆形需用
CircleFactory),不像简单工厂那样只需传入参数。
适用场景
- 产品类型较多,且频繁新增产品(如插件系统、组件库)。
- 需灵活替换产品实现(如不同数据库的连接工厂:MySQLFactory、OracleFactory)。
- 要求严格遵守开闭原则的场景(如框架开发)。
2.3 抽象工厂模式(Abstract Factory Pattern)
定义
当系统中存在 多个相关联的产品族(而非单一产品)时,抽象工厂模式定义一个"产品族工厂接口",每个具体工厂负责创建一个完整的产品族(多个相关产品)。本质是"产品族级别的创建",解决"一系列相关产品的统一创建"问题。
关键概念
- 产品族:一组相关联的产品(如"华为产品族"包含华为手机、华为耳机;"苹果产品族"包含苹果手机、苹果耳机)。
- 产品等级结构:同一类型的产品(如手机是一个产品等级,耳机是另一个产品等级)。
结构组成
- 抽象产品族:多个抽象产品(每个产品等级对应一个抽象产品)。
- 具体产品族:多个具体产品,每个具体产品属于一个产品等级,且归属于某个产品族。
- 抽象工厂:定义创建所有产品等级的接口(每个产品等级对应一个创建方法)。
- 具体工厂:实现抽象工厂接口,创建对应产品族的所有产品(如华为工厂创建华为手机、华为耳机)。
C++ 实现代码
cpp
#include <iostream>
#include <memory>
#include <string>
// 1. 抽象产品族(两个产品等级:手机、耳机)
// 抽象产品1:手机
class Phone {
public:
virtual ~Phone() = default;
virtual std::string getName() const = 0;
};
// 抽象产品2:耳机
class Headphone {
public:
virtual ~Headphone() = default;
virtual std::string getName() const = 0;
};
// 2. 具体产品族1:华为产品族
// 具体产品1-1:华为手机
class HuaweiPhone : public Phone {
public:
std::string getName() const override {
return "华为 Mate 60 Pro";
}
};
// 具体产品2-1:华为耳机
class HuaweiHeadphone : public Headphone {
public:
std::string getName() const override {
return "华为 FreeBuds Pro 3";
}
};
// 具体产品族2:苹果产品族
// 具体产品1-2:苹果手机
class IPhone : public Phone {
public:
std::string getName() const override {
return "iPhone 16 Pro";
}
};
// 具体产品2-2:苹果耳机
class AirPods : public Headphone {
public:
std::string getName() const override {
return "AirPods Pro 2";
}
};
// 3. 抽象工厂:定义创建所有产品等级的接口(手机 + 耳机)
class ElectronicFactory {
public:
virtual ~ElectronicFactory() = default;
virtual std::unique_ptr<Phone> createPhone() const = 0;
virtual std::unique_ptr<Headphone> createHeadphone() const = 0;
};
// 4. 具体工厂1:华为工厂(创建华为产品族的所有产品)
class HuaweiFactory : public ElectronicFactory {
public:
std::unique_ptr<Phone> createPhone() const override {
return std::make_unique<HuaweiPhone>();
}
std::unique_ptr<Headphone> createHeadphone() const override {
return std::make_unique<HuaweiHeadphone>();
}
};
// 具体工厂2:苹果工厂(创建苹果产品族的所有产品)
class AppleFactory : public ElectronicFactory {
public:
std::unique_ptr<Phone> createPhone() const override {
return std::make_unique<IPhone>();
}
std::unique_ptr<Headphone> createHeadphone() const override {
return std::make_unique<AirPods>();
}
};
// 测试代码:使用者通过具体工厂获取完整的产品族
int main() {
// 华为工厂:创建华为手机 + 华为耳机
std::unique_ptr<ElectronicFactory> huaweiFactory = std::make_unique<HuaweiFactory>();
auto huaweiPhone = huaweiFactory->createPhone();
auto huaweiHeadphone = huaweiFactory->createHeadphone();
std::cout << "华为产品族:" << huaweiPhone->getName() << " + " << huaweiHeadphone->getName() << std::endl;
// 苹果工厂:创建苹果手机 + 苹果耳机
std::unique_ptr<ElectronicFactory> appleFactory = std::make_unique<AppleFactory>();
auto iphone = appleFactory->createPhone();
auto airpods = appleFactory->createHeadphone();
std::cout << "苹果产品族:" << iphone->getName() << " + " << airpods->getName() << std::endl;
return 0;
}
输出结果
华为产品族:华为 Mate 60 Pro + 华为 FreeBuds Pro 3
苹果产品族:iPhone 16 Pro + AirPods Pro 2
核心优点
- 统一产品族创建:一个具体工厂对应一个完整的产品族,确保产品族内的产品兼容性(如华为手机和华为耳机可联动)。
- 符合开闭原则:新增产品族时,只需新增"具体产品族 + 具体工厂",无需修改现有代码。
- 封装性更强:使用者无需知道产品族内各产品的创建细节,只需通过工厂获取整套产品。
核心缺点
- 扩展产品等级困难:若需新增产品等级(如在"手机+耳机"基础上新增"平板"),需修改抽象工厂接口和所有具体工厂,违反开闭原则。
- 复杂度高:系统中存在多个抽象产品和具体产品,类结构复杂,理解和维护成本高。
- 初始化成本高:具体工厂需创建产品族的所有产品,若产品族较大,初始化开销较高。
适用场景
- 系统中存在多个相关联的产品族(如电子设备、UI 组件主题、数据库驱动+连接池)。
- 需确保产品族内产品兼容性的场景(如同一主题的按钮、输入框、下拉框)。
- 不常新增产品等级,但可能新增产品族的场景(如框架的多主题支持)。
三、三种工厂模式的对比与选择
| 特性 | 简单工厂模式 | 工厂方法模式 | 抽象工厂模式 |
|---|---|---|---|
| 核心定位 | 单一工厂创建所有产品 | 一个产品对应一个工厂 | 一个产品族对应一个工厂 |
| 开闭原则兼容性 | 违反(新增产品需改工厂) | 符合(新增产品加工厂) | 符合产品族,违反产品等级 |
| 类数量 | 少(1 工厂 + N 产品) | 中(N 工厂 + N 产品) | 多(N 工厂 + M*N 产品) |
| 适用场景 | 产品少、不常扩展 | 产品多、常扩展 | 存在产品族、需保证兼容性 |
| 使用者复杂度 | 低(仅传参数) | 中(需知道工厂-产品对应关系) | 高(需理解产品族概念) |
| 扩展成本 | 低(修改工厂) | 中(新增工厂+产品) | 产品族扩展低,产品等级扩展高 |
选择建议
- 优先选简单工厂:若产品类型少(≤5 种)、不常新增,简单工厂的简洁性是最优选择。
- 选工厂方法:若产品类型多、需频繁新增,且无产品族关联,工厂方法的扩展性更优。
- 选抽象工厂:若存在多个相关联的产品族(如主题、设备套装),需保证产品兼容性,抽象工厂是唯一选择。
四、C++ 工厂模式的关键实现技巧
4.1 内存管理:使用智能指针避免泄漏
工厂模式中,产品通常通过 new 创建,手动管理内存易导致泄漏。C++11 后推荐使用 智能指针 (std::unique_ptr/std::shared_ptr):
std::unique_ptr:独占所有权,适合产品生命周期由使用者管理的场景(如示例代码)。std::shared_ptr:共享所有权,适合产品需被多个模块共享的场景(如缓存中的产品实例)。
4.2 工厂的单例化
工厂类本身通常无需多个实例(创建产品的逻辑是固定的),可将工厂设计为单例(如 Meyers 单例),减少实例创建开销:
cpp
// 单例工厂(以简单工厂为例)
class ShapeFactory {
public:
static ShapeFactory& getInstance() {
static ShapeFactory instance;
return instance;
}
std::unique_ptr<Shape> createShape(const std::string& type) {
// 创建逻辑...
}
// 禁止拷贝赋值
ShapeFactory(const ShapeFactory&) = delete;
ShapeFactory& operator=(const ShapeFactory&) = delete;
private:
ShapeFactory() = default; // 私有构造
};
// 使用:通过单例工厂创建产品
auto circle = ShapeFactory::getInstance().createShape("circle");
4.3 产品初始化参数传递
若产品需要构造参数(如配置信息),可在工厂方法中添加参数:
cpp
// 抽象产品:带参数初始化的数据库连接
class DBConnection {
public:
virtual ~DBConnection() = default;
virtual void connect() const = 0;
};
// 具体产品:MySQL 连接
class MySQLConnection : public DBConnection {
public:
MySQLConnection(const std::string& host, int port, const std::string& dbName)
: host_(host), port_(port), dbName_(dbName) {}
void connect() const override {
std::cout << "连接 MySQL:" << host_ << ":" << port_ << "/" << dbName_ << std::endl;
}
private:
std::string host_;
int port_;
std::string dbName_;
};
// 工厂方法:传递初始化参数
class MySQLFactory : public DBFactory {
public:
std::unique_ptr<DBConnection> createConnection(
const std::string& host, int port, const std::string& dbName) const {
return std::make_unique<MySQLConnection>(host, port, dbName);
}
};
// 使用
auto mysqlConn = MySQLFactory().createConnection("127.0.0.1", 3306, "test_db");
mysqlConn->connect();
4.4 工厂的延迟初始化与缓存
对于创建成本高的产品(如数据库连接、重型组件),可在工厂中添加缓存机制,复用已创建的实例:
cpp
class ShapeFactory {
public:
std::shared_ptr<Shape> getCachedShape(const std::string& type) {
// 检查缓存
auto it = cache_.find(type);
if (it != cache_.end()) {
return it->second;
}
// 缓存未命中,创建产品并加入缓存
auto shape = createShape(type);
cache_.emplace(type, shape);
return shape;
}
private:
std::unordered_map<std::string, std::shared_ptr<Shape>> cache_; // 产品缓存
// createShape 方法...
};
五、工厂模式的适用场景与反场景
5.1 适用场景
- 对象创建逻辑复杂:产品创建需多步初始化、依赖外部资源(如配置文件、网络连接),或需校验参数。
- 产品类型多变:需频繁新增产品(如插件系统、组件库),或替换产品实现(如不同数据库驱动)。
- 需要统一管理对象:需集中控制产品的创建、销毁、缓存(如连接池、线程池)。
- 解耦创建与使用:使用者无需关心产品实现细节,仅依赖抽象接口(如框架开发、跨模块协作)。
- 产品族关联场景:存在多个相关联的产品,需保证兼容性(如 UI 主题、电子设备套装)。
5.2 反场景(不建议使用)
- 简单对象创建 :产品创建仅需
new关键字,无复杂逻辑(如std::string实例),使用工厂会增加不必要的复杂度。 - 产品类型极少且固定 :无需扩展,简单
new即可满足需求(如工具类的单一实例)。 - 需直接控制对象构造:使用者需灵活调整产品的构造参数、初始化顺序,工厂的封装会限制灵活性。
- 过度设计风险:为了"用设计模式"而强行使用工厂,导致系统复杂度上升(如小型项目的简单组件)。
六、C++ 成熟库中的工厂模式应用
- C++ 标准库 :
std::make_unique/std::make_shared本质是简单工厂,封装了智能指针的创建逻辑。 - Boost 库 :
boost::factory提供通用工厂模板,支持灵活配置产品创建逻辑,避免重复编写工厂代码。 - Qt 框架 :
QFactoryLoader是抽象工厂的典型应用,用于加载插件(如 Qt 风格插件、图像格式插件),通过插件名创建对应产品。 - 数据库连接池 :如
MySQL Connector/C++中的连接工厂,根据配置创建不同类型的数据库连接(MySQL、Oracle)。
七、总结与核心原则
工厂模式的核心是 "封装创建,解耦依赖",三种类型从简单到复杂,覆盖不同场景:
- 简单工厂:简洁优先,适合产品少的场景;
- 工厂方法:扩展优先,适合产品多的场景;
- 抽象工厂:产品族优先,适合关联产品的场景。
核心原则
- 优先依赖抽象:使用者仅依赖抽象产品和抽象工厂,不依赖具体实现。
- 避免过度设计:根据实际场景选择最简单的工厂模式,无需盲目追求复杂扩展。
- 结合 C++ 特性:使用智能指针管理内存、单例模式优化工厂、模板简化工厂代码。
- 遵守设计原则:工厂模式的设计需符合开闭原则、单一职责原则、依赖倒置原则,确保系统可维护、可扩展。
在实际开发中,工厂模式是最常用的创建型设计模式之一,掌握其核心思想和实现技巧,能有效提升代码的灵活性、可维护性,尤其在框架开发、组件化设计中发挥重要作用。