C++工厂模式详解:从简单工厂到抽象工厂的实战指南

C++工厂模式设计模式简单工厂
于 2026-10-02 07:49:06 修改
·本内容遵循CC 4.0 BY-SA版权协议

从 new 到 工厂,这可能是你写 C++ 最值得花一小时搞明白的设计模式。做了这么多年 C++,但凡接手过一个超过两万行的业务系统,你一定见过这种代码:if 套 else if 再套 switch,创建对象的逻辑散落在各个模块里,改一个类型能牵连出一串编译错误。工厂模式就是冲着这类问题来的。

这篇不聊花架子,我把三种工厂——简单工厂、工厂方法、抽象工厂——从使用场景、代码实现、重构过程到踩坑经验完整捋一遍。不管你是刚接触设计模式的学生,还是在 VSCode 里配置 C++ 环境后想写点像样代码的初学者,这篇都能直接拿来用。

1. 工厂模式到底在解决什么问题

1.1 没有工厂的日子:需求从 1 个变成 8 个

想象一个很常见的场景:你要做一个日志系统,最开始只有文件日志。

CPP
class FileLogger {
public:
void log(const std::string& msg) {
// 写文件
}
};
 
class LoggerManager {
public:
void process(const std::string& msg) {
FileLogger logger;
logger.log(msg);
}
};

直接 FileLogger logger;,轻松搞定。然后产品经理来了,说要加控制台日志、数据库日志、云日志、加密日志、压缩日志……你看着 LoggerManager 里 8 个 if-else 分支,每个分支里都是一个具体类对象,代码开始变成一团浆糊。

这时候工厂模式就派上用场了。它的核心思想一句话:把对象的创建和使用分离。调用方不再关心对象是怎么 new 出来的、具体是哪个类,只关心传入一个标识或参数,就能拿到一个可用的对象。

1.2 违反了什么原则,以及为什么重要

不用工厂的代码,问题出在违反了“依赖倒置原则”——高层模块依赖了低层具体类,还违反了“开闭原则”——加一个新日志类型就要改 LoggerManager 的代码。

我记得有次把一个项目里所有 new 关键词搜出来,发现创建对象的代码散布在几十个文件里。后来想要替换整个底层通讯库,光是找依赖点就花了整整两天。工厂模式就是把这些“创建点”收敛到同一个入口,改起来只动一处。

2. 简单工厂模式:最朴素也最容易用错的写法

2.1 核心实现:一个函数搞定所有创建

简单工厂(Simple Factory)严格来说不算 GoF 的 23 种设计模式,但它是最直观、最常用的一种。用一个统一的工厂函数,根据参数返回不同类型的对象。

CPP
# include <iostream>
# include <memory>
# include <string>
 
enum class LoggerType {
File,
Console,
Database
};
 
class ILogger {
public:
virtual ~ILogger() = default;
virtual void log(const std::string& msg) = 0;
};
 
class FileLogger : public ILogger {
public:
void log(const std::string& msg) override {
std::cout << "[File] " << msg << std::endl;
}
};
 
class ConsoleLogger : public ILogger {
public:
void log(const std::string& msg) override {
std::cout << "[Console] " << msg << std::endl;
}
};
 
class DatabaseLogger : public ILogger {
public:
void log(const std::string& msg) override {
std::cout << "[Database] " << msg << std::endl;
}
};
 
// 一个函数,根据类型创建对象
class LoggerFactory {
public:
static std::unique_ptr<ILogger> create(LoggerType type) {
switch (type) {
case LoggerType::File: return std::make_unique<FileLogger>();
case LoggerType::Console: return std::make_unique<ConsoleLogger>();
case LoggerType::Database: return std::make_unique<DatabaseLogger>();
default: throw std::invalid_argument("Unknown logger type");
}
}
};
 
int main() {
auto logger = LoggerFactory::create(LoggerType::Console);
logger->log("hello factory");
 
auto fileLogger = LoggerFactory::create(LoggerType::File);
fileLogger->log("hello file");
return 0;
}

这里有几个关键点值得细说。

2.2 为什么接口类必须声明虚析构函数

在上面的代码里,virtual ~ILogger() = default; 这行很多人会漏。如果你漏了,通过 std::unique_ptr<ILogger> 销毁对象时,只会调用基类析构函数,派生类 FileLogger、ConsoleLogger 中申请的资源就不会被释放。我排查过好几次内存泄漏,最后发现元凶都是这种没写虚析构的接口类。

2.3 用 std::unique_ptr 还是裸指针

现代 C++ 里强烈建议用智能指针。裸指针最大的问题是所有权不清晰——调用方拿到指针后要不要 delete?如果忘记 delete 就泄漏,如果 delete 两次就崩溃。用 std::unique_ptr 把所有权和生命周期管理交给 RAII 机制,异常安全,代码也简洁。C++ 14 及以上可以用 std::make_unique,如果你还在用 C++ 11,可以手动 std::unique_ptr<T>(new T(...)) 替代。

2.4 简单工厂的适用场景与天然局限

适用场景很明确:产品种类少、且不太可能频繁增加。比如一个工具类的字符串转换器,就两三种实现,用简单工厂完全足够。

局限也很明显:

  • 加一个新类型,必须改工厂函数内部代码——违反了开闭原则
  • 工厂函数会越来越膨胀,参数越来越多,负责的逻辑越来越杂
  • 所有类型都耦合在一个函数里,不便于独立测试

我在实际项目里见过一个写了几百行的简单工厂,里面 switch 分支有 20 多个,还混进了配置读取和依赖初始化逻辑。这种时候,就该考虑下一步的工厂方法模式了。

3. 工厂方法模式:把选择权交给子类

3.1 从静态函数到虚函数

工厂方法模式解决的核心问题是:把“创建哪个对象”的决定权延迟到子类。基类定义了一个创建产品的接口,但是不实现具体逻辑,由子类去决定实例化哪个具体产品。

还是用日志系统的例子。现在需求变了:不同的业务模块需要不同的日志策略——A 模块写文件,B 模块写数据库,C 模块要加密。如果用简单工厂,得维护一个全局大函数;用工厂方法,就可以把创建逻辑分摊到各个模块自己的工厂里。

CPP
# include <iostream>
# include <memory>
# include <string>
 
// 产品
class ILogger {
public:
virtual ~ILogger() = default;
virtual void log(const std::string& msg) = 0;
};
 
class FileLogger : public ILogger {
public:
void log(const std::string& msg) override {
std::cout << "[File] " << msg << std::endl;
}
};
 
class ConsoleLogger : public ILogger {
public:
void log(const std::string& msg) override {
std::cout << "[Console] " << msg << std::endl;
}
};
 
// 工厂基类
class LoggerCreator {
public:
virtual ~LoggerCreator() = default;
std::unique_ptr<ILogger> getLogger() {
auto logger = createLogger();
// 公共的初始化逻辑可以在这里做,比如设置日志级别
// initLogger(logger);
return logger;
}
 
protected:
virtual std::unique_ptr<ILogger> createLogger() = 0;
};
 
// 具体工厂A:生成文件日志
class FileLoggerCreator : public LoggerCreator {
protected:
std::unique_ptr<ILogger> createLogger() override {
return std::make_unique<FileLogger>();
}
};
 
// 具体工厂B:生成控制台日志
class ConsoleLoggerCreator : public LoggerCreator {
protected:
std::unique_ptr<ILogger> createLogger() override {
return std::make_unique<ConsoleLogger>();
}
};
 
int main() {
std::unique_ptr<LoggerCreator> creator = std::make_unique<FileLoggerCreator>();
auto logger = creator->getLogger();
logger->log("factory method demo");
return 0;
}

3.2 模板方法思想的穿插

注意我在 getLogger 里留了“公共初始化”的注释。这是一个很容易被忽视的好处:工厂基类可以在创建产品后统一做一些事情,比如设置日志级别、绑定上下文、埋点统计、资源审计等。子类只关心“提供什么样的产品”,而“产品出来之后怎么处理”可以在基类统一管理。这其实就是模板方法模式和工厂方法模式的结合,实际开发中非常实用。

3.3 什么时候该从简单工厂升级到工厂方法

我自己的判断标准有三个:

  • 产品类型数量超过 5 个,且还在持续增加
  • 创建产品的过程带有各自的定制逻辑,不只是一个 new
  • 需要在不同模块中使用不同的产品配置,希望创建逻辑随模块划分

之前我重构过一个消息推送系统,最初用简单工厂管理微信、短信、邮件三种推送。后来加了 App Push、钉钉、企业微信,每次都要改工厂函数,而且不同业务线想要不同的推送组合,简单工厂根本没法优雅处理。改成工厂方法后,每个业务线创建自己的工厂,互不干扰,产品扩展时只需要新增一个工厂类加一个产品类,老代码一行不动。

3.4 工厂方法的最大缺点

工厂方法也有它的代价:类的数量会翻倍。每增加一个产品,就要同时增加一个产品类和对应的工厂类。如果项目里只有两三种产品,这种“仪式感”反而显得繁琐。性能敏感场景也要注意,虚函数调用有极小的间接开销,但绝大多数业务系统根本感知不到,不用为这个过度优化。

4. 抽象工厂模式:产品族的批量生产方案

4.1 从单一产品到产品族

前两种工厂解决的是“某个类型怎么创建”的问题。但现实里还有一个场景:你需要的不是单个对象,而是一整个配套的产品组合。

举个例子,你要开发一个跨平台 UI 组件库。Windows 风格下需要 WindowsButton、WindowsTextBox;Linux 风格下需要 LinuxButton、LinuxTextBox。你不能让界面上出现 Windows 按钮配 Linux 文本框——这就是产品族的一致性要求。

4.2 抽象工厂的完整代码示例

CPP
# include <iostream>
# include <memory>
 
// 抽象产品:按钮
class IButton {
public:
virtual ~IButton() = default;
virtual void render() = 0;
};
 
class WindowsButton : public IButton {
public:
void render() override {
std::cout << "Render Windows Button" << std::endl;
}
};
 
class LinuxButton : public IButton {
public:
void render() override {
std::cout << "Render Linux Button" << std::endl;
}
};
 
// 抽象产品:文本框
class ITextBox {
public:
virtual ~ITextBox() = default;
virtual void render() = 0;
};
 
class WindowsTextBox : public ITextBox {
public:
void render() override {
std::cout << "Render Windows TextBox" << std::endl;
}
};
 
class LinuxTextBox : public ITextBox {
public:
void render() override {
std::cout << "Render Linux TextBox" << std::endl;
}
};
 
// 抽象工厂:负责创建一整套相关产品
class IUIFactory {
public:
virtual ~IUIFactory() = default;
virtual std::unique_ptr<IButton> createButton() = 0;
virtual std::unique_ptr<ITextBox> createTextBox() = 0;
};
 
class WindowsUIFactory : public IUIFactory {
public:
std::unique_ptr<IButton> createButton() override {
return std::make_unique<WindowsButton>();
}
std::unique_ptr<ITextBox> createTextBox() override {
return std::make_unique<WindowsTextBox>();
}
};
 
class LinuxUIFactory : public IUIFactory {
public:
std::unique_ptr<IButton> createButton() override {
return std::make_unique<LinuxButton>();
}
std::unique_ptr<ITextBox> createTextBox() override {
return std::make_unique<LinuxTextBox>();
}
};
 
void renderUI(IUIFactory& factory) {
auto button = factory.createButton();
auto textBox = factory.createTextBox();
button->render();
textBox->render();
}
 
int main() {
WindowsUIFactory windowsFactory;
renderUI(windowsFactory);
 
LinuxUIFactory linuxFactory;
renderUI(linuxFactory);
return 0;
}

看到区别了吗?抽象工厂中,客户端使用的是接口 IUIFactory,它能够保证:你用 WindowsUIFactory,拿到的就永远是 Windows 系产品,不会混进 Linux 系。这种一致性保障是抽象工厂最大的价值。

4.3 抽象工厂的“开闭”问题

抽象工厂有一个著名的矛盾:增加新产品族容易,增加新产品类型难。在上面例子里,如果你想新增一个 Mac 风格,只要加一个 MacUIFactory 和两个具体产品类,不用改动已有代码。但是如果 UI 组件库里想新增一个“下拉框”,你不得不去修改 IUIFactory 接口,添加一个 createComboBox() 方法,然后所有具体工厂类都要跟着改。这显然违反了开闭原则。

GoF 里也承认这一点。实际工程中,抽象工厂通常用在产品族结构比较稳定的领域,比如数据库访问层——你可能有 MySQL、PostgreSQL、SQLite 三种数据库,但“连接”“命令”“事务”这几个抽象操作是固定的;再比如主题换肤系统,主题风格可能越来越多,但组成主题的元素类型是固定的。

4.4 三种工厂里的“选择策略”如何配合

如果你现在面对的不仅是一族产品,而且产品类型还在持续扩张,可以考虑在抽象工厂内部配合工厂方法:每个具体工厂用工厂方法创建自己的具体产品,这样组合起来灵活度会高很多。C++ 的 std::pmr、插件系统、游戏引擎里的对象管理,很多就是这么实现的。

5. 三种工厂模式对比与选型建议

5.1 一张表看清区别

模式 层级 核心机制 解决什么问题 典型场景 扩展方式
简单工厂 无(不属于GoF 23) 集中式静态函数 + 参数判断 根据参数创建不同类型的对象 类型少、创建逻辑简单 修改工厂函数
工厂方法 类级 虚函数延迟到子类 把创建逻辑下放给子类,各子类自己决定 不同类型的产品各自有定制创建过程 新增工厂类和产品类
抽象工厂 产品族级 一组工厂方法 保证一整套相关产品的一致性 跨平台 UI、数据库访问层、主题系统 新增具体工厂类

5.2 我的选型口诀

选型从来不是“越高级越好”,而是“复杂度匹配需求”。我一般这样判断:

  • 产品就 2 到 3 个,以后大概率也不多 → 直接简单工厂,甚至干脆别用工厂,手动 new 就行
  • 产品会持续增加,或者不同模块需要不同的创建逻辑 → 工厂方法
  • 涉及一整套配套产品,必须保证互相兼容 → 抽象工厂
  • 想兼顾产品族扩展和产品类型扩展,但暂时没把握 → 用工厂方法为主,别硬上抽象工厂

还有一个容易被忽略的角度:团队的维护成本。工厂方法 + 抽象工厂会产生大量类,对于一个小团队、一个小项目,代码结构越简单越好。我自己吃过亏,一上来就上最复杂的设计,结果代码量翻倍,新人接手一脸懵。

5.3 性能和资源管理考量

如果你在写对性能敏感的服务,比如每秒钟创建销毁几十万次对象,工厂模式里的虚函数调用和 make_unique 的堆分配开销是不能忽略的。这种情况下可以:

  • 用 std::shared_ptr 缓存实例,避免频繁创建销毁
  • 用对象池复用对象
  • 在工厂方法里返回 std::unique_ptr 而不是 shared_ptr,减少引用计数的原子操作开销

但记住:不要提前优化。先用清楚的结构把代码写对,再通过 profile 工具确认瓶颈。

6. 实战中的坑与排查技巧

6.1 坑一:忘记虚析构导致的内存泄漏

这是 C++ 工厂模式里最经典的坑。接口基类中没有 virtual ~ILogger() = default;,那么通过基类指针删除派生类对象时,派生类析构函数不会被调用。如果你在派生类里持有文件句柄、网络连接、std::thread 等资源,不释放就会泄漏。

排查方法:用 Valgrind 或者 AddressSanitizer 跑一轮用例,如果每次调用工厂创建再销毁都有泄漏报错,十有八九就是这个问题。

6.2 坑二:工厂方法内抛异常的处理

创建对象的过程中可能抛出异常,比如数据库连接失败、文件打不开、内存不足。如果工厂内部用裸指针 new,在初始化后续资源时抛出异常,会导致指针丢失、内存泄漏。用 std::unique_ptr 则没有这个问题——构造函数一旦完成,智能指针就拥有对象所有权,异常安全有保障。

还有一种情况:调用方尝试创建不存在的类型,比如配置写错了类型名。这时工厂应该抛出一个明确的异常或者返回 nullptr。我的建议是:能抛异常就不要返回 nullptr。返回空指针很容易被调用方忽视,运行到后面才空指针崩溃,排查起来很痛苦。抛出 std::invalid_argument 并带上可读的类型名,日志里能直接看到问题。

6.3 坑三:回归到不统一的“假工厂”

有一种反模式很常见:工厂函数里不只看类型,还掺杂了一堆业务逻辑,比如根据配置文件、环境变量、随机数来决定返回哪种类型。这种代码初看很灵活,但排查起来特别头疼——你不知道为什么这一次返回的是 A 而不是 B。

我的建议:工厂只负责“根据明确标识创建对应产品”。如果标识本身就来自配置或环境变量,那是上层调用方的事情,在调用工厂之前先解析好传进来,尽量不要让工厂再去做决策。

6.4 还没完:工厂模式的高级玩法——注册表工厂

如果你面临的是插件化架构,比如编辑器里支持各种文件格式解析器,每种格式一个类,未来还会不断增加。这种场景可以考虑“注册表工厂”思路:一个工厂类内部维护一个映射表,把类型名和创建函数绑定起来,新的产品类只需要注册自己的构造器,不需要修改工厂代码。

CPP
# include <functional>
# include <memory>
# include <string>
# include <unordered_map>
 
class ICodec {
public:
virtual ~ICodec() = default;
virtual std::string decode(const std::string& data) = 0;
};
 
class RegistryCodecFactory {
public:
using Creator = std::function<std::unique_ptr<ICodec>()>;
 
static RegistryCodecFactory& instance() {
static RegistryCodecFactory factory;
return factory;
}
 
void registerCreator(const std::string& name, Creator creator) {
creators_[name] = std::move(creator);
}
 
std::unique_ptr<ICodec> create(const std::string& name) {
auto it = creators_.find(name);
if (it == creators_.end()) {
throw std::invalid_argument("Unknown codec: " + name);
}
return it->second();
}
 
private:
std::unordered_map<std::string, Creator> creators_;
};
 
// 注册的宏或静态初始化函数
# define REGISTER_CODEC(name, ClassName) \
static bool _register_##ClassName = [] { \
RegistryCodecFactory::instance().registerCreator( \
name, [] { return std::make_unique<ClassName>(); }); \
return true; \
}()

这种实现非常灵活:新增一种文件格式,你就写一个类,然后在源文件里调用 REGISTER_CODEC("png", PngCodec),工厂代码完全不用改。不过要注意静态初始化顺序的问题,注册代码不要依赖别的全局对象。

6.5 与单例模式、构建者模式如何搭配

工厂模式不会单独出现,几乎总是和其他模式搭配使用。我在实际开发中常用的组合:

  • 工厂 + 单例:工厂本身做成单例,避免每次创建产品都构造工厂对象
  • 工厂 + 原型模式:如果产品创建成本高,可以先存一个原型对象,工厂里克隆
  • 工厂 + 构建者:工厂负责决定返回哪种复杂对象,构建者负责对象内部各个部件的装配

搭配要适度。我见过一个项目,一个工厂类里塞了单例、模板方法、观察者、状态机……代码完全没法读。设计模式是工具,不是目的,选两三个能解决问题的就够了。

7. 常见编译环境坑与跨平台注意事项

7.1 C++ 标准版本问题

std::make_unique 是 C++14 才有的,如果你还在用 VSCode 配置 C/C++ 环境后默认的 C++11,编译会报错。解决方案两种:把项目标准设置为 C++14 或更新版本,或者自己实现一个 make_unique。

在 CMake 里这样设置:

CMAKE
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

另外,VSCode 里如果你遇到 error: microsoft visual c++ 14.0 or greater is required 这种报错,通常是 Python 包编译或者其他依赖工具链的问题,和你写的 C++ 代码无关,但要注意它说明你的机器上没有安装完整的 MSVC 构建工具。这个报错挺坑的,我遇到过几次,解决方法一般是安装 Visual Studio Build Tools(勾选“使用 C++ 的桌面开发”工作负载),装完重启终端就能解决。

7.2 虚析构加 = default 还是 = 0

接口基类里通常会写:

CPP
virtual ~ILogger() = default;

或者

CPP
virtual ~ILogger() = 0;
ILogger::~ILogger() {} // 纯虚析构需要类外定义

两种写法都行。前者是“默认虚析构”,更简洁;后者是“纯虚析构”,强制让接口类不可实例化。如果接口本身还有其他纯虚函数,那用 = default 就够了。如果这个接口只有一个纯虚析构,要注意给析构函数提供类外实现,否则链接会报错。

7.3 Windows 和 Linux 下工厂模式没有本质区别

工厂模式本身跨平台使用没有差异,但要注意不同平台下的动态库接口问题。如果你在 Windows 上用 DLL 导出工厂函数,记得导出的是接口类,不要在 DLL 边界上传 std::unique_ptr,因为两个模块可能链接了不同版本的 CRT,析构函数执行时会发生跨模块内存释放错误。稳妥的做法是:在 DLL 内部导出创建和销毁两个函数,销毁也在 DLL 内完成。

这个坑我印象很深,当时在 Windows 上做插件系统,插件返回 std::shared_ptr,DLL 卸载时直接崩溃,排查了一个下午。后来改成 DLL 内部持有对象,只导出一个 create() 和一个 destroy() 函数,问题彻底解决。

8. 一个完整实例:从零重构一个支付系统

前面讲了很多模式理论,最后用一个完整的例子串一遍。假设你要写一个支付网关,支持支付宝、微信、银联三种支付方式。用简简单单的 if-else 写,代码是能跑,但后续加支付方式非常痛苦。

8.1 第一版:简单工厂

CPP
enum class PayType { Alipay, WeChat, UnionPay };
 
class IPayment {
public:
virtual ~IPayment() = default;
virtual void pay(double amount) = 0;
};
 
class AlipayPayment : public IPayment {
public:
void pay(double amount) override { /* 支付宝逻辑 */ }
};
 
class WeChatPayment : public IPayment {
public:
void pay(double amount) override { /* 微信逻辑 */ }
};
 
class UnionPayPayment : public IPayment {
public:
void pay(double amount) override { /* 银联逻辑 */ }
};
 
class PaymentFactory {
public:
static std::unique_ptr<IPayment> create(PayType type) {
switch (type) {
case PayType::Alipay: return std::make_unique<AlipayPayment>();
case PayType::WeChat: return std::make_unique<WeChatPayment>();
case PayType::UnionPay: return std::make_unique<UnionPayPayment>();
default: throw std::invalid_argument("unknown pay type");
}
}
};

8.2 第二版:升级工厂方法

后来各个支付通道对接的配置不一样,支付宝要设置 appId、私钥,微信要设置 mchId、证书路径,创建过程各不相同。为了让每个渠道自己管理配置,把工厂改成工厂方法模式,每个渠道有自己的工厂,配置从哪里读取由各自工厂决定。

这种演进的好处是:每个渠道的变化被隔离在各自类中,新增渠道不需要动已有支付代码,符合开闭原则。

8.3 第三版:抽象工厂处理多供应商

再后来你发现支付还有“国内版”和“国际版”,每个版本下都有这三种支付渠道。这时候用抽象工厂:一个 IPaymentFactory 定义 createAlipay()、createWeChat()、createUnionPay(),然后国内版和国际版各自实现一组工厂。这样整个支付体系在被替换时,只需换一个工厂实例,所有支付方式一致切换,不会出现国内版支付宝配国际版微信这种混乱。

如果你能跟着思路把这三版代码各写一遍,我对工厂模式的理解就算是真正过关了。

我在实际项目里踩过的坑很多,最有价值的一条经验是:设计模式的引入要趁早,但不要过度。等你看见 if-else 已经堆积到恶心再重构,成本很高;“啥也没发生”之前先抽象三层类,也只会让代码更复杂。最好的时机是在改动边界出现时顺手引入工厂,让代码结构始终比需求超前半步。工厂模式尤其如此——它的价值不在于多写几个类,而在于让你每一次新增功能,都能从容地“加一个类”而不是“改一堆代码”。

C++ 设计模式实践的完整详解与实战指南,重点聚焦于工厂模式(简单工厂、工厂方法、抽象工厂)和 单例模式(饿汉式、懒汉式、线程安全优化)
本文深入讲解C++中工厂模式(简单工厂、工厂方法、抽象工厂)和单例模式(饿汉式、懒汉式、Meyers线程安全实现)的核心原理与现代C++17/20实践,结合Qt日志系统与插件架构进行多模式协同落地,强调开闭原则、接口抽象、线程安全及资源管理。
张工在路上
966
C++工厂模式(简单工厂、工厂方法、抽象工厂)
本文深入探讨了工厂模式在软件设计中的应用,从简单工厂模式开始,逐步过渡到工厂方法和抽象工厂模式。通过代码示例展示了如何创建和扩展产品对象,同时阐述了各种模式的优点和不足。简单工厂模式虽然易于理解,但不满足开闭原则;工厂方法通过基类指针实现多态,增强了扩展性;抽象工厂则能创建一组相关产品,更好地模拟现实世界中的产品簇。
Redamanc
15427
C++设计模式 - 简单工厂,工厂方法和抽象工厂
本文介绍了C++中的三种设计模式:简单工厂、工厂方法和抽象工厂。简单工厂用于解耦对象创建,但不满足开闭原则;工厂方法遵循开闭原则,为每种产品提供相应的工厂;抽象工厂则用于创建产品簇,减少工厂类数量,更符合现实工厂模式。文中通过代码示例和UML图详细解释了三种模式的差异和应用场景。
大秦坑王
2549
【C++设计模式】Factory 工厂模式详解(简单工厂、工厂方法、抽象工厂)
本文详细介绍了C++中的三种工厂模式:简单工厂、工厂方法和抽象工厂。简单工厂模式用于创建对象,但对修改不封闭。工厂方法模式遵循开闭原则,允许扩展而无需修改。抽象工厂模式则适用于创建多个相关或相互依赖的对象族。每种模式都包括定义、特点、结构和实现,以及对应的UML图和示例程序。
ZY-JIMMY
1936
简单工厂、工厂模式、抽象工厂的区别
本文主要介绍了C++中简单工厂、工厂方法和抽象工厂三种创建型设计模式。阐述了它们的概念、结构、优缺点及适用场景。简单工厂简单易管理但不符开闭原则;工厂方法符合开闭原则,责任分离清晰;抽象工厂能确保产品一致性,也遵循开闭原则,但复杂度较高。
阳光开朗_大男孩儿
1110
【C++】简单工厂模式/工厂方法模式/抽象工厂模式
本文介绍了C++中简单工厂模式、工厂方法模式和抽象工厂模式这三种创建型设计模式。从定义、结构、适用场景和代码示例等方面对它们进行对比分析,指出各自优缺点,并给出选择建议,如产品少用简单工厂,需扩展性用工厂方法,创建相关依赖产品用抽象工厂。
OpenC++
956
c++简单工厂模式、工厂方法模式与抽象工厂模式的实现和优缺点
本文介绍了C++中简单工厂模式、工厂方法模式和抽象工厂模式。简单工厂用单独类控制实例化,有动态实例化优点但违背开闭原则;工厂方法克服此缺点,但增加产品会增加开发量;抽象工厂创建系列对象,需求改动小,但新增功能时需增加类和修改工厂类。核心思想是降耦。
做梦人z
1768
C++:浅谈工厂模式与抽象工厂模式
本文浅谈C++中的工厂模式和抽象工厂模式,通过实例解释两者的区别和应用场景。工厂模式提供了一种创建对象的方式,而抽象工厂模式则扩展到创建相关对象家族。文章还讨论了两种模式的优缺点,并给出了一个使用抽象工厂模式解决跨平台图形编程任务的案例。
小威威__
5734
工厂模式:抽象工厂模式 C++代码实现、场景及优缺点
本文详细介绍了抽象工厂模式的概念、C++代码实现,包括其适用场景、特点以及优缺点。通过实例和结构图展示如何创建相关产品系列,以及如何通过抽象工厂管理产品家族和降低耦合度。,
五木大大
1825
C++设计模式:三种工厂模式详解(简单工厂,工厂模式,抽象工厂)
本文介绍了三种工厂模式:简单工厂模式、工厂方法模式和抽象工厂模式。简单工厂模式通过一个工厂类创建具有共同父类的产品对象,工厂方法模式则通过抽象工厂和具体工厂类将产品创建过程进一步抽象,支持多态性。抽象工厂模式关注产品族的创建,允许在不指定具体产品类型的情况下创建一系列相关产品。这些模式有助于减少客户端对产品实现的依赖并简化扩展。
Yuleo_
1286
C++工厂模式全解析:从简单工厂到抽象工厂的进阶之路
博客介绍了软件设计中工厂模式,它能将对象创建和使用解耦,使代码更灵活。详细阐述了简单工厂、工厂方法和抽象工厂三种模式,包括概念、适用场景、C++实现方式及优缺点。如简单工厂适用于创建对象少的系统,工厂方法符合开闭原则,抽象工厂适合创建多种产品类型的系统。
加班码农斯基
1143
工厂模式C++示例代码:简单工厂模式、工厂方法模式和抽象工厂模式
本文详细介绍了C++中的简单工厂模式、工厂方法模式和抽象工厂模式,以及它们的适用场景和注意事项,强调了设计原则和灵活性的重要性。
五木大大
1390
设计模式——简单工厂、工厂方法、抽象工厂介绍(附C++代码)
本文介绍了简单工厂、工厂方法和抽象工厂三种设计模式。详细解释了各模式的UML图结构,包括产品类、工厂类及其关系。同时给出了C++代码示例和代码说明,展示如何通过工厂类创建产品对象,体现了将对象创建与使用分离、遵循开闭原则等优势。
Gabriel_Swift
1392
C++设计模式:简单工厂、工厂方法、抽象工厂
本文介绍了C++中的三种工厂模式:简单工厂、工厂方法和抽象工厂。简单工厂模式通过一个工厂类创建对象,但违反了开放-封闭原则。工厂模式通过多态和继承解决这一问题,每个子类工厂负责生产特定类型的对象。抽象工厂模式则适用于更复杂的业务场景,创建系列相关或相互依赖的对象。总结指出,工厂模式和抽象工厂遵循开放-封闭原则,且抽象工厂创建的产品更复杂。
起个别名
1082
【设计模式学习笔记】简单工厂模式、工厂模式、抽象工厂模式案例详解(C++实现)
本文深入探讨了三种工厂模式:简单工厂模式、工厂模式(多态工厂)和抽象工厂模式。简单工厂模式通过一个工厂类创建所有产品实例,但存在扩展困难的问题。工厂模式引入了抽象工厂接口,允许在不修改原有代码的情况下增加新产品。抽象工厂模式则进一步扩展,支持创建一族相关或相互依赖的对象,适用于产品线有多个等级的情况。通过对三种模式的实例分析,展示了它们在产品创建中的灵活性和可扩展性。
Mindtechnist
1225
【C/C++】设计模式——工厂模式:简单工厂、工厂方法、抽象工厂
本文详细介绍了C语言中的工厂模式,包括简单工厂、工厂方法和抽象工厂,强调了它们在封装对象创建、解耦、扩展性方面的优势。通过实例展示了如何在C++中实现这些设计模式。,
天喜Studio
1226
C++设计模式 | 工厂模式之抽象工厂模式详解
本文介绍了工厂模式和抽象工厂模式。工厂模式核心思想是降低耦合,使程序结构清晰、方便修改和复用。抽象工厂模式提供创建相关对象的接口,更便于产品子类扩展。还给出代码示例,同时阐述了抽象工厂模式的优缺点,如需求改变改动小,但新增功能时需增加类和修改工厂类。
小贾-同志
2311
【C++实战㊵】C++抽象工厂模式:解锁高效对象创建的密钥
本文详细介绍了C++中抽象工厂模式的概念、适用场景及实现方法。通过对比工厂方法模式,分析了其在产品族创建和跨平台开发中的优势。文章还提供了具体的代码示例,并探讨了如何结合依赖注入和线程安全处理提升系统灵活性与稳定性。最后,通过一个跨平台UI组件库的实战案例,展示了抽象工厂模式的实际应用场景。
奔跑吧邓邓子
1200
工厂模式(分简单工厂模式、工厂方法模式、抽象工厂模式)
本文介绍了三种工厂模式:简单工厂模式用于创建简单的对象,工厂方法模式允许运行时动态创建对象,抽象工厂模式则处理相互依赖的对象族创建。这些模式在需要动态创建对象或降低客户端与产品类耦合时适用。C++实现展示了如何使用工厂模式创建Shape对象,包括简单工厂和工厂方法的实现。
田翁野老
2204
C++设计模式——工厂模式 :简单工厂、工厂方法、抽象工厂
本文介绍了工厂模式的三种变体:简单工厂、工厂方法和抽象工厂,分析了它们的实现、优缺点及适用场景,强调选择何种模式需考虑业务需求。
键盘会跳舞
2106
OGRE设计模式详解
OGRE(Object-Oriented Graphics Rendering Engine)作为一款开源、跨平台、面向对象的高性能3D图形渲染引擎,自2001年诞生以来,便以高度模块化、可扩展性强、设计思想先进而著称。《OGRE设计模式详解》一书正是深入剖析其底层架构与软件工程智慧的核心文献,它远不止于API使用手册或功能速查指南,而是从软件工程本质出发,系统性地揭示OGRE如何通过经典与创新并存的设计模式,在复杂图形系统开发中实现高内聚、低耦合、易维护、可演化的目标。该书围绕C++语言特性展开,紧密结合面向对象设计原则(如开闭原则、里氏替换原则、依赖倒置原则、接口隔离原则与单一职责原则),将抽象的设计哲学落地为具体可复用的代码结构与交互范式。首先,工厂模式(Factory Pattern)在OGRE中具有基石性地位。引擎大量采用抽象工厂(Abstract Factory)与简单工厂(Simple Factory)变体来解耦资源创建逻辑与具体实现。例如,`RenderSystem`子系统的初始化并非硬编码调用Direct3D9RenderSystem或OpenGLRenderSystem构造函数,而是通过`RenderSystemFactory`注册机制动态加载——用户仅需配置渲染后端类型,引擎即自动实例化对应渲染系统,并注入统一抽象接口`RenderSystem`。同理,材质(Material)、纹理(Texture)、网格(Mesh)、粒子系统(ParticleSystem)等核心资源均通过`ResourceManager`配合`ResourceFactory`体系完成延迟创建、引用计数管理与按需加载,极大提升了内存效率与运行时灵活性。这种设计不仅支持插件热插拔,更使OGRE天然具备多后端兼容能力(如Vulkan、Metal等后续扩展亦可无缝集成)。其次,观察者模式(Observer Pattern)被广泛应用于事件驱动与状态同步场景。OGRE的`SceneManager`与`Camera`、`Light`、`MovableObject`之间并非紧耦合更新,而是通过`SceneNode`的变换变更触发`Node::Listener`回调链;`RenderTarget`在帧缓冲切换、视口调整或渲染完成时,向注册的`RenderTargetListener`广播事件;`Log`系统亦采用观察者机制,允许多个日志处理器(如文件写入器、控制台输出器、网络上报器)同时响应同一日志消息。这种松耦合通信显著增强了模块独立性,使开发者可在不修改核心引擎代码的前提下,定制化监控、调试、性能分析甚至远程诊断功能。进一步地,OGRE的插件系统(Plugin System)是策略模式(Strategy Pattern)、桥接模式(Bridge Pattern)与组合模式(Composite Pattern)的集大成体现。每个插件(如`Plugin_ParticleFX`、`Plugin_BSPSceneManager`)封装特定算法或数据结构,通过`Plugin`抽象基类定义生命周期接口(install/uninstall),由`PluginManager`统一加载、注册与调度;而`SceneManager`接口则作为桥接角色,将高层场景操作(如`createEntity`、`setAmbientLight`)与底层具体实现(Octree、BSP、Portal等)彻底分离。开发者可依据场景复杂度、硬件能力或项目需求,自由切换不同场景管理策略,无需重构业务逻辑。此外,`SceneManager`本身又以组合模式组织`SceneNode`树形结构,每个节点既可作为容器承载子节点,也可挂载可移动对象(`MovableObject`),形成递归嵌套的层次化空间管理模型,完美支撑大规模开放世界、室内复杂建筑及动态LOD切换等高级需求。在渲染架构层面,OGRE采用“渲染管线抽象层+可编程着色器绑定”的分层设计,融合了模板方法模式(Template Method Pattern)与命令模式(Command Pattern)。`RenderQueue`对所有可渲染对象按材质、距离、渲染顺序进行分组与排序,再交由`RenderSystem`执行批量绘制调用(`_render`),其中`RenderOperation`封装顶点缓冲、索引缓冲、着色器参数等状态,构成可序列化、可缓存、可重放的渲染命令单元。而`Pass`、`Technique`、`Scheme`等材质子系统组件,则以模板方法定义渲染流程骨架(如`preRender`→`bindGpuPrograms`→`render`→`postRender`),允许子类在关键钩子点插入定制逻辑,兼顾标准化与扩展性。最后,场景管理中的`SceneManager`与`SceneNode`体系,深刻体现了面向对象设计中“封装变化点”的思想:位置、朝向、缩放等空间属性被封装进`Quaternion`与`Vector3`,并通过`SceneNode::setPosition()`等接口提供安全访问;父子关系通过智能指针(`SharedPtr`)维护,自动处理内存生命周期;而`SceneManager`则作为协调者,统一调度所有节点的世界变换矩阵更新、视锥体裁剪(Frustum Culling)、遮挡剔除(Occlusion Culling)与光照计算。这种设计使上层应用逻辑完全脱离底层数学细节与性能优化负担,真正实现“所见即所控”。综上,《OGRE设计模式详解》不仅是学习OGRE引擎的权威指南,更是面向对象软件架构设计的实战教科书。它展示了如何在C++这一兼具性能与表达力的语言平台上,将设计模式从理论范式转化为稳定、健壮、可演化的工业级图形系统骨架。掌握其设计精髓,不仅能高效开发高质量3D应用,更能反哺通用软件架构能力,在游戏引擎、仿真系统、数字孪生、AR/VR平台等前沿领域构建更具生命力的技术底座。
C++ 工厂模式 (简单工厂、工厂和抽象工厂)
### C++工厂模式详解:简单工厂、工厂方法与抽象工厂#### 一、引言在软件工程领域,设计模式是一种被广泛接受的解决常见问题的解决方案。
1905
简单工厂模式、工厂模式、抽象工厂模式案例(C++实现)
在这个压缩包中,包含了三种工厂模式的C++实现:简单工厂模式、工厂方法模式以及抽象工厂模式。让我们一一探讨这些模式。1.
Mindtechnist
847
C++ 设计模式:简单工厂、工厂方法、抽象工厂练习代码
在软件开发领域,设计模式是解决常见问题的模板,它们代表了最佳实践并已被广泛接受。本主题聚焦于C++中的三个工厂模式:简单工厂、工厂方法和抽象工厂。
studyAlisa
229
C++设计模式之抽象工厂模式
"C++设计模式之抽象工厂模式"在软件工程中,设计模式是一种解决常见问题的标准解决方案,它们经过时间验证,具有良好的可复用性。本文将深入探讨C++中的抽象工厂模式,它是对工厂方法模式的一种扩展,
weixin_38593738
295
抽象工厂模式+工厂方法模式+策略模式+类图实现手机加工厂
在软件设计领域,模式是一种解决常见问题的有效方法。本文将探讨三个重要的设计模式:抽象工厂模式、工厂方法模式以及策略模式,并结合一个实际的场景——手机加工厂,来具体阐述它们的应用。
发发同学
1309
C++实现简单工厂模式和抽象工厂模式
本文介绍了C++中实现简单工厂模式和抽象工厂模式的方法。简单工厂模式通过一个工厂类创建不同类型的对象,而抽象工厂模式则创建一系列相关或依赖对象。文中通过具体的C++代码示例展示了两种模式的实现细节。
小码hh
c++ 实现简单工厂模式和抽象工厂模式
本文介绍了在C++中实现简单工厂模式和抽象工厂模式的方法。简单工厂模式通过一个工厂类来创建其他类的对象,而抽象工厂模式则创建一组相关或依赖对象族。文中提供了两种模式的C++代码示例,展示了如何使用工厂模式来解耦客户端代码与对象的实例化过程。
小码hh