突破编程_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)不易于使用继承等面向对象特性: 由于简单工厂模式通常直接返回具体产品类的实例,而不是通过继承等面向对象特性来创建对象,因此可能无法充分利用面向对象编程的优势。

需要注意的是,简单工厂模式是一种相对简单的工厂模式,适用于产品类型较少且不太可能经常变动的场景。在更复杂的场景中,可能需要考虑使用抽象工厂模式来应对更多的变化和扩展需求。

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

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

相关文章

线程安全--浅谈Ad-hoc与加锁的区别

浅谈Ad-hoc 与加锁 两者要解决的都是对对象的语义混乱操作&#xff0c;即有个count进行累加操作。 我的理解/文心一言的反馈如下: 加锁是保证我们对同一个count在多线程下的访问有序&#xff0c;即“读写-修改-写入”具有原子性。 而Ad-hoc机制就是通过程序员自己定义一个私有…

【Java】生成条形码工具类

报销单需要根据单号生成条形码 先看效果图 直接上代码&#xff0c;复制即可使用 /*** Description:生成条形码*/ public class BarCodeUtils {/*** 默认图片宽度*/private static final int DEFAULT_PICTURE_WIDTH 300;/*** 默认图片高度*/private static final int DEFAULT_…

一起来读李清照

当然先祝各位女生节日快乐&#x1f381;&#x1f381;啦​。​ 但是呢&#xff0c;今天&#xff0c;我们不聊技术&#xff0c;来聊点其他的。 大家都知道今天是三八妇女节&#xff0c;三八妇女节的是中国人的叫法&#xff0c;也叫国际妇女节。是为了纪念妇女权利的运动&#…

基于禁忌搜索算法(TS)的TSP(Python实现)

本篇文章是博主在最化优学习、人工智能等领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对相关等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅解。文章分类在最优化算…

springboot248校园资产管理

校园资产管理 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本校园资产管理就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大…

CDN(内容分发网络):加速网站加载与优化用户体验

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

java——2024-03-03

String类的对象能被修改吗&#xff1f;如果不能需要用什么修改&#xff1f;StringBuilder和StringBuffer的区别&#xff1f;equals和区别谈谈对面向对象的理解重载和重写的区别说一下ArrayList&#xff0c;LinkedList底层实现以及区别什么是哈希冲突&#xff1f;hashMap和conCu…

mysql 8.0 日志文件无权限问题处理

无论如何修改权限总是报这个日志文件权限问题。 解决方法 输入指令&#xff1a; setenforce 0 systemctl restart mysgld

LVS集群(Linux Virtual server)介绍----及LVS的NAT模式部署(一)

群集的含义 ●Cluster&#xff0c;集群、群集由多台主机构成&#xff0c;但对外只表现为一个整体&#xff0c;只提供访问入口(域名或IP地址)&#xff0c;相当于一台大型计算机 问题&#xff1a; 互联网应用中&#xff0c;随着站点对硬件性能、响应速度、服务稳定性、数据可靠…

【Spring Cloud 2023】【ZooKeeper】服务注册与发现

【Spring Cloud 2023】【ZooKeeper】服务注册与发现 背景介绍开发环境开发步骤及源码一、创建提供服务的应用二、创建消费服务的应用工程目录结构总结背景 软件开发是一门实践性科学,对大多数人来说,学习一种新技术不是一开始就去深究其原理,而是先从做出一个可工作的DEMO入…

Java开发从入门到精通(一):JavaJava的基础语法知识高阶

Java大数据开发和安全开发 &#xff08;一&#xff09;Java的数组1.1 静态初始化数组1.1.1 静态数组的定义1.1.2 数组的原理1.1.3 数组的访问1.1.3.1 数组的取值1.1.3.2 数组的赋值1.1.3.3 数组的长度1.1.3.4 数组的使用 1.1.4 数组的遍历1.1.3 数组的案例 1.2 动态初始化数组1…

WanAndroid(鸿蒙版)开发的第一篇

前言 DevEco Studio版本&#xff1a;4.0.0.600 WanAndroid的API链接&#xff1a;玩Android 开放API-玩Android - wanandroid.com 为了兼容HarmonyOS&#xff0c;我这边以Arkts--API9为例进行实现 通过华为官网渠道目前下载的版本还是3.1的&#xff0c;这边提供下4.0版本下载…

实用 Tips 分享|用 File Browser 上传大文件

在使用 OpenBayes 平台过程中&#xff0c;很多用户都会遇到上传大文件 (&#xff1e;100 G) 的情况&#xff0c;无论是上传至数据仓库&#xff0c;还是在工作空间中完成上传&#xff0c;过大的文件在传输过程中常会出现上传中断、速度过慢等情况。 针对这一问题&#xff0c;我…

从2个角度来简单讨论一下伦敦金走势图怎么看

进入伦敦金市场之后&#xff0c;投资者无时无刻都在思考着一个问题&#xff0c;那就是伦敦金走势怎么看&#xff1f;关于这个问题&#xff0c;其实在市场中有很多的文章和视频去介绍&#xff0c;在书店里也有很多投资前贤所写的书籍讨论过这个问题。但是他们都有一个特征&#…

python中[[“jfk“,“kul“],[“jfk“,“nrt“]]是如何进行sort()排序的

在Python中&#xff0c;列表&#xff08;list&#xff09;的sort()方法默认按照元素的字典顺序进行排序。对于包含字符串的列表&#xff0c;它会按照字符串的字典顺序&#xff08;即字母顺序&#xff09;进行排序。 然而&#xff0c;对于嵌套列表&#xff08;即列表中的元素也…

【SpringMVC】快速体验 SpringMVC接收数据 第一期

文章目录 一、SpringMVC 介绍1.1 主要作用1.2 核心组件和调用流程理解 二、快速体验三、SpringMVC接收数据3.1 访问路径设置3.1.1 精准路径匹配3.1.2 模糊路径匹配3.1.3 类和方法级别区别3.1.4 附带请求方式限制3.1.5 进阶注解 与 常见配置问题 3.2 接收参数&#xff08;重点&a…

linux kernel物理内存概述(七)

目录 一、内核中小内存、频繁分配和释放场景 二、slab是内存池化技术 三、内核中使用slab对象池的地方 四、slab内核设计 使用比页小的内存&#xff0c;内核的处理方式使用slab 一、内核中小内存、频繁分配和释放场景 slab首先会向伙伴系统一次性申请一个或者多个物理内存…

医学大数据|文献阅读|有关“肠癌+机器学习”的研究记录

目录 1.机器学习算法识别结直肠癌中的免疫相关lncRNA signature 2.基于机器学习的糖酵解相关分子分类揭示了结直肠癌癌症患者预后、TME和免疫疗法的差异&#xff0c;2区7 3.整合深度学习-病理组学、放射组学和免疫评分预测结直肠癌肺转移患者术后结局 4.最新7.4分纯生信&am…

二叉树的前、中、后序遍历

leetcode144. 二叉树的前序遍历 leetcode94. 二叉树的中序遍历 leetcode145. 二叉树的后序遍历 思路 这里前中后序遍历&#xff0c;其实指的就是中间节点的遍历顺序 前序遍历&#xff1a;中左右 中序遍历&#xff1a;左中右 后序遍历&#xff1a;左右中 self.result.append(r…

复杂业务场景下,如何优雅的使用设计模式来优化代码?

1、引言 本文以一个实际案例来介绍在解决业务需求的路上&#xff0c;如何通过常用的设计模式来逐级优化我们的代码&#xff0c;以把我们所了解的到设计模式真实的应用于实战。 2、背景 假定我们现在有一个订单流程管理系统&#xff0c;这个系统对于用户发起的一笔订单&#…