设计模式 之 工厂模式(简单工厂模式、工厂方法模式、抽象工厂模式)(C++)
文章目录
- C++ 工厂模式
- 引言
- 一、简单工厂模式
- 概念
- 实现步骤
- 示例代码
- 优缺点
- 二、工厂方法模式
- 概念
- 实现步骤
- 示例代码
- 优缺点
- 三、抽象工厂模式
- 概念
- 实现步骤
- 示例代码
- 优缺点
C++ 工厂模式
引言
在 C++ 编程中,对象的创建是一个常见且基础的操作。然而,当项目规模逐渐增大,对象的创建逻辑变得复杂时,直接在代码中使用 new 关键字创建对象会带来诸多问题,比如代码的可维护性变差、难以扩展等。工厂模式应运而生,它为对象的创建提供了一种更加灵活、可扩展的解决方案。本文将详细介绍 C++ 中的工厂模式,包括简单工厂模式、工厂方法模式和抽象工厂模式,并通过具体的例子帮助大家理解。
一、简单工厂模式
概念
简单工厂模式是工厂模式的基础版本,它定义了一个工厂类,该类可以根据传入的参数决定创建并返回哪种产品类的实例。简单来说,就是把对象的创建逻辑封装在一个工厂类中。
实现步骤
- 定义产品基类:创建一个抽象的产品基类,所有具体产品类都要继承这个基类。
- 创建具体产品类:实现产品基类的接口,创建具体的产品类。
- 创建工厂类:在工厂类中定义一个创建产品的方法,根据传入的参数决定创建哪种具体产品。
示例代码
#include<iostream>
#include<memory>// 定义水果抽象基类,包含纯虚函数 name
class Fruit{public:// 纯虚函数,用于输出水果名称,派生类需实现virtual void name()=0;
};// 苹果类,继承自 Fruit 类
class Apple:public Fruit{public:// 重写基类的 name 函数,输出苹果名称void name() override{std::cout<<"Apple"<<std::endl;}
};// 香蕉类,继承自 Fruit 类
class Banana:public Fruit{public:// 重写基类的 name 函数,输出香蕉名称void name() override{std::cout<<"Banana"<<std::endl;}
};// 工厂类,用于创建不同类型的水果对象
class Factory{public:// 静态方法,根据传入的水果名称创建对应的水果对象static std::unique_ptr<Fruit> createFruit(std::string fruit_name){if(fruit_name=="apple"){// 创建苹果对象并返回其 unique_ptrreturn std::unique_ptr<Fruit>(new Apple());}else if(fruit_name=="banana"){// 创建香蕉对象并返回其 unique_ptrreturn std::unique_ptr<Fruit>(new Banana());}else{// 若名称不匹配,返回空指针return nullptr;}}
};int main()
{// 使用工厂类创建苹果对象std::unique_ptr<Fruit> fruit = Factory::createFruit("apple");// 调用苹果对象的 name 函数输出名称fruit->name(); // 使用工厂类创建香蕉对象fruit = Factory::createFruit("banana");// 调用香蕉对象的 name 函数输出名称fruit->name();// 再次使用工厂类创建苹果对象fruit = Factory::createFruit("apple");return 0;
}
优缺点
- 优点:实现简单,将对象的创建和使用分离,提高了代码的可维护性。
- 缺点:工厂类职责过重,违反了开闭原则(对扩展开放,对修改关闭)。如果需要新增产品,就需要修改工厂类的代码。
二、工厂方法模式
概念
工厂方法模式是在简单工厂模式的基础上进行了改进,它将创建对象的具体逻辑延迟到子类中实现。定义一个创建对象的抽象方法,让子类决定实例化哪个具体产品类。
实现步骤
- 定义产品基类:同简单工厂模式。
- 创建具体产品类:同简单工厂模式。
- 定义抽象工厂类:定义一个抽象的工厂类,其中包含一个抽象的创建产品的方法。
- 创建具体工厂类:继承抽象工厂类,实现创建产品的方法,决定创建哪种具体产品。
示例代码
#include<iostream>
#include<memory>// 定义抽象基类 Fruit,包含纯虚函数 name
// 任何继承自该类的具体水果类都必须实现 name 函数
class Fruit {
public:// 纯虚函数,用于输出水果名称virtual void name() = 0;
};// 定义 Apple 类,继承自 Fruit 类
class Apple : public Fruit {
public:// 重写基类的纯虚函数 name,输出苹果名称void name() override {std::cout << "Apple" << std::endl;}
};// 定义 Banana 类,继承自 Fruit 类
class Banana : public Fruit {
public:// 重写基类的纯虚函数 name,输出香蕉名称void name() override {std::cout << "Banana" << std::endl;}
};// 定义抽象工厂类 Factory,包含纯虚函数 create
// 具体的工厂类需要实现该函数来创建水果对象
class Factory {
public:// 纯虚函数,用于创建水果对象virtual std::shared_ptr<Fruit> create() = 0;
};// 定义 AppleFactory 类,继承自 Factory 类
class AppleFactory : public Factory {
public:// 重写基类的纯虚函数 create,创建苹果对象std::shared_ptr<Fruit> create() override {return std::make_shared<Apple>();}
};// 定义 BananaFactory 类,继承自 Factory 类
class BananaFactory : public Factory {
public:// 重写基类的纯虚函数 create,创建香蕉对象std::shared_ptr<Fruit> create() override {return std::make_shared<Banana>();}
};int main() {// 创建一个指向 AppleFactory 的智能指针std::shared_ptr<Factory> fruit_factory(new AppleFactory());// 调用工厂的 create 方法创建苹果对象std::shared_ptr<Fruit> fruit = fruit_factory->create();// 调用水果对象的 name 方法输出名称fruit->name();// 重置工厂指针,指向 BananaFactoryfruit_factory.reset(new BananaFactory());// 调用新工厂的 create 方法创建香蕉对象fruit = fruit_factory->create();// 调用水果对象的 name 方法输出名称fruit->name();return 0;
}
优缺点
- 优点:符合开闭原则,当需要新增产品时,只需要新增具体产品类和对应的具体工厂类,不需要修改现有代码。
- 缺点:类的数量会增多,增加了系统的复杂度。
三、抽象工厂模式
概念
抽象工厂模式:工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题。但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。此时,我们可以考虑将一些相关的产品组成一个产品族(位于不同产品等级结构中功能相关联的产品组成的家族),由同一个工厂来统一生产,这就是抽象工厂模式的基本思想。
实现步骤
- 定义产品族的抽象基类:为每个产品族定义一个抽象基类。
- 创建具体产品类:实现每个产品族的具体产品类。
- 定义抽象工厂类:定义一个抽象的工厂类,其中包含多个创建不同产品的抽象方法。
- 创建具体工厂类:继承抽象工厂类,实现创建不同产品的方法,决定创建哪些具体产品。
示例代码
#include<iostream>
#include<memory>// 水果抽象基类,定义了输出水果名称的纯虚函数
class Fruit {
public:virtual void name() = 0;
};// 苹果类,继承自 Fruit 类,实现了输出苹果名称的方法
class Apple : public Fruit {
public:void name() override {std::cout << "Apple" << std::endl;}
};// 香蕉类,继承自 Fruit 类,实现了输出香蕉名称的方法
class Banana : public Fruit {
public:void name() override {std::cout << "Banana" << std::endl;}
};// 动物抽象基类,定义了输出动物名称的纯虚函数
class Animal {
public:virtual void name() = 0;
};// 羊类,继承自 Animal 类,实现输出名称方法
class Lamb : public Animal {
public:void name() override {std::cout << "Lamb" << std::endl;}
};// 狗类,继承自 Animal 类,实现了输出狗名称的方法
class Dog : public Animal {
public:void name() override {std::cout << "Dog" << std::endl;}
};// 抽象工厂类,定义了获取水果和动物对象的纯虚函数
class Factory {
public:virtual std::shared_ptr<Fruit> getFruit(const std::string& name) = 0;virtual std::shared_ptr<Animal> getAnimal(const std::string& name) = 0;
};// 水果工厂类,继承自 Factory 类,实现了获取水果对象的方法,获取动物对象返回空指针
class FruitFactory : public Factory {
public:virtual std::shared_ptr<Fruit> getFruit(const std::string& name);virtual std::shared_ptr<Animal> getAnimal(const std::string& name);
};// 动物工厂类,继承自 Factory 类,实现了获取动物对象的方法,获取水果对象返回空指针
class AnimalFactory : public Factory {
public:virtual std::shared_ptr<Fruit> getFruit(const std::string& name);virtual std::shared_ptr<Animal> getAnimal(const std::string& name);
};// 工厂管理类,提供静态方法根据名称创建对应的工厂对象
class FactoryManager {
public:static std::shared_ptr<Factory> creaete(const std::string& name);
};int main() {// 通过工厂管理类创建水果工厂对象std::shared_ptr<Factory> fruit_factory = FactoryManager::creaete("fruit");// 从水果工厂获取苹果对象并输出名称std::shared_ptr<Fruit> fruit = fruit_factory->getFruit("apple");fruit->name();// 从水果工厂获取香蕉对象并输出名称fruit = fruit_factory->getFruit("banana");fruit->name();return 0;
}
优缺点
- 优点:将一系列相关的产品对象的创建封装在一起,保证了产品之间的一致性,同时也符合开闭原则。
- 缺点:实现复杂,当产品族需要增加新的产品时,需要修改抽象工厂类和所有具体工厂类的代码。
相关文章:
设计模式 之 工厂模式(简单工厂模式、工厂方法模式、抽象工厂模式)(C++)
文章目录 C 工厂模式引言一、简单工厂模式概念实现步骤示例代码优缺点 二、工厂方法模式概念实现步骤示例代码优缺点 三、抽象工厂模式概念实现步骤示例代码优缺点 C 工厂模式 引言 在 C 编程中,对象的创建是一个常见且基础的操作。然而,当项目规模逐渐…...
3、Kubernetes 集群部署 Prometheus 和 Grafana
Kubernetes 集群部署 Prometheus 和 Grafana node-exporter 安装Prometheus 安装和配置Prometheus 配置热加载Grafana 安装部署Grafana 配置 实验环境 控制节点/master01 192.168.110.10 工作节点/node01 192.168.110.20 工作节点/node02 192.168.110.30 node-exporter 安装 #…...
【C语言】第八期——指针
目录 1 初始指针 2 获取变量的地址 3 定义指针变量、取地址、取值 3.1 定义指针变量 3.2 取地址、取值 4 对指针变量进行读写操作 5 指针变量作为函数参数 6 数组与指针 6.1 指针元素指向数组 6.2 指针加减运算(了解) 6.2.1 指针加减具体数字…...
如何在 Mac 上安装并配置 JDK 环境变量
如何在Mac上安装并配置JDK环境变量 在开发过程中,许多应用和框架都需要使用Java,尤其是使用Java开发的应用程序。如果你是Mac用户,以下是安装并配置JDK环境变量的步骤,确保你能顺利运行Java程序。 步骤 1:下载JDK 访…...
【git-hub项目:YOLOs-CPP】本地实现05:项目移植
ok,经过前3个博客,我们实现了项目的跑通。 但是,通常情况下,我们的项目都是需要在其他电脑上也跑通,才对。 然而,经过测试,目前出现了2 个bug。 项目一键下载【⬇️⬇️⬇️】: 精…...
LeetCode 热题 100 206. 反转链表
LeetCode 热题 100 | 206. 反转链表 大家好,今天我们来解决一道经典的算法题——反转链表。这道题在 LeetCode 上被标记为简单难度,要求我们将一个单链表反转,并返回反转后的链表。下面我将详细讲解解题思路,并附上 Python 代码实…...
2025年02月21日Github流行趋势
项目名称:source-sdk-2013 项目地址url:https://github.com/ValveSoftware/source-sdk-2013项目语言:C历史star数:7343今日star数:929项目维护者:JoeLudwig, jorgenpt, narendraumate, sortie, alanedwarde…...
WebXR教学 03 项目1 旋转彩色方块
一、项目结构 webgl-cube/ ├── index.html ├── main.js ├── package.json └── vite.config.js二、详细实现步骤 初始化项目 npm init -y npm install three vite --save-devindex.html <!DOCTYPE html> <html lang"en"> <head><…...
深入解析JVM垃圾回收机制
1 引言 本节常见面试题 如何判断对象是否死亡(两种方法)。简单的介绍一下强引用、软引用、弱引用、虚引用(虚引用与软引用和弱引用的区别、使用软引用能带来的好处)。如何判断一个常量是废弃常量如何判断一个类是无用的类垃圾收…...
【简单】209.长度最小的子数组
题目描述 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回0。 示例 1: 输入&am…...
细说 Java 引用(强、软、弱、虚)和 GC 流程(二)
一、前文回顾 在 细说Java 引用(强、软、弱、虚)和 GC 流程(一) 我们对Java 引用有了总体的认识,本文将继续深入分析 Java 引用在 GC 时的一些细节。 还是从我们在前文中提到的引用流程图里说起,这里不清…...
CSS通过webkit-scrollbar设置滚动条样式
查看::-webkit-scrollbar-*各项关系 以下图为例,可以分别定义滚动条背景、滚动轨道、滚动滑块的样式。 需要先给外部容器设置高度,再设置overflow: auto,最后设置三个webkit属性。 <!DOCTYPE html> <html lang"en">…...
Win10配置VSCode的C/C++编译环境
GNU(编译器工具集合)包含了g、gcc和gdb等编译器。MinGW(Minimalist GNU for Windows)是一个适用于Windows操作系统的最小化的GNU工具集,它包括了GCC编译器(包括g)以及其他一些必要的库和工具。M…...
数据结构与算法再探(七)查找-排序
查找 一、二分查找 二分查找是一种高效的查找算法,适用于在已排序的数组或列表中查找特定元素。它通过将搜索范围逐步减半来快速定位目标元素。理解二分查找的“不变量”和选择左开右闭区间的方式是掌握这个算法的关键。 二分查找关键点 不变量 在二分查找中&a…...
【C语言】指针(5)
前言:上篇文章的末尾我们使用了转移表来解决代码冗余的问题,那我们还有没有什么办法解决代码冗余呢?有的这就是接下来要说的回调函数。 往期文章: 指针1 指针2 指针3 指针4 文章目录 一,回调函数二,qsort实现快速排序1…...
大数据组件(四)快速入门实时数据湖存储系统Apache Paimon(2)
Paimon的下载及安装,并且了解了主键表的引擎以及changelog-producer的含义参考: 大数据组件(四)快速入门实时数据湖存储系统Apache Paimon(1) 利用Paimon表做lookup join,集成mysql cdc等参考: 大数据组件(四)快速入门实时数据…...
PLC通讯
PPI通讯 是西门子公司专为s7-200系列plc开发的通讯协议。内置于s7-200 CPU中。PPI协议物理上基于RS-485口,通过屏蔽双绞线就可以实现PPI通讯。PPI协议是一种主-从协议。主站设备发送要求到从站设备,从站设备响应,从站不能主动发出信息。主站…...
前端js进阶,ES6语法,包详细
进阶ES6 作用域的概念加深对js理解 let、const申明的变量,在花括号中会生成块作用域,而var就不会生成块作用域 作用域链本质上就是底层的变量查找机制 作用域链查找的规则是:优先查找当前作用域先把的变量,再依次逐级找父级作用域直到全局…...
Scrum方法论指导下的Deepseek R1医疗AI部署开发
一、引言 1.1 研究背景与意义 在当今数智化时代,软件开发方法论对于项目的成功实施起着举足轻重的作用。Scrum 作为一种广泛应用的敏捷开发方法论,以其迭代式开发、快速反馈和高效协作的特点,在软件开发领域占据了重要地位。自 20 世纪 90 …...
LINUX安装使用Redis
参考 Install Redis on Linux | Docs 安装命令 sudo apt-get install -y lsb-release curl gpgcurl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpgsudo chmod 644 /usr/share/keyrings/redis-archive-keyrin…...
循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
对象回调初步研究
_OBJECT_TYPE结构分析 在介绍什么是对象回调前,首先要熟悉下结构 以我们上篇线程回调介绍过的导出的PsProcessType 结构为例,用_OBJECT_TYPE这个结构来解析它,0x80处就是今天要介绍的回调链表,但是先不着急,先把目光…...
数据库——redis
一、Redis 介绍 1. 概述 Redis(Remote Dictionary Server)是一个开源的、高性能的内存键值数据库系统,具有以下核心特点: 内存存储架构:数据主要存储在内存中,提供微秒级的读写响应 多数据结构支持&…...
