突破编程_C++_设计模式(简单工厂模式)
1 简单工厂模式的概念
简单工厂模式(Simple Factory Pattern)是设计模式中的一种创建型模式。它的主要目的是将对象的实例化与使用解耦,使得客户端无需关心对象的创建细节,只需通过工厂类来获取所需的对象。
在简单工厂模式中,通常会有一个工厂类,它负责根据客户端的请求创建并返回相应的对象。客户端只需调用工厂类的方法并传入相应的参数,即可获取所需的对象,而无需关心对象的创建过程。
在简单工厂模式中,通常包括以下几个角色:
(1)工厂(Creator)角色: 工厂角色是简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。
(2)抽象产品(Product)角色: 抽象产品角色是简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
(3)具体产品(Concrete Product)角色: 具体产品角色是简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。
简单工厂模式允许客户端只需要传入一个正确的参数,就可以获取所需要的对象,而无需知道其创建的细节。这有利于明确各个类的职责,并优化整个软件体系结构。
2 简单工厂模式的实现步骤
在 C++ 实现简单工厂模式的实现步骤如下:
(1)定义产品接口: 首先,你需要定义一个抽象的产品接口,这个接口将声明所有产品对象都需要实现的方法。这确保了工厂方法能够返回统一类型的产品对象,而客户端则可以通过这个接口来操作这些对象。
(2)实现具体产品类: 接下来,创建实现了产品接口的具体产品类。这些类将实现接口中定义的方法,并且提供具体的功能。
(3)创建工厂类: 然后,你需要创建一个工厂类,这个类将负责根据客户端的请求创建并返回相应的产品对象。工厂类通常包含一个静态方法,这个方法接收一个参数(如产品类型或标识符),然后基于这个参数来实例化并返回相应的产品对象。
(4)客户端调用工厂方法: 客户端代码不需要直接实例化产品对象,而是调用工厂类的静态方法,并传入所需的参数。工厂方法根据这些参数创建并返回相应的产品对象。
(5)客户端使用产品对象: 客户端现在可以通过产品接口来使用返回的产品对象,而无需关心对象是如何创建的。
(6)处理资源释放: 由于工厂模式通常涉及到动态内存分配(例如使用 new 关键字创建对象),因此客户端在使用完产品对象后需要负责释放这些对象所占用的资源(如使用 delete 关键字)。这通常是客户端调用工厂方法后需要注意的一个方面。
如下为样例代码:
#include <iostream>
#include <memory>
#include <string> // 产品抽象接口
class Product {
public:virtual void use() = 0;virtual ~Product() {} // 虚析构函数确保正确释放派生类对象
};// 具体产品类A
class ProductA : public Product {
public:void use() override {std::cout << "Using ProductA" << std::endl;}
};// 具体产品类B
class ProductB : public Product {
public:void use() override {std::cout << "Using ProductB" << std::endl;}
};// 工厂类
class SimpleFactory {
public:// 使用智能指针返回产品对象 static std::unique_ptr<Product> createProduct(const std::string& type) {if (type == "A") {return std::make_unique<ProductA>(); // 使用make_unique创建ProductA对象 }else if (type == "B") {return std::make_unique<ProductB>(); // 使用make_unique创建ProductB对象 }else {return nullptr; // 或者抛出一个异常 }}
};// 客户端代码
int main()
{// 使用工厂方法创建产品对象,并自动管理其生命周期 auto productA = SimpleFactory::createProduct("A");if (productA) {productA->use();// 不需要手动delete,unique_ptr会在离开作用域时自动释放内存 }auto productB = SimpleFactory::createProduct("B");if (productB) {productB->use();// 同样,不需要手动delete }return 0;
}
上面代码的输出为:
Using ProductA
Using ProductB
3 简单工厂模式的应用场景
C++ 简单工厂模式的应用场景主要包括以下几种情况:
(1)创建对象不需要知道具体类的情况: 当客户端需要创建某个产品对象,但并不需要知道具体是哪个类的时候,可以使用简单工厂模式。工厂类根据客户端的请求和提供的参数,返回相应的产品对象,客户端只需要通过产品接口来操作这些对象。
(2)减少客户端与具体产品类的耦合: 在传统的设计中,客户端通常需要直接实例化具体的产品类。这会导致客户端与产品类紧密耦合,不利于代码的维护和扩展。通过使用简单工厂模式,客户端只需与工厂类交互,从而降低了与具体产品类的耦合度。
(3)需要创建的对象较少且不会经常变动: 简单工厂模式适用于产品类型相对较少且不太可能经常变动的场景。如果产品类型非常多或者经常需要添加新的产品类型,简单工厂模式可能会导致工厂类变得庞大而难以维护。在这种情况下,可能需要考虑使用其他更高级的工厂模式,如工厂方法模式或抽象工厂模式。
(4)隐藏产品类的具体实现细节: 简单工厂模式可以隐藏产品类的具体实现细节,使得客户端无需关心产品对象的创建过程。这有助于将对象的创建与使用分离,提高代码的可读性和可维护性。
(5)统一创建接口: 当多个产品类具有相似的创建逻辑时,可以使用简单工厂模式来统一这些产品的创建接口。这样,客户端可以通过统一的接口来创建不同类型的产品对象,简化了代码结构。
3.1 简单工厂模式应用于创建对象不需要知道具体类的情况
以下是一个简单工厂模式应用于创建对象而不需要知道具体类的样例。在这个例子中,有一个 Shape 接口和两个实现了这个接口的类:Circle 和 Rectangle。客户端代码通过简单工厂类 ShapeFactory 来创建这些形状对象,而无需知道它们的具体类。
#include <iostream>
#include <string>
#include <memory> // 用于std::unique_ptr // 形状接口
class Shape {
public:virtual void draw() = 0;virtual ~Shape() {} // 虚析构函数确保正确释放派生类对象
};// 圆形类
class Circle : public Shape {
public:void draw() override {std::cout << "Drawing a circle." << std::endl;}
};// 矩形类
class Rectangle : public Shape {
public:void draw() override {std::cout << "Drawing a rectangle." << std::endl;}
};// 形状工厂类
class ShapeFactory {
public:// 根据类型创建形状对象 static std::unique_ptr<Shape> createShape(const std::string& shapeType) {if (shapeType == "Circle") {return std::make_unique<Circle>();}else if (shapeType == "Rectangle") {return std::make_unique<Rectangle>();}else {return nullptr; // 或者抛出一个异常 }}
};// 客户端代码
int main()
{// 使用工厂创建形状对象,无需知道具体类 std::unique_ptr<Shape> shape1 = ShapeFactory::createShape("Circle");if (shape1) {shape1->draw(); // 输出:Drawing a circle. }std::unique_ptr<Shape> shape2 = ShapeFactory::createShape("Rectangle");if (shape2) {shape2->draw(); // 输出:Drawing a rectangle. }// 无需担心shape1和shape2的具体类型,它们都被当作Shape接口使用 return 0;
}
上面代码的输出为:
Drawing a circle.
Drawing a rectangle.
在这个例子中,客户端代码通过调用 ShapeFactory::createShape 方法并传入形状类型(“Circle"或"Rectangle”)来创建形状对象。由于返回的是 Shape 接口的 unique_ptr,客户端代码可以统一地调用 draw 方法,而无需关心实际创建的是哪种形状对象。这种方式使得客户端代码更加灵活,并且容易扩展新的形状类型,因为只需要在工厂类中添加新的创建逻辑即可。
3.2 简单工厂模式应用于减少客户端与具体产品类的耦合
以下是一个简单工厂模式应用于减少客户端与具体产品类耦合的 C++ 样例。在这个例子中,有一个 Payment 接口和两个实现了该接口的类:CreditCardPayment 和 CashPayment。客户端代码通过 PaymentFactory 简单工厂类来创建支付对象,从而减少了与具体支付类的耦合。
#include <iostream>
#include <string>
#include <memory> // 用于std::unique_ptr // 支付接口
class Payment {
public:virtual void pay() = 0;virtual ~Payment() {} // 虚析构函数确保正确释放派生类对象
};// 信用卡支付类
class CreditCardPayment : public Payment {
public:void pay() override {std::cout << "Processing credit card payment." << std::endl;}
};// 现金支付类
class CashPayment : public Payment {
public:void pay() override {std::cout << "Processing cash payment." << std::endl;}
};// 支付工厂类
class PaymentFactory {
public:// 根据支付方式创建支付对象 static std::unique_ptr<Payment> createPayment(const std::string& paymentType) {if (paymentType == "CreditCard") {return std::make_unique<CreditCardPayment>();}else if (paymentType == "Cash") {return std::make_unique<CashPayment>();}else {return nullptr; // 或者抛出一个异常 }}
};// 客户端代码
int main()
{// 使用工厂创建支付对象,减少与具体支付类的耦合 std::unique_ptr<Payment> payment1 = PaymentFactory::createPayment("CreditCard");if (payment1) {payment1->pay(); // 输出:Processing credit card payment. }std::unique_ptr<Payment> payment2 = PaymentFactory::createPayment("Cash");if (payment2) {payment2->pay(); // 输出:Processing cash payment. }// 客户端代码不需要直接实例化CreditCardPayment或CashPayment,只需要通过Payment接口和工厂类进行交互 return 0;
}
上面代码的输出为:
Processing credit card payment.
Processing cash payment.
在这个例子中,客户端代码不需要知道 CreditCardPayment 或 CashPayment 类的具体实现细节。它只需要调用 PaymentFactory::createPayment 方法,并传入支付方式(“CreditCard” 或 “Cash”),工厂类就会返回相应的支付对象。由于返回的是 Payment 接口的 unique_ptr,客户端代码可以统一地调用 pay 方法,而无需关心实际创建的是哪种支付对象。
这种方式减少了客户端与具体支付类的耦合,使得客户端代码更加简洁和易于维护。如果需要添加新的支付方式,只需要在工厂类中添加新的创建逻辑,而无需修改客户端代码。这符合开闭原则,即对扩展开放,对修改封闭。
4 简单工厂模式的优点与缺点
C++ 简单工厂模式的优点主要包括:
(1)封装性好: 简单工厂模式封装了对象创建的细节,客户端不需要直接实例化具体的产品类。这有助于隐藏具体产品类的实现细节,使得客户端代码更加简洁和易于维护。
(2)解耦: 简单工厂模式降低了客户端与具体产品类之间的耦合度。客户端只需要与工厂类交互,而不需要知道具体产品类的实现细节。这有助于减少代码的依赖性和提高系统的可维护性。
(3)代码复用: 工厂类集中了产品对象的创建逻辑,可以在多个地方重用该工厂类来创建对象,提高了代码的复用性。
(4)扩展性好: 当需要添加新的产品类时,只需要在工厂类中添加相应的创建逻辑,而无需修改客户端代码。这符合开闭原则,即对扩展开放,对修改封闭。
然而,C++ 简单工厂模式也存在一些缺点:
(1)违反单一职责原则: 简单工厂类通常负责创建多种类型的产品对象,这可能导致工厂类的职责过多,违反了单一职责原则。当产品类型较多时,工厂类的代码可能会变得庞大而难以维护。
(2)类型判断逻辑复杂: 简单工厂模式通常需要根据客户端提供的参数或类型信息来判断需要创建哪种类型的产品对象。随着产品类型的增加,类型判断逻辑可能会变得复杂和易错。
(3)不易于扩展新的产品族: 简单工厂模式是针对一个产品族设计的,如果需要添加新的产品族(即一组具有共同主题的产品),可能需要修改工厂类的代码,这违反了开闭原则。
(4)不易于使用继承等面向对象特性: 由于简单工厂模式通常直接返回具体产品类的实例,而不是通过继承等面向对象特性来创建对象,因此可能无法充分利用面向对象编程的优势。
需要注意的是,简单工厂模式是一种相对简单的工厂模式,适用于产品类型较少且不太可能经常变动的场景。在更复杂的场景中,可能需要考虑使用抽象工厂模式来应对更多的变化和扩展需求。
相关文章:
突破编程_C++_设计模式(简单工厂模式)
1 简单工厂模式的概念 简单工厂模式(Simple Factory Pattern)是设计模式中的一种创建型模式。它的主要目的是将对象的实例化与使用解耦,使得客户端无需关心对象的创建细节,只需通过工厂类来获取所需的对象。 在简单工厂模式中&a…...
C语言——快速排序
C语言——快速排序 一、 含义二、算法思想三、实现步骤代码实现 一、 含义 快速排序算法是在几种排序算法中效率最高的一个排序算法了,故称为快速排序,它的时间复杂度为:O(nlog2n),相比冒泡排序算法的O(n2)有很大的提升。 二、算…...
FP独立站获客秘籍大揭秘:简单高效,一看就会!
跨境电商的大潮中,越来越多的卖家选择跳出第三方平台的框架,拥抱独立站的自由与机遇。但独立站获客难、成本高的问题,也让不少卖家头疼不已。别担心,今天就来给大家揭秘FP独立站获客的简单高效方法! 首先,…...
英伟达tx2光驱烧录功能支持
今天得到一个任务,是在当前nvidia tx2平台上使能usb cdrom并且调试烧录功能。首先测试给到的信息是不能在平台上使用(废话嘛,能用还用我干嘛) 拿到本地ubuntu机器上看了下,使用brasero等软件可以顺利烧录。 此时捕获了…...

关于stm32(CubeMX+HAL库)的掉电检测以及flash读写
1.掉电检测 CubeMX配置 只需使能PVD中断即可 但是使能了PVD中断后还需要自行配置一些PWR寄存器中的参数,我也通过HAL库进行编写 void PVD_config(void) {//配置PWRPWR_PVDTypeDef sConfigPVD; sConfigPVD.PVDLevel PWR_PVDLEVEL_7; …...
Elastic script_score的使用
script_score介绍 在Elasticsearch中,script_score是在function_score查询中的一种功能强大的方式,允许用户使用内置Painless脚本语言或者其他支持的语言来动态计算每个文档的评分 script_score语法 GET /<索引名>/_search {"query":…...

【Spring Boot 3】获取已注入的Bean
【Spring Boot 3】获取已注入的Bean 背景介绍开发环境开发步骤及源码工程目录结构总结 背景 软件开发是一门实践性科学,对大多数人来说,学习一种新技术不是一开始就去深究其原理,而是先从做出一个可工作的DEMO入手。但在我个人学习和工作经历…...
C# 对于点位置的判断
1.判断点是否在一群点内部 要判断一个点是否在一个由多个点围成的多边形内部(例如一圈点),可以使用射线法(Ray Casting Algorithm)来实现。以下是一个简单的 C# 实现示例 using System;public class Point {public d…...

最新CLion + STM32 + CubeMX 开发环境搭建
网上有不少相关教程,但都是基于老版本Clion,新版有一些改变,但整体是简单了。 PS:本教程基于CLion 2023.3.4 安装所需工具参考:Clion搭建stm32开发环境(STM32F103C8T6),有这一篇就够…...
【Python3】观察者模式
观察者模式(Observer Pattern)是一种常见的设计模式,用于定义对象之间的一对多依赖关系,使得一个对象的状态改变能够通知所有依赖于它的对象并自动更新。 在观察者模式中,有两个核心角色: Subject…...

HTML5 Web Worker之性能优化
描述 由于 JavaScript 是单线程的,当执行比较耗时的任务时,就会阻塞主线程并导致页面无法响应,这就是 Web Workers 发挥作用的地方。它允许在一个单独的线程(称为工作线程)中执行耗时的任务。这使得 JavaScript 代码可…...

应对恶意IP攻击的有效方法
在当今数字化时代,网络攻击已经成为了互联网安全的重大挑战之一。恶意IP攻击是网络安全领域中的一种常见威胁,它可能导致数据泄露、服务中断、系统瘫痪等严重后果。因此,有效地应对恶意IP攻击至关重要。IP数据云将深入探讨如何应对恶意IP攻击…...

如何使用“Docker registry创建本地仓库,在服务器之间进行文件push和pull”?
1.1、在服务器1,运行registry docker run -d -p 5000:5000 -v ${PWD}/registry:/var/lib/registry --restart always --name registry registry:2.7.11.2、编辑/etc/docker/daemon.json 文件, 192.168.xxx.xxx 换成你自己 registry 服务的地址 sudo na…...

Rocky Linux - Primavera P6 EPPM 安装及分享
引言 继上一期发布的Redhat Linux版环境发布之后,近日我又制作了基于Rocky Enterprise Linux 的P6虚拟机环境,同样里面包含了全套P6 最新版应用服务 此虚拟机仅用于演示、培训和测试目的。如您在生产环境中使用此虚拟机,请先与Oracle Primav…...
API 管理调研
当前大部分团队内 API 管理都是依赖 Postman,postman最大的问题是共享问题,如果我要使用另外一个人已经调试好的 API 非常麻烦。因此,能实现协作的 API 管理将极大提升效率。 Yapi https://github.com/YMFE/yapi https://hellosean1025.gi…...

在centOS服务器安装docker,并使用docker配置nacos
遇到安装慢的情况可以优先选择阿里镜像 安装docker 更新yum版本 yum update安装所需软件包 yum install -y yum-utils device-mapper-persistent-data lvm2添加Docker仓库 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.rep…...
JVM运行时数据区概述以及分别存放的内容
JVM的运行时数据区是JVM在执行Java程序时用于存储数据和状态信息的内存区域。它分为多个部分,每个部分都有其特定的作用和存放的内容。 1. 方法区(Method Area) 作用:方法区是所有线程共享的内存区域,用于存放已被虚…...
数据体系规范化
基础是标准化、规范化 建立数据仓库,面向主题的、集成的、相对稳定的、反映历史变化的数据集合,以支持管理决策decision making 大数据:大量volumn、多样variety、快速velocity、价值密度低value、准确性veracity、可视化visualization、合法性validity 多源数据、多样数…...

从政府工作报告探计算机行业发展
从政府工作报告中,我们可以深入洞察计算机行业在未来一年的发展趋势和政策导向。报告中明确提出了数字经济创新发展的重要性,以及制造业数字化转型、服务业数字化、智慧城市和数字乡村建设等关键任务,这些都为计算机行业提供了广阔的发展空间…...

【软件工具】网络性能测试工具 Iperf
Iperf 是一款专业的开源网络性能测试工具,它被广泛用于测量网络带宽、延迟、抖动和数据包丢失等网络性能指标,支持 TCP 和 UDP 等,可用于点对点或客户端-服务器等模式的网络测试。 软件获取 官方下载地址:https://iperf.fr/iper…...

Day32:安全开发-JavaEE应用Servlet路由技术JDBCMybatis数据库生命周期
目录 JavaEE-HTTP-Servlet&路由&周期 JavaEE-数据库-JDBC&Mybatis&库 思维导图 Java知识点: 功能:数据库操作,文件操作,序列化数据,身份验证,框架开发,第三方库使用等. 框架…...
C语言下使用SQL语言
需头文件:#include<sqlite.h>---需下载 1.sqlite3_open int sqlite3_open( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); 功能: 打开数据库文件(…...
Gitea相关漏洞
Go代码审计:Gitea远程命令执行漏洞链_新闻中心-网盾网络安全培训学校 Vulhub靶场gitea-1.4远程代码执行漏洞复现_gitea 漏洞-CSDN博客...

基于深度学习的图像去雨去雾
基于深度学习的图像去雨去雾 文末附有源码下载地址 b站视频地址: https://www.bilibili.com/video/BV1Jr421p7cT/ 基于深度学习的图像去雨去雾,使用的网络为unet, 网络代码: import torch import torch.nn as nn from torchsumm…...
使用JS的for循环实现九九乘法表
九九乘法表是我们在学习基础数学时经常会遇到的一个概念。在编程中,使用循环结构来实现九九乘法表是一个很好的练习。下面,我将详细介绍如何使用JavaScript的for循环来实现九九乘法表。 首先,我们需要理解for循环的基本结构。在JavaScript中…...

Leetcode 70 爬楼梯
文章目录 1. 题目描述2. 我的尝试 1. 题目描述 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 示例 1: 输入:n 2 输出:2 解释:有两种方法可以爬到…...

基于SpringBoot+MYSQL+Vue的校园管理系统
目录 1、前言介绍 2、主要技术 3、系统流程分析 3.1、操作流程 3.2、添加信息流程 3.3、删除信息流程 4、系统设计 4.1 系统体系结构 4.2开发流程设计 4.3 数据库设计原则 4.4 数据表 5、运行截图(部分) 5.1管理员功能模块 5.2用户功能模块 5.3院校管理员功能模块…...
Oracle P6 负浮时和必须完成日期
前言 学习过计划的人大都有这有这样的经历,即无论是Microsoft Project 亦或是P6见过负浮时那么 Primavera P6 计划中的负浮时是从何而来的呢,那么本文可能会有所帮助。 首先,当活动的最晚日期早于最早日期时,就会出现负浮时。 换…...

【C++】STL--String
这一节主要总结string类的常见接口,以及完成了string类的模拟实现。 目录 标准库的String类 string类常见接口 string类对象的常见构造 string析构函数:~string string类对象的容量操作 string类对象的访问及遍历操作 string类对象的修改操作 s…...
深入理解与使用go之中间件--实现
深入理解与使用go之--中间件实现 目录 引子 中间件 定义 原理 简单实现 多个中间件 优化 扩展 gin框架实现 引子 我们在做web开发的时候,经常会遇到下面一些需求: 统计耗时:想程序内部统计某个路由的请求耗时 预处理:接口需要登录鉴权后才能继续进行 错误捕获:当…...