C++设计模式创建型之工厂模式整理
一、工厂模式分类
工厂模式属于创建型模式,一般可以细分为简单工厂模式、工厂模式和抽象工厂模式。每种都有不同的特色和应用场景。
二、工厂模式详情
1、简单工厂模式
1)概述
简单工厂模式相对来说,在四人组写的《设计模式------可复用面向对象软件的基础》中并没有提及,所以可以认为这并不算是一个标准的设计模式,但因为其场景较多,所以这里简单介绍下。有的人认为可以看做一种编程收发或者编程技巧。
2)简单工厂模式代码
class Monster
{
public:
Monster(int life, int magic, int attack):m_life(life),m_magic(magic),m_attack(attack){}
virtual ~Monster(){}
protected:
int m_life;
int m_magic;
int m_attack;
};
class M_Undead: public Monster
{
public:
M_Undead(int life, int magic, int attack):Monster(life, magic, attack)
{
cout << "一只亡灵类怪物诞生在这个世界上了" << endl;
};
};
class M_Element: public Monster
{
public:
M_Element(int life, int magic, int attack):Monster(life, magic, attack)
{
cout << "一只元素类怪物诞生在这个世界上了" << endl;
};
};
class M_Mechanic: public Monster
{
public:
M_Mechanic(int life, int magic, int attack):Monster(life, magic, attack)
{
cout << "一只机械类怪物诞生在这个世界上了" << endl;
};
};
class CSimpleFactory
{
public:
Monster* createMonster(eMonsterType eType)
{
Monster* pCreateObj = nullptr;
if (eType == Undead_Type)
{
pCreateObj = new M_Undead(300, 50, 80);
}
else if (eType == Element_Type)
{
pCreateObj = new M_Element(200, 80, 100);
}
else if (eType == Mechanic_Type)
{
pCreateObj = new M_Mechanic(400, 0, 110);
}
return pCreateObj;
}
};
int main()
{
CSimpleFactory simobj;
Monster* pM1 = simobj.createMonster(Undead_Type);
Monster* pM2 = simobj.createMonster(Element_Type);
Monster* pM3 = simobj.createMonster(Mechanic_Type);
delete pM1;
delete pM2;
delete pM3;
return 0;
}
总结:通过工厂类来创建怪物,则意味着创建怪物时不再使用new关键字,而是通过该工厂类来进行,这样的话,即便将来拐歪种类增加,main主函数中创建怪物的代码也可以尽量保持稳定。同时也避免了在main函数中直接使用new创建对象时必须知道具体类名的情形发生,实现了创建怪物的代码与各个具体怪物类对象要实现的业务逻辑的代码隔离。当然此模式也具有明显的缺点,当引入新的怪物类型时,需要修改createMonster成员函数的源码来增加if判断分支,从而支持对新类型怪物的创建工作,这就违反了开放封闭原则。如果if分支不是很多,只有数个而并不是数十上百个,那适当违反开闭原则也可以接收的。
2、工厂模式
1)概述
工厂模式通常也是指得是工厂方法模式,换句话说,工厂方法模式可以简称工厂模式或多态工厂模式,此种模式实现难度比简单工厂模式略高一些。需要针对每种类型的怪物都需要创建一个对应的工厂类,它通过增加新的工厂类来符合面向对象程序设计的开闭原则,但付出的代价是需要增加多个新的工厂类。
2)代码示例
class M_ParFactory
{
public:
virtual Monster* createMonster() = 0;
virtual ~M_ParFactory(){}
};
class M_UndeadFactory : public M_ParFactory
{
public:
virtual Monster* createMonster()
{
return new M_Undead(300, 50, 80);
}
};
class M_ElementFactory : public M_ParFactory
{
public:
virtual Monster* createMonster()
{
return new M_Element(200, 80, 100);
}
};
class M_MechanicFactory : public M_ParFactory
{
public:
virtual Monster* createMonster()
{
return new M_Mechanic(400, 0, 110);
}
};
Monster* Gbl_CreateMonster(M_ParFactory *factoryObj)
{
return factoryObj->createMonster();
}
int main()
{
M_ParFactory* p_ud_fy = new M_UndeadFactory();
Monster* pM1 = Gbl_CreateMonster(p_ud_fy); //构造一只亡灵类怪物
M_ParFactory* p_ele_fy = new M_ElementFactory();
Monster* pM2 = Gbl_CreateMonster(p_ele_fy); //构造一只元素类怪物
M_ParFactory* p_mec_fy = new M_MechanicFactory();
Monster* pM3 = Gbl_CreateMonster(p_mec_fy); //构造一只机械类怪物
//释放资源
delete p_ud_fy;
delete p_ele_fy;
delete p_mec_fy;
delete pM1;
delete pM2;
delete pM3;
}
总结:从上述代码中可以看到,创建怪物对象时,不需要记住具体怪物类的名称,但需要知道创建该类怪物的工厂名称。工厂模式的实现意图是定义一个用于创建对象的接口,但由子类决定要实例化的类是哪一个,该模式是的某个类的实例化延迟到子类。它当出现一个新怪物类型时,既不需要更改Gbl_CreateMonster函数,也不需要像简单工厂模式那样修改MonsterFactory类中的createMonster成员函数来增加新的if分支。
3、抽象工厂模式
1)概述
随着业务的变化,游戏中的战斗场景数量和类型不断增加,从原来的在城镇中战斗逐步进入在沼泽战斗、在山脉地区战斗等,于是场景将重新分类,怪物分为3类,战斗场景3类,这样会产生9类怪物。如果一个工厂子类能够生产不止一种具有相同规则的怪物对象,那么就可以有效地减少所创建的工厂子类数量,这就是抽象工厂模式的核心思想。抽象工厂模式是按照产品族来生产产品,一个地点有一个工厂,该工厂负责生产本地的所有产品。
2)代码示例
//沼泽类怪物
class M_Undead_Swamp : public Monster
{
public:
M_Undead_Swamp(int life, int magic, int attack):Monster(life, magic, attack)
{
cout << "一只沼泽的亡灵类怪物诞生在这个世界上了" << endl;
};
};
class M_Element_Swamp : public Monster
{
public:
M_Element_Swamp(int life, int magic, int attack):Monster(life, magic, attack)
{
cout << "一只沼泽的元素类怪物诞生在这个世界上了" << endl;
};
};
class M_Mechanic_Swamp : public Monster
{
public:
M_Mechanic_Swamp(int life, int magic, int attack):Monster(life, magic, attack)
{
cout << "一只沼泽的机械类怪物诞生在这个世界上了" << endl;
};
};
//------------------------------------------------------------------------------
//山脉类类怪物
class M_Undead_Mountain : public Monster
{
public:
M_Undead_Mountain(int life, int magic, int attack):Monster(life, magic, attack)
{
cout << "一只山脉的亡灵类怪物诞生在这个世界上了" << endl;
};
};
class M_Element_Mountain : public Monster
{
public:
M_Element_Mountain(int life, int magic, int attack):Monster(life, magic, attack)
{
cout << "一只山脉的元素类怪物诞生在这个世界上了" << endl;
};
};
class M_Mechanic_Mountain : public Monster
{
public:
M_Mechanic_Mountain(int life, int magic, int attack):Monster(life, magic, attack)
{
cout << "一只山脉的机械类怪物诞生在这个世界上了" << endl;
};
};
//------------------------------------------------------------------------------
//城镇类怪物
class M_Undead_Town : public Monster
{
public:
M_Undead_Town(int life, int magic, int attack):Monster(life, magic, attack)
{
cout << "一只城镇的亡灵类怪物诞生在这个世界上了" << endl;
};
};
class M_Element_Town : public Monster
{
public:
M_Element_Town(int life, int magic, int attack):Monster(life, magic, attack)
{
cout << "一只城镇的元素类怪物诞生在这个世界上了" << endl;
};
};
class M_Mechanic_Town : public Monster
{
public:
M_Mechanic_Town(int life, int magic, int attack):Monster(life, magic, attack)
{
cout << "一只城镇的机械类怪物诞生在这个世界上了" << endl;
};
};
class M_ParFactory
{
public:
virtual Monster* createMonster_Undead() = 0;
virtual Monster* createMonster_Element() = 0;
virtual Monster* createMonster_Mechanic() = 0;
virtual ~M_ParFactory(){}
};
//工厂子类:
//-------------------------------------------------------------------------------------
//沼泽地区工厂
class M_Factory_Swamp : public M_ParFactory
{
public:
virtual Monster* createMonster_Undead()
{
return new M_Undead_Swamp(300, 50, 120);
}
virtual Monster* createMonster_Element()
{
return new M_Element_Swamp(200, 80, 110);
}
virtual Monster* createMonster_Mechanic()
{
return new M_Mechanic_Swamp(400, 0, 90);
}
};
//-------------------------------------------------------------------------------------
//山脉地区工厂
class M_Factory_Mountain : public M_ParFactory
{
public:
virtual Monster* createMonster_Undead()
{
return new M_Undead_Mountain(300, 50, 80);
}
virtual Monster* createMonster_Element()
{
return new M_Element_Mountain(200, 80, 100);
}
virtual Monster* createMonster_Mechanic()
{
return new M_Mechanic_Mountain(600, 0, 110);
}
};
//-------------------------------------------------------------------------------------
//城镇地区工厂
class M_Factory_Town : public M_ParFactory
{
public:
virtual Monster* createMonster_Undead()
{
return new M_Undead_Town(300, 50, 80);
}
virtual Monster* createMonster_Element()
{
return new M_Element_Town(200, 80, 100);
}
virtual Monster* createMonster_Mechanic()
{
return new M_Mechanic_Town(400, 0, 110);
}
};
总结:抽象工厂模式通过增加新代码不是修改原有代码来为游戏增加新功能。如果需要需要产品族,还需要修改工厂父类来增加新的虚函数以支持新类型,各个工厂有需要增加对新类型的支持,那么这种情况下破坏了开闭原则,同时也不适用抽象工厂模式。抽象工厂模式具备工厂模式的优点,如果只是增加新的产品族,则只需要增加新的子工厂类,符合开闭原则,这是抽象工厂模式优点,但是如果增加新的产品等级结构,那么就需要修改抽象层的代码,这是抽象工厂模式的缺点,因此应该避免在产品等级结构不稳定的情况下使用该模式。引入抽象工厂模式实现意图是:提供一个接口让该接口负责创建一系列相关或者相互依赖的对象,而无需制定它们的具体类。
4、三个模式间对比
1)从代码实现复杂度上,简单工厂模式最简单,工厂方法模式次之,抽象工厂模式最复杂。把简单工厂模式中的代码修改得符合开闭原则,就变成了工厂方法模式,修改工厂方法模式的代码是一个工厂支持对多个具体产品的生产,就变成了抽象工厂模式。
2)从需要的工厂数量上,简单工厂模式需要的工厂数量最少,工厂方法模式需要的工厂数量最多,抽象工厂模式能够有效地减少工厂方法模式所需要的工厂数量。
3)从实际应用上,当项目中的产品数量较少时考虑使用简单工厂模式,如果项目稍大一点或者为了满足开闭原则,则可以使用工厂方法模式,而对于大型项目中有众多厂商并且每个厂商都生产一系列产品时应考虑使用抽象工厂模式。
相关文章:
C++设计模式创建型之工厂模式整理
一、工厂模式分类 工厂模式属于创建型模式,一般可以细分为简单工厂模式、工厂模式和抽象工厂模式。每种都有不同的特色和应用场景。 二、工厂模式详情 1、简单工厂模式 1)概述 简单工厂模式相对来说,在四人组写的《设计模式------可复用面…...

前端安全XSS和CSRF讲解
文章目录 XSSXSS攻击原理常见的攻击方式预防措施 CSRFCSRF攻击原理常见攻击情景预防措施: CSRF和XSS的区别 XSS 全称Cross Site Scripting,名为跨站脚本攻击。为啥不是单词第一个字母组合CSS,大概率与样式名称css进行区分。 XSS攻击原理 不…...

本地化部署自建类ChatGPT服务远程访问
本地化部署自建类ChatGPT服务远程访问 文章目录 本地化部署自建类ChatGPT服务远程访问前言系统环境1. 安装Text generation web UI2.安装依赖3. 安装语言模型4. 启动5. 安装cpolar 内网穿透6. 创建公网地址7. 公网访问8. 固定公网地址 🍀小结🍀 前言 Te…...

一、Webpack相关(包括webpack-dev-server用以热更新和html-webpack-plugin)
概念与功能: webpack是前端项目工程化的具体解决方案。它提供了友好的前端模块化开发支持,以及代码压缩混淆、处理浏览器端JavaScript的兼容性、性能优化等强大的功能。 快速上手:隔行变色 -S实际是--save的简写,表示安装的第三方…...

安全防御(3)
1.总结当堂NAT与双机热备原理,形成思维导图 2.完成课堂nat与双机热备试验 引用IDS是指入侵检测系统,它可以在网络中检测和防御入侵行为。IDS的签名是指根据已知入侵行为的特征制定的规则,用于检测和警告可能存在的入侵行为。签名过滤器可以根…...

AR远程专家指导在汽车改装上的应用有哪些?
随着科技的不断发展,AR增强现实技术逐渐走进了我们的生活。加上商贸国际化,远程协同纵深到制造生产的更多环节,研发协同、工艺优化等场景复杂、跨层级、需要频繁沟通确认的流程正通过AR应用实现全面远程化的过渡,在汽车行业&#…...

css-3:什么是响应式设计?响应式的原理是什么?如何做?
1、响应式设计是什么? 响应式网站设计(Responsive WEB desgin)是一个网络页面设计布局,页面的设计与开发应当根据用户行为以及设备环境(系统平台、屏幕尺寸、屏幕定向等)进行相应的相应和调整。 描述响应式…...
Armstrong数,n位数等于其各位数的n次方之和。
//Armstrong数具有以下特征:一个n位数等于其各位数的n次方之和。 //例如:1531^35^33^3 16341^46^43^44^4 #include<stdio.h> #include<math.h> //实在不知道的暴力求解方法 int main() {//Armstrong数具有以下特征:一个…...

blender的下载安装和配置中文环境
引言 在3D建模和动画设计领域,Blender 作为一款强大且免费的开源软件,一直以优秀的性能和对众多技术的支持赢得了大批用户的喜爱。然而,对于刚接触这款软件的用户而言,其安装和配置过程可能会带来一定困扰,尤其是在设…...

MyCat配置rule.xml、server.xml讲解
1. rule.xml分片规则配置文件 rule.xml中配置的主要就是拆分表的规则,rule.xml中主要包含两类标签 tableRule 和Function。 tableRule标签里面主要配置我们的分片规则,Function里面涉及的是分片规则里面所涉及的java类,都是在function中配置…...

Linux项目部署
目录 一JAVAWeb环境的部署【安装JDK,MySQL数据库,Tomcat】 二.手工部署SpringBoot项目(写的最好的) 1.在IDEA中开发SpringBoot项目并打成jar包--点击右侧的Maven执行package命令 2.将jar包上传到Linux服务器 3.执行以下命令&a…...

案例:Docker 镜像的创建及使用(commit与dockerfile方式)
文章目录 1、commit方式创建镜像1.1、前期准备1.2、制成镜像1.3、启动镜像1.3.1、启动镜像启动nginx1.3.2、一个命令直接全部启动1.3.3、两种方式区别 1.4、commit创建镜像方式的本质 2、Dockerfile的使用2.1、Dockerfile指令2.2、nginx镜像制作案例2.3、查看构建历史ÿ…...

QGIS二次开发三:显示Shapefile
Shapefile 为 OGR 所支持的最重要的数据格式之一,自然可以被 QGIS 加载。那么该如何显示Shapefile呢? 一、先上代码 #include <qgsapplication.h> #include <qgsproviderregistry.h> #include <qgsmapcanvas.h> #include <qgsvec…...
FFmpeg中相关结构体介绍
AVFormatContext:此结构体存储音视频封装格式中包含的信息,并且这个结构体是贯穿整个播放流程的。在这个结构体中主要包含AVInputFormat,AVOutputFormat、AVStream等。 struct AVInputFormat *iformat; // 输入数据的封装格式 AVIOContext *…...

Llama 2 云端部署与API调用【AWS SageMaker】
Meta 刚刚发布了 Llama 2 大模型。如果你和我们一样,你一定会迫不及待地想要亲自动手并用它来构建。 推荐:用 NSDT设计器 快速搭建可编程3D场景。 使用任何类型的 LLM 进行构建的第一步是将其托管在某处并通过 API 使用它。 然后你的开发人员可以轻松地将…...

根文件系统制作
1.官网下载工具 制作工具:busybox https://busybox.net/downloads/ 2.制作根文件系统 2.1准备工作 a.把压缩包放在FSP1M目录下,并解压 2.2正式开始 2.2.1配置交叉编译工具链 1. 打开Makefile文件 2. 修改ARCH ?$(SUBARCH) …...

Linux ARM64架构 动态替换 altinstructions
文章目录 简介一、altinstructions节1.1 .altinstructions1.2 .rela.altinstructions 二、内核模块重定位源码分析参考资料 简介 在内核开发中,有时需要对内核代码进行修补,以解决bug、优化性能或引入新功能。替代指令(altinstructions&…...

Mac显示隐藏文件夹
1、设置隐藏文件可见 defaults write com.apple.finder AppleShowAllFiles TRUE 2、killall Finder killall Finder...
使用 Habana Gaudi2 加速视觉语言模型 BridgeTower
🤗 宝子们可以戳 阅读原文 查看文中所有的外部链接哟! 在对最先进的视觉语言模型 BridgeTower 进行微调时,使用 Optimum Habana v1.6, Habana Gaudi2 可以达到 近 3 倍于 A100 的速度。硬件加速的数据加载以及 fast DDP 这两个新特…...

mysql查询语句之实践篇
基础查询语句 完整语法格式如下: select 字段列表 from 表名列表 where 条件列表 group by 分组字段 having 分组之后的条件 order by 排序 limit 分页限定 -- 创建表 create table stu(id int,name varchar(20),chinese double,english double,math double ); --…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...

大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...

IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...

Unity VR/MR开发-VR开发与传统3D开发的差异
视频讲解链接:【XR马斯维】VR/MR开发与传统3D开发的差异【UnityVR/MR开发教程--入门】_哔哩哔哩_bilibili...