设计模式之工厂模式(C++)

news2025/2/27 17:31:07

作者:翟天保Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

一、工厂模式是什么?

       工厂模式是一种创建型的软件设计模式。定义一个用于创建对象的工厂接口,并让工厂子类决定实例化哪一个产品类,使产品类的实例化延迟到工厂子类中执行。说白了就是用来造东西的,一般是比较简单的东西,我们不需要知道它如何生产的,直接从工厂拿到产品即可。

       工厂模式的优点:

  1. 良好的封装性。将产品的实例化封装执行,避免被修改,这样的产品具备良好的一致性。
  2. 良好的扩展性。增加产品时,同步增加一个工厂子类,不会违反开闭原则。
  3. 标准的解耦合框架。使用者只需要知道自己要什么产品即可,不用去管产品具体的特性等等,降低了模块间的耦合。

      工厂模式的缺点:

  1. 代码量大。每加一个产品,都要加一个工厂子类,代码会显得臃肿。
  2. 不利于扩展复杂的产品结构。如果你要苹果、香蕉、梨,工厂模式的结构还可以,但如果你要山东的苹果、海南的香蕉、北京的苹果,就显得结构呆呆的。这可以用抽象工厂模式解决,对产品族和产品种类进行区分。

二、简单工厂模式

       在介绍工厂模式前,先介绍其前身-简单工厂模式,简单工厂模式是用一个简单的工厂类,直接对产品进行实例化,虽然大大减少了代码量,但是违反了设计中的开闭原则,因为每次添加产品,工厂类都要进行修改。

2.1 结构图

       客户端即Main主函数,调用简单工厂制造产品,并获取产品。具体要什么产品由客户端命令决定。

 2.2 代码示例

       场景描述:我联系了一个生产水果的工厂,从工厂拿了一个苹果、一个香蕉和一个梨,用来果腹,工厂给我一个报价我付钱即可。

//Prodect.h
/****************************************************/
#pragma once
#include <iostream>

using namespace std;

// 产品种类
enum PRODECT_TYPE
{
	APPLE,                     // 苹果
	BANANA,					   // 香蕉
	PEAR					   // 梨
};

// 抽象产品类
class Prodect
{
public:
	// 构造函数
	Prodect(int price) :m_price(price) {};
	// 析构函数
	virtual ~Prodect() {};
	// 获取价格
	int getPrice() {
		return m_price;
	}
protected:
	// 产品价格
	int m_price;
};

// 具体产品类-苹果
class AppleProdect : public Prodect
{
public:
	// 构造函数
	AppleProdect(int price) :Prodect(price) {
		cout << "获得了一个苹果。" << endl;
	};
	// 析构函数
	virtual ~AppleProdect() {
		cout << "吃掉了一个苹果。" << endl;
	};
};

// 具体产品类-香蕉
class BananaProdect : public Prodect
{
public:
	// 构造函数
	BananaProdect(int price) :Prodect(price) {
		cout << "获得了一个香蕉。" << endl;
	};
	// 析构函数
	virtual ~BananaProdect() {
		cout << "吃掉了一个香蕉。" << endl;
	};
};

// 具体产品类-梨
class PearProdect : public Prodect
{
public:
	// 构造函数
	PearProdect(int price) :Prodect(price) {
		cout << "获得了一个梨。" << endl;
	};
	// 析构函数
	virtual ~PearProdect() {
		cout << "吃掉了一个梨。" << endl;
	};
};

//Factory.h
/****************************************************/
#pragma once
#include <iostream>
#include "Prodect.h"

using namespace std;

// 简单工厂
class SimpleFactory
{
public:
	// 获取产品
	Prodect* getProdect(PRODECT_TYPE type) {
		Prodect* prodect = nullptr;
		switch (type)
		{
		case APPLE:
			prodect = new AppleProdect(5);
			break;
		case BANANA:
			prodect = new BananaProdect(2);
			break;
		case PEAR:
			prodect = new PearProdect(3);
			break;
		default:
			cout << "无该产品。" << endl;
			break;
		}
		return prodect;
	}
};

//main.cpp
/****************************************************/
#include <iostream>
#include "Factory.h"
#include "Prodect.h"

using namespace std;

int main()
{
	SimpleFactory* factory = new SimpleFactory();
	cout << "开始生产。" << endl;
	Prodect *A = factory->getProdect(APPLE);
	Prodect *B = factory->getProdect(BANANA);
	Prodect *C = factory->getProdect(PEAR);
	int applePrice = A->getPrice();
	int bananaPrice = B->getPrice();
	int pearPrice = C->getPrice();
	int sum = A->getPrice() + B->getPrice() + C->getPrice();
	cout << "苹果价格:" << applePrice << "元。" << endl;
	cout << "香蕉价格:" << bananaPrice << "元。" << endl;
	cout << "梨子价格:" << pearPrice << "元。" << endl;
	cout << "累计消费:" << sum << "元。" << endl;
	delete A;
	delete B;
	delete C;
	cout << "享用完毕。" << endl;
	return 0;
}

       程序结果如下。

        在上述示例中,我们可以看到,如果我想给工厂再添加一个产品,那么除了添加一个产品子类外,还要跑到简单工厂的类中进行switch的扩展,这样不利于代码的封装,破坏了开闭原则。而工厂模式的设计能弥补该不足。

三、工厂模式

3.1 结构图

       客户端即Main主函数,通过工厂子类来制造对应的产品,并获取产品。具体要什么产品由工厂子类决定。

 3.2 代码示例

       场景描述:我联系了一个生产水果的工厂,从工厂拿了一个苹果、一个香蕉和一个梨,用来果腹,工厂给我一个报价我付钱即可。

//Prodect.h
/****************************************************/
#pragma once
#include <iostream>

using namespace std;

// 抽象产品类
class Prodect
{
public:
	// 构造函数
	Prodect(int price) :m_price(price) {};
	// 析构函数
	virtual ~Prodect() {};
	// 获取价格
	int getPrice() {
		return m_price;
	}
protected:
	// 产品价格
	int m_price;
};

// 具体产品类-苹果
class AppleProdect : public Prodect
{
public:
	// 构造函数
	AppleProdect(int price) :Prodect(price) {
		cout << "获得了一个苹果。" << endl;
	};
	// 析构函数
	virtual ~AppleProdect() {
		cout << "吃掉了一个苹果。" << endl;
	};
};

// 具体产品类-香蕉
class BananaProdect : public Prodect
{
public:
	// 构造函数
	BananaProdect(int price) :Prodect(price) {
		cout << "获得了一个香蕉。" << endl;
	};
	// 析构函数
	virtual ~BananaProdect() {
		cout << "吃掉了一个香蕉。" << endl;
	};
};

// 具体产品类-梨
class PearProdect : public Prodect
{
public:
	// 构造函数
	PearProdect(int price) :Prodect(price) {
		cout << "获得了一个梨。" << endl;
	};
	// 析构函数
	virtual ~PearProdect() {
		cout << "吃掉了一个梨。" << endl;
	};
};

//Factory.h
/****************************************************/
#pragma once
#include <iostream>
#include "Prodect.h"

using namespace std;

// 抽象工厂类
class Factory
{
public:
	// 获取产品
	virtual Prodect* getProdect() = 0;
};

// 具体工厂类-苹果
class AppleFactory : public Factory
{
public:
	// 获取产品
	virtual Prodect* getProdect() {
		Prodect* prodect = new AppleProdect(5);
		return prodect;
	}
};

// 具体工厂类-香蕉
class BananaFactory : public Factory
{
public:
	// 获取产品
	virtual Prodect* getProdect() {
		Prodect* prodect = new BananaProdect(2);
		return prodect;
	}
};

// 具体工厂类-梨
class PearFactory : public Factory
{
public:
	// 获取产品
	virtual Prodect* getProdect() {
		Prodect* prodect = new PearProdect(3);
		return prodect;
	}
};

//main.cpp
/****************************************************/
#include <iostream>
#include "Factory.h"
#include "Prodect.h"

using namespace std;

int main()
{
	Factory* factoryA = new AppleFactory();
	Factory* factoryB = new BananaFactory();
	Factory* factoryC = new PearFactory();
	cout << "开始生产。" << endl;
	Prodect *A = factoryA->getProdect();
	Prodect *B = factoryB->getProdect();
	Prodect *C = factoryC->getProdect();
	int applePrice = A->getPrice();
	int bananaPrice = B->getPrice();
	int pearPrice = C->getPrice();
	int sum = A->getPrice() + B->getPrice() + C->getPrice();
	cout << "苹果价格:" << applePrice << "元。" << endl;
	cout << "香蕉价格:" << bananaPrice << "元。" << endl;
	cout << "梨子价格:" << pearPrice << "元。" << endl;
	cout << "累计消费:" << sum << "元。" << endl;
	delete A;
	delete B;
	delete C;
	cout << "享用完毕。" << endl;
	return 0;
}

       程序结果如下。

        这样设计的好处就是如果多一个产品,那就只需要在产品类和工厂类中各扩展一个子类即可,不影响原有的程序。

四、总结

       我尽可能用较通俗的话语和直观的代码例程,来表述我对工厂模式的理解,或许有考虑不周到的地方,如果你有不同看法欢迎评论区交流!希望我举的例子能帮助你更好地理解工厂模式。

       如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!

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

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

相关文章

MybatisPlus------application文件配置新增SQL打印以及测试类编写(二)

MybatisPlus------application文件配置新增SQL语句打印以及测试类编写 增加日志打印&#xff0c;具体打印执行的SQL语句。 操作&#xff1a; 只需在application配置文件中增加如下配置&#xff1a; mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout…

快递员配送手机卡,要求当面激活有“猫腻”吗?

咨询&#xff1a;快递员配送手机卡&#xff0c;要求当面激活有“猫腻”吗&#xff1f;有些朋友可能在网上看到了一些关于快递小哥激活会采集信息的文章&#xff0c;所以觉得让快递小哥激活流量卡并不安全&#xff0c;其实&#xff0c;哪有这么多的套路&#xff0c;只要你自己在…

一文看懂Java语言与Java生态圈

Java语言与Java生态圈 1、Oracle JDK与Open JDK之间的关系 Oracle JDK Java最早是由SUN公司发明&#xff0c;Oracle JDK之前叫SUN JDK&#xff0c;显而易见&#xff0c;这是在2009年Oracle收购SUN公司之前&#xff0c;收购之后被名为Oracle JDK&#xff0c;实际上&#xff0…

【图文教程】笔记本总是自动关机怎么办?win11自动关机问题解决方案

使用场景及环境&#xff1a; 日常使用&#xff0c;代码、文档使用。 系统&#xff1a;win11、win10、win8、win7笔记本都可通用 笔记本&#xff1a;联想ThinkPad E450 笔记本相关性能参数 设备名称 XXX 处理器 Intel Core™ i3-8145U CPU 2.10GHz 2.30 GHz 机带 RAM 8.00 GB (…

JavaEE——MyBatis的简单介绍和使用

MyBatis是什么 MyBatis是一个支持普通SQL查询&#xff0c;存储过程以及高级映射的持久层框架&#xff0c;他消除了几乎所有的JDBC代码和参数的手动设置以及对结果集的检索&#xff0c;使用简单的XML或注解进行配置和原始映射&#xff0c;将接口和Java的POJO映射成数据库中的记…

StarUML画流程图

1. 前言 工作中&#xff0c;在写文档总结时&#xff0c;对于需求的理解总有一个重要的环节&#xff0c;那就是画流程图&#xff0c;Ubuntu下我推荐使用StartUML 来画&#xff0c;嘎嘎好用&#xff0c;如果不知道这个工具&#xff0c;请参考笔者这篇文章下载安装&#xff1a; U…

关于springboot读取配置类,使用@Autowired自动注入为null的问题

问题 之前写了一个配置类&#xff0c;读取config.yml配置文件里写好的阿里云oss的一些参数配置。配置类读取并无问题&#xff0c;但是在另一个普通类里自动注入该配置类时&#xff0c;引用配置类属性却报NullPointer异常。然后终于发现问题原因了。 代码 1. 配置文件 appli…

GeoTools:Shapefile创建

在上一篇文章《GeoTools&#xff1a;Feature&Shapefile之CRUD操作》中&#xff0c;介绍了基于GeoTools的Shapefile文件CRUD基本操作&#xff0c;那么&#xff0c;能否使用GeoTools创建Shapefile文件呢&#xff1f;答案是可以的。以下&#xff0c;我们将深入讨论如何实现。 …

零基础机器学习做游戏辅助第十二课--原神自动钓鱼(二)

一、模拟训练环境 上节课我们已经能够判断人物的钓鱼状态,接下来我们就需要对鱼儿上钩后的那个受力框进行DQN训练。 方案有两个: 使用卷积神经网络直接输入图像对网络进行训练。使用普通网络,自己写代码模拟出图像中三个点的动态并把值给神经网络进行训练。这里我们选用第二…

IB数学课程有哪些异同?如何选课?

自从IBDP在2019年进行数学课程改革后&#xff0c;许多IB小伙伴们就不知该如何选课了……改革后的IB数学课程包括4门课程&#xff0c;也就是&#xff1a;● Mathematics: analysis and approaches SL & HL (first assessment 2021)● Mathematics: applications and interpr…

科技云报道:“吞金兽”ChatGPT背后:AI算力告急!

科技云报道原创。 近两个月来&#xff0c;全世界的网友们都在兴致勃勃的“调教”ChatGPT&#xff0c;但第一个受不了的却是ChatGPT的所有者。 为了更长远的发展&#xff0c;OpenAI宣布了付费订阅版ChatGPT Plus&#xff0c;每月收费20美元。 虽然OpenAI表示&#xff0c;将持…

Monorepo or 物料市场?结合工作实际情况对公司现有前端体系的思考

前言 去年年中基于若依vue前端框架进行了改造&#xff0c;加上后端的配合&#xff0c;我写了一套脚手架和项目中后台模板。中后台模板中包含了许多基础代码&#xff0c;比如登录/注册、路由、权限等等相关功能。这个中后台模板是基于我们实际开发定制的&#xff0c;所以跟通用…

SpringBoot——配置文件

项目中有许多公共使用的变量&#xff0c;例如端口号&#xff0c;连接数据库的配置&#xff0c;还有我们自己创建的变量&#xff0c;这些可以放到SpringBoot的配置文件中统一调配使用 properties 基本语法格式&#xff1a; keyvalue例如配置项目的端口号为8888&#xff1a; …

Bean注入到Spring方式

扒一扒Bean注入到Spring的那些姿势 配置文件的方式就是以外部化的配置方式来声明Spring Bean&#xff0c;在Spring容器启动时指定配置文件。配置文件方式现在用的不多了&#xff0c;但是为了文章的完整性和连续性&#xff0c;这里我还是列出来了&#xff0c;知道的小伙伴可以自…

Jetson Xavier NX设备将opencv和tensorrt链接到conda环境

注意安装的时候先查看设备旧版本的opencv&#xff0c;卸载干净后再装。 Jetpack4.6 opencv4.1.1 conda安装 过程翻一下之前的博客把&#xff0c;下面是创建环境开始 conda create -n py36 python3.6.9 OPENCV_EXTRA_MODULES_PATH/home/ta/open/opencv_contrib-4.1.1/modul…

白银走势图如何做空?

做现货白银的好处是&#xff0c;我们可以在白银走势图上做空&#xff0c;不再像股票那样只能先卖后买&#xff0c;还能先卖后买&#xff0c;这样我们做投资就多了一份从容&#xff01;任何时候我们都能获得投资获利的机会&#xff0c;但是由于习惯了单向交易&#xff0c;我们要…

Sarsa算法讲解及实现

Sarsa算法讲解及实现 1. Q表格 我们使用表格来存储每一个状态 state, 和在这个 state 每个行为 action 所拥有的 Q 值。 Q即为Q&#xff08;s,a&#xff09;就是在某一时刻的 s 状态下(s∈S)&#xff0c;采取动作a (a∈A)动作能够获得收益的期望&#xff0c;环境会根据agent…

java嵌入式持久化消息队列SMQ,改造自FQueue

一、说明之前项目中一直使用ConcurrentLinkedQueue做为缓冲队列&#xff08;主要是单个项目内&#xff0c;单条改批量的场景&#xff0c;多个项目间使用的是rocketmq&#xff09;&#xff0c;虽然用着方便但是是纯内存的&#xff0c;如果项目发生异常崩溃内存队列中的数据就会全…

JavaSE学习day6 进制转换和idea的调试

1.进制 1.1 常见的进制分类(掌握) 学过计算机组成原理的同学可以跳过这里。 二进制 十进制 八进制 十六进制 1.2 二进制 计算机数据在底层存储和运算的时候&#xff0c;都是以二进制的形式操作的&#xff0c;了解不同的进制&#xff0c;便于我们对数据的运算过程理解的更…

个人博客推出了更多功能

背景 Web2.0的典型代表博客&#xff0c;吸引着粉丝们打造属于自己的个人博客&#xff0c;分享自己的学习经验&#xff0c;记录自己的日常生活。随着大厂的入局&#xff0c;我们可以很容易的申请自己的个人博客&#xff0c;但是弊端就是往往会被他们控制&#xff0c;甚至封号。…