C++ 工厂模式:从入门到进阶,彻底掌握对象创建的艺术

在面向对象软件设计中,对象的创建是最基础也最容易产生耦合的环节。当业务代码中散落着大量 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++ 软件开发中无处不在,典型应用包括:

  1. 日志系统:根据配置创建控制台日志、文件日志、远程日志等不同输出渠道
  2. 数据库访问层:创建 MySQL、PostgreSQL、SQLite 等不同数据库的连接对象
  3. 插件框架:动态加载并创建第三方插件实例,实现功能的热扩展
  4. 游戏开发:创建不同类型的敌人、道具、技能等游戏对象
  5. GUI 框架:创建跨平台的窗口、按钮、菜单等 UI 组件

七、避坑指南与选型原则

7.1 常见误区

  • 过度设计 :如果只有 1-2 种产品且长期稳定,直接 new 对象即可,无需强行引入工厂模式
  • 工厂职责膨胀:不要在工厂中掺杂业务逻辑,工厂仅负责对象创建与初始化
  • 忽略生命周期管理:返回裸指针极易导致内存泄漏,现代 C++ 项目必须使用智能指针

7.2 选型原则

  1. 产品少、迭代慢 → 简单工厂,用最少的代码满足需求
  2. 产品多、迭代快 → 工厂方法,保证扩展性的同时控制复杂度
  3. 存在多系列关联产品 → 抽象工厂,保证产品族的一致性
  4. 大型项目、插件化架构 → 注册式工厂,实现完全的开闭原则

总结

工厂模式的本质是封装变化:将对象创建这个最易变的环节隔离封装,让稳定的业务逻辑不再依赖易变的创建细节。设计模式没有绝对的优劣,选择哪种形态,始终取决于业务的复杂度、迭代频率和团队的维护成本。

对于绝大多数 C++ 项目,工厂方法模式是扩展性与复杂度的最佳平衡点;而对于中大型系统,注册式工厂是工业界的主流选择,它兼顾了灵活性与可维护性,是真正能支撑项目长期迭代的方案。

相关推荐
@insist1232 小时前
系统架构设计师-实时性评价、调度算法与内核架构选型
算法·架构·系统架构·软考·系统架构设计师·软件水平考试
JosieBook3 小时前
【数据库】时序预测能力的分级进化:TimechoAI如何让每一类用户都能精准预见未来
java·开发语言·数据库
加号33 小时前
【C#】 文件与目录管理:创建、删除操作的技术解析
开发语言·c#
diving deep4 小时前
脚本速览-python
开发语言·python
一生了无挂4 小时前
Java处理JSON技巧教学(从基础到高阶实战全覆盖)
java·开发语言·json
swordbob4 小时前
Spring 单例 Bean 是线程安全的吗?
java·开发语言
一拳一个呆瓜5 小时前
【STL】_SCL_SECURE_NO_WARNINGS
c++·stl
小小编程路5 小时前
C++ 异常 完整讲解
开发语言·c++