当前位置: 首页 > news >正文

突破编程_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 简单工厂模式的概念 简单工厂模式&#xff08;Simple Factory Pattern&#xff09;是设计模式中的一种创建型模式。它的主要目的是将对象的实例化与使用解耦&#xff0c;使得客户端无需关心对象的创建细节&#xff0c;只需通过工厂类来获取所需的对象。 在简单工厂模式中&a…...

C语言——快速排序

C语言——快速排序 一、 含义二、算法思想三、实现步骤代码实现 一、 含义 快速排序算法是在几种排序算法中效率最高的一个排序算法了&#xff0c;故称为快速排序&#xff0c;它的时间复杂度为&#xff1a;O(nlog2n)&#xff0c;相比冒泡排序算法的O(n2)有很大的提升。 二、算…...

FP独立站获客秘籍大揭秘:简单高效,一看就会!

跨境电商的大潮中&#xff0c;越来越多的卖家选择跳出第三方平台的框架&#xff0c;拥抱独立站的自由与机遇。但独立站获客难、成本高的问题&#xff0c;也让不少卖家头疼不已。别担心&#xff0c;今天就来给大家揭秘FP独立站获客的简单高效方法&#xff01; 首先&#xff0c;…...

英伟达tx2光驱烧录功能支持

今天得到一个任务&#xff0c;是在当前nvidia tx2平台上使能usb cdrom并且调试烧录功能。首先测试给到的信息是不能在平台上使用&#xff08;废话嘛&#xff0c;能用还用我干嘛&#xff09; 拿到本地ubuntu机器上看了下&#xff0c;使用brasero等软件可以顺利烧录。 此时捕获了…...

关于stm32(CubeMX+HAL库)的掉电检测以及flash读写

1.掉电检测 CubeMX配置 只需使能PVD中断即可 但是使能了PVD中断后还需要自行配置一些PWR寄存器中的参数&#xff0c;我也通过HAL库进行编写 void PVD_config(void) {//配置PWRPWR_PVDTypeDef sConfigPVD; sConfigPVD.PVDLevel PWR_PVDLEVEL_7; …...

Elastic script_score的使用

script_score介绍 在Elasticsearch中&#xff0c;script_score是在function_score查询中的一种功能强大的方式&#xff0c;允许用户使用内置Painless脚本语言或者其他支持的语言来动态计算每个文档的评分 script_score语法 GET /<索引名>/_search {"query":…...

【Spring Boot 3】获取已注入的Bean

【Spring Boot 3】获取已注入的Bean 背景介绍开发环境开发步骤及源码工程目录结构总结 背景 软件开发是一门实践性科学&#xff0c;对大多数人来说&#xff0c;学习一种新技术不是一开始就去深究其原理&#xff0c;而是先从做出一个可工作的DEMO入手。但在我个人学习和工作经历…...

C# 对于点位置的判断

1.判断点是否在一群点内部 要判断一个点是否在一个由多个点围成的多边形内部&#xff08;例如一圈点&#xff09;&#xff0c;可以使用射线法&#xff08;Ray Casting Algorithm&#xff09;来实现。以下是一个简单的 C# 实现示例 using System;public class Point {public d…...

最新CLion + STM32 + CubeMX 开发环境搭建

网上有不少相关教程&#xff0c;但都是基于老版本Clion&#xff0c;新版有一些改变&#xff0c;但整体是简单了。 PS&#xff1a;本教程基于CLion 2023.3.4 安装所需工具参考&#xff1a;Clion搭建stm32开发环境&#xff08;STM32F103C8T6&#xff09;&#xff0c;有这一篇就够…...

【Python3】观察者模式

观察者模式&#xff08;Observer Pattern&#xff09;是一种常见的设计模式&#xff0c;用于定义对象之间的一对多依赖关系&#xff0c;使得一个对象的状态改变能够通知所有依赖于它的对象并自动更新。 在观察者模式中&#xff0c;有两个核心角色&#xff1a; Subject&#xf…...

HTML5 Web Worker之性能优化

描述 由于 JavaScript 是单线程的&#xff0c;当执行比较耗时的任务时&#xff0c;就会阻塞主线程并导致页面无法响应&#xff0c;这就是 Web Workers 发挥作用的地方。它允许在一个单独的线程&#xff08;称为工作线程&#xff09;中执行耗时的任务。这使得 JavaScript 代码可…...

应对恶意IP攻击的有效方法

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

如何使用“Docker registry创建本地仓库,在服务器之间进行文件push和pull”?

1.1、在服务器1&#xff0c;运行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 文件&#xff0c; 192.168.xxx.xxx 换成你自己 registry 服务的地址 sudo na…...

Rocky Linux - Primavera P6 EPPM 安装及分享

引言 继上一期发布的Redhat Linux版环境发布之后&#xff0c;近日我又制作了基于Rocky Enterprise Linux 的P6虚拟机环境&#xff0c;同样里面包含了全套P6 最新版应用服务 此虚拟机仅用于演示、培训和测试目的。如您在生产环境中使用此虚拟机&#xff0c;请先与Oracle Primav…...

API 管理调研

当前大部分团队内 API 管理都是依赖 Postman&#xff0c;postman最大的问题是共享问题&#xff0c;如果我要使用另外一个人已经调试好的 API 非常麻烦。因此&#xff0c;能实现协作的 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程序时用于存储数据和状态信息的内存区域。它分为多个部分&#xff0c;每个部分都有其特定的作用和存放的内容。 1. 方法区&#xff08;Method Area&#xff09; 作用&#xff1a;方法区是所有线程共享的内存区域&#xff0c;用于存放已被虚…...

数据体系规范化

基础是标准化、规范化 建立数据仓库,面向主题的、集成的、相对稳定的、反映历史变化的数据集合,以支持管理决策decision making 大数据:大量volumn、多样variety、快速velocity、价值密度低value、准确性veracity、可视化visualization、合法性validity 多源数据、多样数…...

从政府工作报告探计算机行业发展

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

【软件工具】网络性能测试工具 Iperf

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

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

测试markdown--肇兴

day1&#xff1a; 1、去程&#xff1a;7:04 --11:32高铁 高铁右转上售票大厅2楼&#xff0c;穿过候车厅下一楼&#xff0c;上大巴车 &#xffe5;10/人 **2、到达&#xff1a;**12点多到达寨子&#xff0c;买门票&#xff0c;美团/抖音&#xff1a;&#xffe5;78人 3、中饭&a…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

MySQL用户和授权

开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务&#xff1a; test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

什么是Ansible Jinja2

理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具&#xff0c;可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板&#xff0c;允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板&#xff0c;并通…...

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性&#xff1a; 隐藏字段的实现细节 提供对字段的受控访问 访问控制&#xff1a; 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性&#xff1a; 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑&#xff1a; 可以…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...