讲师中心 微信公众号
AI工具推荐 视频效率加速

工厂模式在C++中怎样应用 抽象工厂与简单工厂实现对比

云浩小哥_9365

云浩小哥_9365

发布时间:2025-07-21 09:31:02

|

273人浏览过

|

来源于php中文网

原创

工厂模式在c++中的应用旨在解耦对象创建过程,使客户端无需关心具体实现。1. 简单工厂通过一个工厂类集中管理对象创建逻辑,适用于产品种类稳定的小型系统,但违反开闭原则;2. 抽象工厂用于创建相关或依赖对象的家族,遵循开闭原则,适合多平台或多风格的产品体系,但结构复杂;3. 选择时应根据项目规模和扩展需求权衡,避免过度设计或维护困难。

工厂模式在C++中怎样应用 抽象工厂与简单工厂实现对比

工厂模式在C++中的应用,核心在于解耦对象的创建过程,让客户端代码不必关心具体产品是如何实例化的。这就像你点外卖,你只管点菜,至于厨房里是哪个厨师、用了什么食材、具体怎么炒的,你通常不用知道。而简单工厂和抽象工厂,就是实现这种“点菜”机制的两种不同策略,它们各有侧重,解决的问题复杂度也不一样。

工厂模式在C++中怎样应用 抽象工厂与简单工厂实现对比

工厂模式在C++中应用,本质上就是将对象的创建逻辑从使用对象的客户端代码中剥离出来。想想看,如果你的代码里充斥着大量的new ConcreteProductA()、new ConcreteProductB(),一旦产品类型增多,或者创建逻辑变得复杂(比如需要读取配置、依赖其他服务),你的客户端代码就会变得臃肿且难以维护。工厂模式就是为了解决这个痛点。它提供了一个专门的接口或类来负责对象的创建,让客户端只需通过这个接口请求所需对象,而无需了解其背后的具体实现细节。这带来了几个显而易见的好处:首先是解耦,客户端和具体产品类之间不再直接依赖;其次是可扩展性,新增产品时,通常只需修改工厂或新增工厂,而无需触碰客户端代码;再者,它能更好地管理对象的生命周期,比如在创建前进行一些初始化,或者在销毁时做一些清理。

工厂模式在C++中怎样应用 抽象工厂与简单工厂实现对比

简单工厂模式的C++实践与适用场景

简单工厂模式,说实话,严格意义上它不算GoF(Gang of Four)设计模式中的一员,但它太常用了,以至于大家都把它当成工厂模式家族的入门级成员。它的核心思想是一个工厂类,内部包含一个静态方法或者一个普通方法,根据传入的参数来决定创建并返回哪种具体的产品对象。

立即学习“C++免费学习笔记(深入)”;

在C++中实现简单工厂,通常会有一个抽象的产品基类或接口,以及多个具体的子类。工厂类则通过一个方法,接收一个字符串或者枚举类型的参数,然后通过条件判断(比如if-else if或者switch语句)来实例化对应的产品。

工厂模式在C++中怎样应用 抽象工厂与简单工厂实现对比
#include <iostream>
#include <string>
#include <memory> // For std::unique_ptr

// 抽象产品基类
class Product {
public:
    virtual void use() const = 0;
    virtual ~Product() = default;
};

// 具体产品A
class ConcreteProductA : public Product {
public:
    void use() const override {
        std::cout << "Using ConcreteProductA." << std::endl;
    }
};

// 具体产品B
class ConcreteProductB : public Product {
public:
    void use() const override {
        std::cout << "Using ConcreteProductB." << std::endl;
    }
};

// 简单工厂类
class SimpleProductFactory {
public:
    // 使用智能指针管理内存,避免手动delete
    static std::unique_ptr<Product> createProduct(const std::string& type) {
        if (type == "A") {
            return std::make_unique<ConcreteProductA>();
        } else if (type == "B") {
            return std::make_unique<ConcreteProductB>();
        } else {
            std::cout << "Unknown product type: " << type << std::endl;
            return nullptr;
        }
    }
};

/*
// 客户端代码示例
int main() {
    std::unique_ptr<Product> product1 = SimpleProductFactory::createProduct("A");
    if (product1) {
        product1->use();
    }

    std::unique_ptr<Product> product2 = SimpleProductFactory::createProduct("B");
    if (product2) {
        product2->use();
    }

    std::unique_ptr<Product> product3 = SimpleProductFactory::createProduct("C"); // 尝试创建不存在的产品
    if (product3) {
        product3->use();
    }

    return 0;
}
*/

这种模式的优点显而易见:简单、直观,将创建逻辑集中管理,客户端代码非常干净。但它的缺点也同样突出:当你需要新增一个产品C时,你不得不修改SimpleProductFactory中的createProduct方法,增加一个else if (type == "C")的分支。这明显违反了“开闭原则”(Open/Closed Principle),即对扩展开放,对修改关闭。对于产品类型不多的、或者产品种类相对稳定的系统来说,简单工厂是个不错的选择,因为它够轻量。但如果你的产品线经常变动,或者产品种类繁多,那么这种模式很快就会变得难以维护,那个createProduct方法会膨胀成一个“上帝方法”。

抽象工厂模式的C++实践与复杂性管理

抽象工厂模式,这才是GoF设计模式中真正的“工厂模式”家族成员。它解决的问题比简单工厂更复杂:它提供一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们具体的类。简单来说,它不是创建单个产品,而是创建一整套“产品家族”。

想象一下,你正在开发一个跨平台的UI库,你需要创建按钮、文本框、下拉菜单等UI组件。这些组件在Windows、macOS、Linux上可能有着完全不同的实现(比如Win32 Button、Cocoa Button、GTK Button)。抽象工厂就是用来解决这类问题的:它让你能创建一整套“Windows风格”的UI组件,或者一整套“macOS风格”的UI组件,而不用关心这些组件的具体类名。

Linux Security Scanner
Linux Security Scanner

Linux 安全审计工具,检查 SSH 配置、开放/监听端口、防火墙规则(ufw/iptables/nftables)、失败登录尝试、sudoers 权限...

下载

在C++中实现抽象工厂,通常会涉及多层抽象:

  1. 抽象工厂接口(Abstract Factory):声明一组用于创建抽象产品的方法。
  2. 具体工厂类(Concrete Factory):实现抽象工厂接口,负责创建具体产品家族中的产品。
  3. 抽象产品接口(Abstract Product):为一类产品声明接口。
  4. 具体产品类(Concrete Product):实现抽象产品接口,是具体工厂创建的对象。
#include <iostream>
#include <string>
#include <memory>

// 抽象产品A (例如:按钮)
class AbstractButton {
public:
    virtual void render() const = 0;
    virtual ~AbstractButton() = default;
};

// 抽象产品B (例如:复选框)
class AbstractCheckbox {
public:
    virtual void check() const = 0;
    virtual ~AbstractCheckbox() = default;
};

// 具体产品:Windows风格的按钮和复选框
class WindowsButton : public AbstractButton {
public:
    void render() const override {
        std::cout << "Rendering Windows Button." << std::endl;
    }
};

class WindowsCheckbox : public AbstractCheckbox {
public:
    void check() const override {
        std::cout << "Checking Windows Checkbox." << std::endl;
    }
};

// 具体产品:Mac风格的按钮和复选框
class MacButton : public AbstractButton {
public:
    void render() const override {
        std::cout << "Rendering Mac Button." << std::endl;
    }
};

class MacCheckbox : public AbstractCheckbox {
public:
    void check() const override {
        std::cout << "Checking Mac Checkbox." << std::endl;
    }
};

// 抽象工厂接口
class AbstractUIFactory {
public:
    virtual std::unique_ptr<AbstractButton> createButton() const = 0;
    virtual std::unique_ptr<AbstractCheckbox> createCheckbox() const = 0;
    virtual ~AbstractUIFactory() = default;
};

// 具体工厂:Windows UI 工厂
class WindowsUIFactory : public AbstractUIFactory {
public:
    std::unique_ptr<AbstractButton> createButton() const override {
        return std::make_unique<WindowsButton>();
    }
    std::unique_ptr<AbstractCheckbox> createCheckbox() const override {
        return std::make_unique<WindowsCheckbox>();
    }
};

// 具体工厂:Mac UI 工厂
class MacUIFactory : public AbstractUIFactory {
public:
    std::unique_ptr<AbstractButton> createButton() const override {
        return std::make_unique<MacButton>();
    }
    std::unique_ptr<AbstractCheckbox> createCheckbox() const override {
        return std::make_unique<MacCheckbox>();
    }
};

/*
// 客户端代码示例
int main() {
    // 使用Windows UI工厂
    std::unique_ptr<AbstractUIFactory> winFactory = std::make_unique<WindowsUIFactory>();
    std::unique_ptr<AbstractButton> winButton = winFactory->createButton();
    std::unique_ptr<AbstractCheckbox> winCheckbox = winFactory->createCheckbox();

    winButton->render();
    winCheckbox->check();

    std::cout << "---" << std::endl;

    // 使用Mac UI工厂
    std::unique_ptr<AbstractUIFactory> macFactory = std::make_unique<MacUIFactory>();
    std::unique_ptr<AbstractButton> macButton = macFactory->createButton();
    std::unique_ptr<AbstractCheckbox> macCheckbox = macFactory->createCheckbox();

    macButton->render();
    macCheckbox->check();

    return 0;
}
*/

抽象工厂的优点是它很好地遵循了开闭原则,当你需要新增一个“产品家族”(比如Linux UI风格)时,你只需新增一个LinuxUIFactory和对应的LinuxButton、LinuxCheckbox,而无需修改现有的工厂接口或具体工厂。这对于管理复杂的产品体系非常有效。然而,它的缺点是显而易见的复杂性增加,涉及的类和接口数量更多。而且,如果你的产品家族中需要新增一个“产品类型”(比如除了按钮和复选框,现在还要增加一个“下拉菜单”),那么你就需要修改所有抽象产品接口、抽象工厂接口,以及所有具体工厂和具体产品类,这会带来较大的改动。

简单工厂与抽象工厂的选择考量及设计陷阱

在C++项目里,到底选简单工厂还是抽象工厂,这确实是个值得深思的问题,没有放之四海而皆准的答案。这两种模式都是为了解耦创建过程,但它们解决的“粒度”不同,也因此带来了不同的权衡。

简单工厂更像是对new操作的集中封装。它的优势在于简单,代码量少,理解成本低。当你面对的情况是:

  • 产品种类不多,且相对稳定,不常变化。
  • 只需要根据一个简单的标识符(如字符串、枚举)来创建对象。
  • 你希望将所有产品的创建逻辑集中在一个地方管理。
  • 项目的规模不大,或者你处于项目初期,希望快速迭代。

它的陷阱在于,一旦产品类型增多,那个工厂方法会变得非常臃肿,每次新增产品都要修改它,这会让人很头疼。这就像一个万能插座,插的东西多了,线路就容易乱。

抽象工厂则更注重产品家族的创建。它的强大之处在于:

  • 你需要创建一组相互关联、相互依赖的产品对象,并且这些产品有不同的“风格”或“变体”。
  • 你的系统需要支持多种产品家族的切换,而客户端代码不应感知具体的家族实现。
  • 你非常看重“开闭原则”,希望在增加新的产品家族时,尽可能不修改现有代码。

然而,抽象工厂的复杂性是其最大的挑战。它会引入更多的接口和类,代码量显著增加。如果你只是想创建几个不相关的产品,或者产品家族的概念并不明显,那么使用抽象工厂无疑是过度设计,会把简单问题复杂化。这就像为了喝杯水,你却造了一整套自来水厂,成本太高。

设计陷阱:

  1. 过度设计:最常见的陷阱就是盲目追求模式,在简单的场景下使用复杂的抽象工厂,导致代码难以理解和维护。我个人觉得,很多时候,一个简单的工厂方法(Factory Method,GoF模式中的另一个成员,比简单工厂更灵活,但比抽象工厂简单)或者一个简单的工厂函数就足够了。
  2. 简单工厂的“上帝类”:如果简单工厂的创建方法承担了过多职责,或者处理了太多不同类型的产品,它会变成一个难以维护的“上帝类”。
  3. 抽象工厂的“类爆炸”:为了实现多维度的扩展(产品类型和产品家族),抽象工厂会引入大量的类。如果你的产品类型和家族数量都很多,那么你的类图可能会变得非常庞大,这对于维护者来说是个噩梦。
  4. 新增产品类型的问题:虽然抽象工厂在新增产品家族时表现出色,但如果你需要在现有的产品家族中新增一个产品类型(比如前面例子中,除了按钮和复选框,现在所有UI风格都要增加一个“滑块”组件),你仍然需要修改抽象工厂接口、所有具体工厂类,以及所有抽象产品接口和具体产品类。这其实是一个挑战,说明抽象工厂也不是银弹。

我的建议是,从最简单的解决方案开始。如果一个简单的函数或者if-else结构就能满足需求,那就用它。当产品类型开始增多,或者创建逻辑变得复杂时,可以考虑引入简单工厂。只有当你发现需要创建一组相关的产品,并且这组产品有多个不同的实现版本时,抽象工厂才真正显示出它的价值。记住,设计模式是工具,不是目的,选择最合适的工具才能事半功倍。

热门AI工具

更多
Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

VibeKnow
VibeKnow Hot

一款AI视频创作工具,主要用于全球首个AI知识视频创作平台,文档、文章、网页,一键生成视频,适合需要提升相关任务效率的用户。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

相关专题

更多
switch语句用法
switch语句用法

switch语句用法:1、Switch语句只能用于整数类型,枚举类型和String类型,不能用于浮点数类型和布尔类型;2、每个case语句后面必须跟着一个break语句,以防止执行其他case的代码块,没有break语句,将会继续执行下一个case的代码块;3、可以在一个case语句中匹配多个值,使用逗号分隔;4、Switch语句中的default代码块是可选的等等。

1071

2023.09.21

Java switch的用法
Java switch的用法

Java中的switch语句用于根据不同的条件执行不同的代码块。想了解更多switch的相关内容,可以阅读本专题下面的文章。

1437

2024.03.13

mysql标识符无效错误怎么解决
mysql标识符无效错误怎么解决

mysql标识符无效错误的解决办法:1、检查标识符是否被其他表或数据库使用;2、检查标识符是否包含特殊字符;3、使用引号包裹标识符;4、使用反引号包裹标识符;5、检查MySQL的配置文件等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

16760

2023.12.04

Python标识符有哪些
Python标识符有哪些

Python标识符有变量标识符、函数标识符、类标识符、模块标识符、下划线开头的标识符、双下划线开头、双下划线结尾的标识符、整型标识符、浮点型标识符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1203

2024.02.23

java标识符合集
java标识符合集

本专题整合了java标识符相关内容,想了解更多详细内容,请阅读下面的文章。

9110

2025.06.11

c++标识符介绍
c++标识符介绍

本专题整合了c++标识符相关内容,阅读专题下面的文章了解更多详细内容。

3622

2025.08.07

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

1858

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

2984

2023.09.04

Docker Desktop怎么设置端口映射
Docker Desktop怎么设置端口映射

本专题聚焦Docker Desktop容器端口映射与访问配置,整理从创建容器、设置端口绑定到验证本地服务访问的操作流程。针对端口被占用、映射配置错误、容器服务无法访问等情况,也提供相应的检查方向,方便用户完成端口配置。

0

2026.11.03

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Python uv官方安装与入门文档
Python uv官方安装与入门文档

共0课时 | 0人学习

Buffalo框架快速入门指南
Buffalo框架快速入门指南

共0课时 | 0人学习

Valgrind支持平台说明
Valgrind支持平台说明

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn