从C++编程入手设计模式2——工厂模式

从C++编程入手设计模式

工厂模式

​ 我们马上就要迎来我们的第二个创建型设计模式:工厂方法模式(Factory Method Pattern)。换而言之,我们希望使用一个这样的接口,使用其他手段而不是直接创建的方式(说的有点奇怪,大致意思是——不是直接new,而是使用父子对象机制,给定一个判断条件让我们的工厂类选择创建具体的子类)

​ 这是因为在软件开发中,直接在代码中使用 new 关键字创建对象会导致代码与具体类紧密耦合,降低了系统的灵活性和可扩展性。工厂方法模式通过引入工厂接口和具体工厂类,将对象的创建过程封装起来,使得客户端代码与具体产品类解耦,从而提高了系统的可维护性和可扩展性。

​ 一个完整的工厂模式中存在四个基本的类。

  1. 抽象产品(Product):定义产品的接口,是所有具体产品类的父类。
  2. 具体产品(ConcreteProduct):实现了抽象产品接口的具体类,表示被创建的对象。
  3. 抽象工厂(Creator):声明工厂方法 factoryMethod(),返回抽象产品类型的对象。
  4. 具体工厂(ConcreteCreator):实现抽象工厂中的工厂方法,返回具体产品的实例。

​ 很显然,抽象的产品和类是一个接口,我们所有的产品都需要满足这个接口,或者说,是属于这个产品类的对象,需要被对应的具体的工厂所创建。当然,对于小项目,笔者一般喜欢合并抽象工厂和具体工厂为工厂,这样的话直接对这工厂类发起对象创建请求即可。

C++实现的一些要点

  • 使用抽象类和虚函数:通过定义抽象产品类和抽象工厂类,利用虚函数实现多态性,使得客户端代码可以通过基类指针或引用操作具体产品对象。
  • 使用智能指针管理对象生命周期:为了避免内存泄漏,建议使用 std::unique_ptrstd::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;
}

一些你需要注意的事情

  • 避免过度使用:在对象创建过程简单且不会发生变化的情况下,使用工厂方法模式可能会增加系统的复杂性,导致代码冗余。(换而言之,不要到处用这个东西,除非真需要了(大量相似对象的创建))
  • 合理组织类结构:随着产品种类和工厂类的增加,类的数量也会增加,需要合理组织类结构,避免类爆炸。
  • 结合其他设计模式使用:工厂方法模式可以与其他设计模式(如单例模式、抽象工厂模式)结合使用,以满足更复杂的系统需求。

例子:快餐连锁店的汉堡制作系统

背景:

您正在为一家快餐连锁店开发一个汉堡制作系统。不同的快餐品牌(如麦当劳和汉堡王)有各自的汉堡制作方式,包括使用的面包类型、配料和包装方式。

任务:

  1. 定义一个抽象基类 Burger,包含纯虚函数 grill()prepare()wrap(),以及成员变量如 namebunTypecondiments
  2. 实现具体的汉堡类,如 McDonaldsCheeseBurgerBurgerKingCheeseBurger,分别继承自 Burger,并实现上述方法,输出相应的制作步骤。
  3. 创建一个抽象工厂类 BurgerJoint,包含纯虚函数 createBurger(const std::string& type),用于创建不同类型的汉堡。
  4. 实现具体的工厂类,如 McDonaldsBurgerKing,继承自 BurgerJoint,根据传入的类型创建相应的汉堡实例。
  5. 在主函数中,模拟客户在不同快餐店点餐的过程,使用工厂类创建汉堡对象,并调用其制作方法。

这些要求你需要做到

  • 使用 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() 三个纯虚函数,代表了制作汉堡的三个主要步骤。然后,我为麦当劳和汉堡王分别实现了具体的汉堡类,如 McBurgerMcCheeseBurgerBurgerKingBurgerBurgerKingCheeseBurger,每个类都根据品牌和汉堡类型的不同,实现了各自的制作流程。

class BurgerProvider {
public:virtual std::unique_ptr<AbstractBurger> create_specifiedBurger(const std::string& specified_type) = 0;virtual ~BurgerProvider() = default;
};

为了创建这些汉堡对象,我定义了一个抽象工厂类 BurgerProvider,并为每个品牌实现了具体的工厂类 McBurgerProviderBurgerKingProvider。这些工厂类根据传入的参数(如 “normal” 或 “cheese”)来决定创建哪种具体的汉堡对象。通过这种方式,客户端代码可以通过工厂类来创建所需的汉堡,而无需了解具体的实现细节,从而实现了对象创建的解耦。

习题

背景:

您正在开发一个通知发送系统,支持多种通知方式,如电子邮件(Email)、短信(SMS)和推送通知(Push Notification)。每种通知方式有其特定的发送逻辑和所需的参数。

任务:

  1. 定义一个抽象基类 Notification,包含纯虚函数 send(const std::string& message)
  2. 实现具体的通知类,如 EmailNotificationSMSNotificationPushNotification,分别继承自 Notification,并实现发送逻辑。
  3. 创建一个工厂类 NotificationFactory,包含静态成员函数 createNotification(const std::string& type),根据传入的类型创建相应的通知对象。
  4. 在主函数中,模拟发送不同类型通知的过程,使用工厂类创建通知对象,并调用其发送方法。

要求:

  • 使用 std::unique_ptr 管理对象生命周期。
  • 考虑每种通知方式所需的特定参数,并在创建对象时传入。
  • 保持代码的可扩展性,方便将来添加新的通知方式。

笔者也有自己的实现:modern-cpp-patterns-playground/FactoryBaseMethod/NotificationSystem at main · Charliechen114514/modern-cpp-patterns-playground

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/diannao/84938.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL、PostgreSQL、Oracle 区别详解

MySQL、PostgreSQL、Oracle 区别详解 一、基础架构对比 1.1 数据库类型 MySQL:关系型数据库(支持NoSQL插件如MySQL Document Store)PostgreSQL:对象-关系型数据库(支持JSON等半结构化数据)Oracle:多模型数据库(关系型+文档+图+空间等)关键结论:PostgreSQL在数据类型…

window11系统 使用GO语言建立TDengine 连接

目录 1、安装GCC、TDengine-client 1、github下载mingw64 软件包 2、解压指定目录、配置环境变量 3、检验gcc是否安装成功 4、安装TDengine-client 2、配置go环境变量 3、配置Goland 系统变量、重启Goland&#xff08;该软件自己也有系统变量&#xff0c;有时候会和win…

VR 赋能病毒分离鉴定:开启微观探索新视界

在大众认知里&#xff0c;VR 技术往往与沉浸式游戏体验、虚拟社交紧密相连&#xff0c;让人仿佛置身于奇幻的虚拟世界中&#xff0c;感受着科技带来的奇妙娱乐享受。而病毒分离鉴定&#xff0c;听起来则是一个充满专业性与严肃性的科学领域&#xff0c;它关乎病毒的研究、疾病的…

Azure Devops pipeline 技巧和最佳实践

1. 如何显示release pipeline ? 解决方法: 登录devops, 找到organization - pipeline - setting下的Disable creation of classic release pipelines,禁用该选项。 然后在project - pipeline - setting,禁用Disable creation of classic release pipelines 现在可以看到r…

GPU的通信技术

GPU 之间直接通信主要采用了以下几种技术1&#xff1a; GPUDirect P2P&#xff1a;NVIDIA 开发的技术&#xff0c;用于单机上的 GPU 间高速通信。在没有该技术时&#xff0c;GPU 间数据交换需先通过 CPU 和 PCIe 总线复制到主机固定的共享内存&#xff0c;再复制到目标 GPU&…

重新测试deepseek Jakarta EE 10编程能力

听说deepseek做了一个小更新&#xff0c;我重新测试了一下Jakarta EE 10编程能力&#xff1b;有点进步&#xff0c;遗漏的功能比以前少了。 采用Jakarta EE 10 编写员工信息表维护表&#xff0c;包括员工查询与搜索、员工列表、新增员工、删除员工&#xff0c;修改员工&#xf…

​Windows 11 安装 Miniconda 与 Jupyter 全流程指南​

​一、Miniconda 安装与配置​ 1. 下载安装程序 ​访问官网​&#xff1a;打开 Miniconda 官网&#xff0c;下载 ​Python 3.x 版本的 Windows 64 位安装包​。​安装路径选择​&#xff1a; 推荐路径&#xff1a;D:\Miniconda3&#xff08;避免使用中文路径和空格&#xff0…

RuoYi前后端分离框架集成手机短信验证码(一)之后端篇

一、背景 本项目基于RuoYi 3.8.9前后端分离框架构建,采用Spring Security实现系统权限管理。作为企业级应用架构的子模块,系统需要与顶层项目实现用户数据无缝对接(以手机号作为统一用户标识),同时承担用户信息采集的重要职能。为此,我们在保留原有账号密码登录方式的基…

Java ThreadLocal 应用指南:从用户会话到数据库连接的线程安全实践

ThreadLocal 提供了一种线程局部变量&#xff08;thread-local variables&#xff09;的机制&#xff0c;这意味着每个访问该变量的线程都会拥有其自己独立的、初始化的变量副本。这确保了线程之间不会共享数据&#xff0c;也避免了因共享数据而可能产生的竞争条件和同步问题&a…

GitCode镜像门法律分析:PL协议在中国的司法实践

本文以2022年引发广泛争议的GitCode开源代码镜像事件为研究对象&#xff0c;系统分析公共许可证&#xff08;Public License&#xff0c;PL&#xff09;在中国法律体系下的适用性挑战。通过研究中国法院近五年涉及GPL、Apache、MIT等主流协议的21个司法案例&#xff0c;揭示开源…

Rider崩溃问题终极解决指南

JetBrains Rider 2025.1.2 频繁崩溃问题解决指南 问题描述&#xff1a; 编辑器频繁自动崩溃&#xff0c;任务管理器显示大量 Git for Windows 进程被启动。 原因分析&#xff1a; 这是 Rider 的自动版本控制功能导致的。当检测到代码变更时&#xff0c;编辑器会不断尝试启动 …

4 串电池保护芯片创芯微CM1341-DAT使用介绍

特性 专用于 4 串锂/铁/钠电池的保护芯片&#xff0c;内置有高精度电压检测电路和电流检测电路。通过检测各节电池的电压、充放电电流及温度等信息&#xff0c;实现电池过充电、过放电、均衡、断线、低压禁充、放电过电流、短路、充电过电流和过温保护等功能&#xff0c;放电过…

煤矿电液控制器-底座倾角传感器4K型护套连接器ZE0703-09(100)

煤矿电液控制器作为井下自动化开采的核心设备&#xff0c;其可靠性直接关系到生产安全与效率。在众多关键组件中&#xff0c;底座倾角传感器4K型护套连接器ZE0703-09&#xff08;100&#xff09;凭借独特设计成为保障系统稳定运行的"神经末梢"&#xff0c;其技术特性…

Vue计算属性与监视

在Vue.js中&#xff0c;处理复杂的逻辑和数据依赖关系是构建高效、可维护的前端应用的关键。Vue提供了两种强大的工具来帮助我们实现这一点&#xff1a;计算属性&#xff08;Computed Properties&#xff09; 和 侦听器&#xff08;Watchers&#xff09;。本文将深入探讨这两者…

基于RT-Thread的STM32F4开发第七讲——RTC(硬件、软件)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、RT-Thread工程创建1.硬件RTC配置2.软件RTC配置3.RTC闹钟配置 总结 前言 本章是基于RT-Thread studio实现RTC硬件和软件下的日历时钟功能&#xff0c;开发板…

Java面试:从Spring Boot到分布式系统的技术探讨

场景一&#xff1a;电商平台的订单处理 面试官&#xff1a; “谢先生&#xff0c;假设我们在一个电商平台工作&#xff0c;你将如何使用Spring Boot构建一个订单处理服务&#xff1f;” 谢飞机&#xff1a; “这个简单&#xff0c;我会使用Spring Boot快速启动项目&#xff0…

【Redis】string 类型

string 一. string 类型介绍二. string 命令set、getmget、msetsetnx、setex、psetexincr、incrby、decr、decrby、incrbyfloatappend、getrange、setrange、strlen 三. string 命令小结四. string 内部编码方式五. string 的应用场景缓存功能计数功能共享会话手机验证码 六. 什…

HTTP/HTTPS与SOCKS5三大代理IP协议,如何选择最佳协议?

在复杂多变的网络环境中&#xff0c;代理协议的选择直接影响数据安全、访问效率和业务稳定性。HTTP、HTTPS和SOCKS5作为三大主流代理协议&#xff0c;各自针对不同场景提供独特的解决方案。本文将从协议特性、性能对比到选型策略&#xff0c;为您揭示如何根据业务需求精准匹配最…

【ArcGIS Pro微课1000例】0071:将无人机照片生成航线、轨迹点、坐标高程、方位角

文章目录 一、照片预览二、生成轨迹点三、照片信息四、查看方位角五、轨迹点连成线一、照片预览 数据位于配套实验数据包中的0071.rar,解压之后如下: 二、生成轨迹点 地理标记照片转点 (数据管理),用于根据存储在地理标记照片文件(.jpg 或 .tif)元数据中的 x、y 和 z 坐…

【C++项目】:仿 muduo 库 One-Thread-One-Loop 式并发服务器

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;C从入门到精通 目录 &#x1f525; 前言 一&#xff1a;&#x1f525; 项目储备知识 &#x1f98b; HTTP 服务器&#x1f98b; Reactor 模型&#x1f380; 单 Reactor 单线程&#xff1a;单I/O多路…