从C++编程入手设计模式2——工厂模式
从C++编程入手设计模式
工厂模式
我们马上就要迎来我们的第二个创建型设计模式:工厂方法模式(Factory Method Pattern)。换而言之,我们希望使用一个这样的接口,使用其他手段而不是直接创建的方式(说的有点奇怪,大致意思是——不是直接new,而是使用父子对象机制,给定一个判断条件让我们的工厂类选择创建具体的子类)
这是因为在软件开发中,直接在代码中使用 new
关键字创建对象会导致代码与具体类紧密耦合,降低了系统的灵活性和可扩展性。工厂方法模式通过引入工厂接口和具体工厂类,将对象的创建过程封装起来,使得客户端代码与具体产品类解耦,从而提高了系统的可维护性和可扩展性。
一个完整的工厂模式中存在四个基本的类。
- 抽象产品(Product):定义产品的接口,是所有具体产品类的父类。
- 具体产品(ConcreteProduct):实现了抽象产品接口的具体类,表示被创建的对象。
- 抽象工厂(Creator):声明工厂方法
factoryMethod()
,返回抽象产品类型的对象。 - 具体工厂(ConcreteCreator):实现抽象工厂中的工厂方法,返回具体产品的实例。
很显然,抽象的产品和类是一个接口,我们所有的产品都需要满足这个接口,或者说,是属于这个产品类的对象,需要被对应的具体的工厂所创建。当然,对于小项目,笔者一般喜欢合并抽象工厂和具体工厂为工厂,这样的话直接对这工厂类发起对象创建请求即可。
C++实现的一些要点
- 使用抽象类和虚函数:通过定义抽象产品类和抽象工厂类,利用虚函数实现多态性,使得客户端代码可以通过基类指针或引用操作具体产品对象。
- 使用智能指针管理对象生命周期:为了避免内存泄漏,建议使用
std::unique_ptr
或std::shared_ptr
管理动态分配的对象。 - 将对象创建逻辑封装在工厂类中:将具体产品类的实例化过程封装在具体工厂类中,客户端代码只需调用工厂方法获取产品对象,而无需关心具体的创建细节。
下面是一个非常经典的例子,但是不够好,体现不出来为什么工厂模式存在,但是绘景代码是如下的:
#include <iostream>
#include <memory>class Shape {
public:virtual void draw() = 0;virtual ~Shape() = default;
};class Circle : public Shape {
public:void draw() override {std::cout << "Drawing a Circle" << std::endl;}
};class Square : public Shape {
public:void draw() override {std::cout << "Drawing a Square" << std::endl;}
};class ShapeFactory {
public:virtual std::unique_ptr<Shape> createShape() = 0;virtual ~ShapeFactory() = default;
};class CircleFactory : public ShapeFactory {
public:std::unique_ptr<Shape> createShape() override {return std::make_unique<Circle>();}
};class SquareFactory : public ShapeFactory {
public:std::unique_ptr<Shape> createShape() override {return std::make_unique<Square>();}
};int main() {std::unique_ptr<ShapeFactory> circleFactory = std::make_unique<CircleFactory>();std::unique_ptr<Shape> circle = circleFactory->createShape(); // 是一个shapecircle->draw(); // 但是是circle,所以调用的就是circle的方法std::unique_ptr<ShapeFactory> squareFactory = std::make_unique<SquareFactory>();std::unique_ptr<Shape> square = squareFactory->createShape();square->draw();return 0;
}
一些你需要注意的事情
- 避免过度使用:在对象创建过程简单且不会发生变化的情况下,使用工厂方法模式可能会增加系统的复杂性,导致代码冗余。(换而言之,不要到处用这个东西,除非真需要了(大量相似对象的创建))
- 合理组织类结构:随着产品种类和工厂类的增加,类的数量也会增加,需要合理组织类结构,避免类爆炸。
- 结合其他设计模式使用:工厂方法模式可以与其他设计模式(如单例模式、抽象工厂模式)结合使用,以满足更复杂的系统需求。
例子:快餐连锁店的汉堡制作系统
背景:
您正在为一家快餐连锁店开发一个汉堡制作系统。不同的快餐品牌(如麦当劳和汉堡王)有各自的汉堡制作方式,包括使用的面包类型、配料和包装方式。
任务:
- 定义一个抽象基类
Burger
,包含纯虚函数grill()
、prepare()
和wrap()
,以及成员变量如name
、bunType
和condiments
。 - 实现具体的汉堡类,如
McDonaldsCheeseBurger
和BurgerKingCheeseBurger
,分别继承自Burger
,并实现上述方法,输出相应的制作步骤。 - 创建一个抽象工厂类
BurgerJoint
,包含纯虚函数createBurger(const std::string& type)
,用于创建不同类型的汉堡。 - 实现具体的工厂类,如
McDonalds
和BurgerKing
,继承自BurgerJoint
,根据传入的类型创建相应的汉堡实例。 - 在主函数中,模拟客户在不同快餐店点餐的过程,使用工厂类创建汉堡对象,并调用其制作方法。
这些要求你需要做到
- 使用
std::unique_ptr
管理对象生命周期。智能指针是一个好东西,多用用! - 保持代码的可扩展性,方便将来添加新的快餐品牌或汉堡类型。
实现:modern-cpp-patterns-playground/FactoryBaseMethod/BurgerCreator at main · Charliechen114514/modern-cpp-patterns-playground
class AbstractBurger {
public:virtual void grill() = 0;virtual void prepare() = 0;virtual void wrap() = 0;virtual ~AbstractBurger() = default; /* this is required to the parent calss */
};
首先,咱们起手定义了一个抽象基类 AbstractBurger
,其中包含了 grill()
、prepare()
和 wrap()
三个纯虚函数,代表了制作汉堡的三个主要步骤。然后,我为麦当劳和汉堡王分别实现了具体的汉堡类,如 McBurger
、McCheeseBurger
、BurgerKingBurger
和 BurgerKingCheeseBurger
,每个类都根据品牌和汉堡类型的不同,实现了各自的制作流程。
class BurgerProvider {
public:virtual std::unique_ptr<AbstractBurger> create_specifiedBurger(const std::string& specified_type) = 0;virtual ~BurgerProvider() = default;
};
为了创建这些汉堡对象,我定义了一个抽象工厂类 BurgerProvider
,并为每个品牌实现了具体的工厂类 McBurgerProvider
和 BurgerKingProvider
。这些工厂类根据传入的参数(如 “normal” 或 “cheese”)来决定创建哪种具体的汉堡对象。通过这种方式,客户端代码可以通过工厂类来创建所需的汉堡,而无需了解具体的实现细节,从而实现了对象创建的解耦。
习题
背景:
您正在开发一个通知发送系统,支持多种通知方式,如电子邮件(Email)、短信(SMS)和推送通知(Push Notification)。每种通知方式有其特定的发送逻辑和所需的参数。
任务:
- 定义一个抽象基类
Notification
,包含纯虚函数send(const std::string& message)
。 - 实现具体的通知类,如
EmailNotification
、SMSNotification
和PushNotification
,分别继承自Notification
,并实现发送逻辑。 - 创建一个工厂类
NotificationFactory
,包含静态成员函数createNotification(const std::string& type)
,根据传入的类型创建相应的通知对象。 - 在主函数中,模拟发送不同类型通知的过程,使用工厂类创建通知对象,并调用其发送方法。
要求:
- 使用
std::unique_ptr
管理对象生命周期。 - 考虑每种通知方式所需的特定参数,并在创建对象时传入。
- 保持代码的可扩展性,方便将来添加新的通知方式。
笔者也有自己的实现:modern-cpp-patterns-playground/FactoryBaseMethod/NotificationSystem at main · Charliechen114514/modern-cpp-patterns-playground
相关文章:
从C++编程入手设计模式2——工厂模式
从C编程入手设计模式 工厂模式 我们马上就要迎来我们的第二个创建型设计模式:工厂方法模式(Factory Method Pattern)。换而言之,我们希望使用一个这样的接口,使用其他手段而不是直接创建的方式(说的有…...

云原生 Cloud Native Build (CNB)使用初体验
云原生 Cloud Native Build(CNB)使用初体验 引言 当“一切皆可云”成为趋势,传统开发环境正被云原生工具重塑。腾讯云CNB(Cloud Native Build)作为一站式开发平台,试图解决多环境协作难题。 本文将分享c…...

格式工厂 FormatFactory v5.20.便携版 ——多功能媒体文件转换工具 长期更新
—————【下 载 地 址】——————— 【本章下载一】:https://pan.xunlei.com/s/VORWF3Q7D0eCVV06LHbzheD-A1?pwdjikz# 【本章下载二】:https://pan.quark.cn/s/8ee59ed83658 【百款黑科技】:https://ucnygalh6wle.feishu.cn/wiki/…...

数据可视化--使用matplotlib绘制高级图表
目录 一、绘制等高线图 contour() 二、绘制矢量场流线图 streamplot() 三、绘制棉棒图 stem() 四、绘制哑铃图 五、绘制甘特图 六、绘制人口金字塔图 barh() 七、绘制漏斗图 简易版漏斗图 八、绘制桑基图 Sankey()---创建桑基图 add()---添加桑基图的选项 finish()…...
卷积神经网络(CNN)完全指南:从原理到实战
卷积神经网络(CNN)完全指南:从原理到实战 引言:为什么CNN改变了计算机视觉? 2012年,AlexNet在ImageNet竞赛中以压倒性优势获胜,将错误率降低了近10个百分点,这标志着卷积神经网络(CNN)时代的开始。如今&a…...

如何做好一个决策:基于 Excel的决策树+敏感性分析应用
决策点: 开发新产品? (是 / 否) 因素 (如果是): 市场接受度 (高 / 中 / 低);概率: 高(0.3), 中(0.5), 低(0.2) 结果值 (NPV): 高(+$1M), 中(+$0.2M), 低(-$0.5M) 不开发成本/收益: $0 开发计算: EMV(市场接受度) = (0.3 * 1M) + (0.5 * 0.2M) + (0.2 * -0.5M) = $0.3M + $…...

【模拟电子电路-工具使用】
模拟电子电路-工具使用 ■ 1. 模拟软件■ 1. circuit JS ■ 2. 万用表■ 3. 示波器■ 4.■ 5.■ 6.■ 7. ■ 1. 模拟软件 ■ 1. circuit JS ■ 2. 万用表 ■ 3. 示波器 ■ 4. ■ 5. ■ 6. ■ 7....

[ElasticSearch] ElasticSearch的初识与基本操作
🌸个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 🏵️热门专栏: 🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 🍕 Collection与…...
Spring AI 代理模式(Agent Agentic Patterns)
一、Agentic Patterns 核心思想 根据Anthropic《构建高效代理》研究报告,高效LLM代理的设计应遵循两大核心原则: 简单性优先:避免过度设计,从最简单的解决方案开始可组合性:通过模块化设计实现灵活组合而非复杂框架 …...

搜索引擎2.0(based elasticsearch6.8)设计与实现细节(完整版)
1 简介 1.1 背景 《搜索引擎onesearch 1.0-设计与实现.docx》介绍了1.0特性,搜索schema,agg,表达式搜索映射,本文介绍onesearch 2.0 新特性, 参考第2节 规划特性与发布计划 1.2 关键词 文档 Document elasticsearch 一行数据称为…...
ps中前景色和背景色
在Photoshop(简称PS)中,前景色和背景色是两个非常重要的概念,它们直接影响着绘图、填充、渐变等操作的最终效果。以下是对前景色和背景色的全面、深入解释: 一、前景色与背景色的定义 前景色:指的是当前绘…...
网页前端开发(基础进阶2--JS)
前面学习了html与css,接下来学习JS(JavaScript与Java无关)。 web标准(网页标准)分为3个部分: 1.html主要负责网页的结构(页面的元素和内容) 2.css主要负责网页的表现(…...

Go 即时通讯系统:客户端与服务端 WebSocket 通信交互
客户端和服务端的交互 客户端与服务端建立连接 客户端:客户端通过浏览器或者其他应用程序发起一个 HTTP 请求到服务端的 /socket.io 路径。在请求中会携带用户的 UUID 作为参数(通过 c.Query("user") 获取)。 // router/socket.…...
2025年5月AI科技领域周报(5.19-5.25):大模型多模态突破 具身智能开启机器人新纪元
2025年5月AI科技领域周报(5.19-5.25):大模型多模态突破 具身智能开启机器人新纪元 目录 2025年5月AI科技领域周报(5.19-5.25):大模型多模态突破 具身智能开启机器人新纪元一、本周热点回顾1. 百度发布全球首…...

某航后缀混淆逆向与顶像风控分析
文章目录 1. 写在前面2. 接口分析3. 加密分析4. 风控分析 【🏠作者主页】:吴秋霖 【💼作者介绍】:擅长爬虫与JS加密逆向分析!Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致…...

[Protobuf]常见数据类型以及使用注意事项
[Protobuf]常见数据类型以及使用注意事项 水墨不写bug 文章目录 一、基本数据类型1、字段2、字段的修饰规则 二、自定义数据类型1、message类型2、enum类型3、Any类型4、oneof类型5、map类型 三、小工具1.hexdump2.decode 四、注意事项 一、基本数据类型 protobuf 支持多种基础…...
【C/C++】面试基础题目收集
C 软件开发面试中常见的刷题题目通常可分为以下几大类:数据结构与算法、系统编程、面向对象设计、C 语言特性、并发编程等。 🧠 一、数据结构与算法(力扣/牛客经典题) 掌握 STL 和底层结构实现能力: 📌 数…...

模拟实现线程池(线程数目为定值)和定时器
前言 昨天学习关于定时器的相关知识。今天花时间去模拟实现了一个定时器,同时也去模拟实现了一个线程池(线程数目为定值)。我感觉我收获了很多,对于线程的理解加深了。跟大家分享一下~ 线程池和定时器(这个是主要)的实现 代码 线程池 import java.ut…...

数据结构之队列实验
引言 在计算机科学中,进制转换是基础但重要的操作。例如将一个十进制数转换为二进制或八进制表示时,我们通常使用“短除法”——即不断用目标进制去除当前数,记录余数,直到商为0为止。 这种方法得到的是低位先产生的结果&#x…...
Java求职者面试题详解:计算机网络、操作系统、设计模式与数据结构
Java求职者面试题详解:计算机网络、操作系统、设计模式与数据结构 第一轮:基础概念问题 1. 请解释什么是HTTP协议? HTTP(HyperText Transfer Protocol)是一种用于传输超文本的协议,它定义了客户端和服务…...
每日八股文6.1
每日八股-6.1 Go1.Sync.map的底层实现2.结构体的tag如何获取?3.Go实现单例模式(使用sync.Once)4.Go实现单例模式(不使用sync.Once)5.make和new的区别6.Go项目引用包为什么用_以及包的init()函数7.如何判断一个结构体是…...

【Ubuntu】摸鱼技巧之虚拟机环境复制
前言 提示:所有的操作都需要关闭虚拟机 如何快速在其它电脑布置,linux环境,如果我们有一个环境直接拷贝就有时间摸鱼呀。 1.直接复制简单粗暴 不做赘述,如果不会复制,那么请右击鼠标压缩复制 2.克隆虚拟机 2.1 …...

室内VR全景助力房产营销及装修
在当今的地产行业,VR全景已成为不可或缺的应用工具。从地产直播到楼市VR地图,从效果图到水电家装施工记录,整个地产行业的上下游生态中,云VR全景的身影无处不在。本文将探讨VR全景在房产营销及装修领域的应用,并介绍众…...

jenkins集成gitlab实现自动构建
jenkins集成gitlab实现自动构建 前面我们已经部署了Jenkins和gitlab,本文介绍将二者结合使用 项目源码上传至gitee提供公网访问:https://gitee.com/ye-xiao-tian/my-webapp 1、创建一个群组和项目 2、添加ssh密钥 #生成密钥 [rootgitlab ~]# ssh-keyge…...
【C语言练习】070. 编写代码处理C语言中的异常情况
070. 编写代码处理C语言中的异常情况 070. 编写代码处理C语言中的异常情况C语言异常处理的基本思路返回值检查示例errno使用示例setjmp/longjmp示例最佳实践建议1. 使用返回值检查错误2. 使用全局变量记录错误状态3. 使用回调函数或信号处理程序4. 使用`setjmp`和`longjmp`示例…...
Java基本数据类型、抽象类和接口、枚举、时间类、String类全面介绍
JAVA基本数据类型知识总结 基本数据类型(Primitive Types) 类型占用字节默认值范围示例byte10-128 ~ 127byte a 100;short20-32,768 ~ 32,767short b 2000;int40-2 ~ 2-1int c 100000;long80L-2⁶ ~ 2⁶-1long d 10000000000L;float40.0f~7位小数f…...

Spring Boot微服务架构(八):开发之初就引入APM工具监控
使用 APM(Application Performance Management)工具监控 Spring Boot 应用,可以帮助开发者实时追踪性能瓶颈、分析调用链路、监控资源使用情况,并快速定位故障。以下是详细的步骤和常用工具的选择指南: 一、常用 A…...

大规模真实场景 WiFi 感知基准数据集
一段话总结 本文提出CSI-Bench,首个大规模真实场景WiFi感知基准数据集,覆盖26个室内环境、35名用户、16种商用设备,包含461小时有效数据,支持跌倒检测、呼吸监测、定位、运动源识别等单任务及用户身份、活动、 proximity联合标注的多任务学习。通过标准化评估协议和基线模…...

Python实现HPSO-TVAC优化算法优化支持向量机SVC分类模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在当今数据驱动的时代,支持向量机(SVM)作为一种经典的机器学习算法,…...

ck-editor5的研究 (3):初步使用 CKEditor5 的事件系统和API
前言 在上一篇文章中—— ck-editor5的研究(2):对 CKEditor5 进行设计,并封装成一个可用的 vue 组件 ,我已经把 CKEditor5 封装成了一个通用vue组件,并且成功在nuxt中运行,并具备一定的通用性&…...