C++11 设计模式2. 简单工厂模式
简单工厂(Simple Factory)模式
我们从实际例子出发,来看在什么情况下,应用简单工厂模式。
还是以一个游戏举例
//策划:亡灵类怪物,元素类怪物,机械类怪物:都有生命值,魔法值,攻击力三个属性。
//Monster作为父类,M_Undead(亡灵类),M_Element(元素类怪物),M_Mechanic(机械类怪物)。
一般写法如下:
#include <iostream>
using namespace std;//(1)简单工厂(Simple Factory)模式
//策划:亡灵类怪物,元素类怪物,机械类怪物:都有生命值,魔法值,攻击力三个属性。
//Monster作为父类,M_Undead(亡灵类),M_Element(元素类怪物),M_Mechanic(机械类怪物)。namespace _namespace1 {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;};//M_Undead(亡灵类)class M_Undead :public Monster {public:M_Undead(int life, int magic, int attack) :Monster(life, magic, attack) {cout << "创建了一个亡灵类 life = " << m_life <<" magic = "<< m_magic << " attack = "<< m_attack << endl;}};//M_Element(元素类怪物)class M_Element :public Monster {public:M_Element(int life, int magic, int attack) :Monster(life, magic, attack) {cout << "创建了一个元素类怪物 life = " << m_life << " magic = " << m_magic << " attack = " << m_attack << endl;}};//M_Mechanic(机械类怪物)class M_Mechanic :public Monster {public:M_Mechanic(int life, int magic, int attack) :Monster(life, magic, attack) {cout << "创建了一个机械类怪物 life = " << m_life << " magic = " << m_magic << " attack = " << m_attack << endl;}};};void normalTest() {_namespace1::Monster *pm1 = new _namespace1::M_Undead(1, 2, 3);_namespace1::Monster *pm2 = new _namespace1::M_Element(4, 5, 6);_namespace1::Monster *pm3 = new _namespace1::M_Mechanic(7, 8, 9);delete pm1;delete pm2;delete pm3;}int main()
{_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);//程序退出时检测内存泄漏并显示到“输出”窗口//不使用工厂模式的一般写法normalTest();std::cout << "Hello World!\n";
}
问题
那么这个不使用工厂模式的一般写法有啥问题呢?或者说有啥缺点呢?
//假设我们在每一个关卡都要 new 出来这些实例对象。
//有一天策划找到我们说,机械怪物的生命力要加1,我们能想到的合适的办法是:
//将怪物的参数做成配置文件,游戏加载时候就将配置文件读取成一个一个的全局变量,然后new 的时候用这些全局变量
//有一天策划又找到我们说:怪物还应该有个"盔甲","鞋子","帽子","武器","盾牌"这些属性,
//那我们就要改动构造方法了,这个不改不行了,又因为我们在每一关都要new出来这些怪物,因此每个关卡的代码都要改动。
//言外之意是:这种普通的写法 new +具体类名来创建对象是一种 依赖具体类型的紧耦合关系
解决方案
那么怎么改动才合理呢?引入简单工厂模式
//工厂模式:通过把创建对象的代码包装起来,做到创建对象的代码与具体的业务逻辑代码相隔离的目的。
#include <iostream>
using namespace std;//(1)简单工厂(Simple Factory)模式
//策划:亡灵类怪物,元素类怪物,机械类怪物:都有生命值,魔法值,攻击力三个属性。
//Monster作为父类,M_Undead(亡灵类),M_Element(元素类怪物),M_Mechanic(机械类怪物)。namespace _namespace1 {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;};//M_Undead(亡灵类)class M_Undead :public Monster {public:M_Undead(int life, int magic, int attack) :Monster(life, magic, attack) {cout << "创建了一个亡灵类 life = " << m_life <<" magic = "<< m_magic << " attack = "<< m_attack << endl;}};//M_Element(元素类怪物)class M_Element :public Monster {public:M_Element(int life, int magic, int attack) :Monster(life, magic, attack) {cout << "创建了一个元素类怪物 life = " << m_life << " magic = " << m_magic << " attack = " << m_attack << endl;}};//M_Mechanic(机械类怪物)class M_Mechanic :public Monster {public:M_Mechanic(int life, int magic, int attack) :Monster(life, magic, attack) {cout << "创建了一个机械类怪物 life = " << m_life << " magic = " << m_magic << " attack = " << m_attack << endl;}};const int UndeadType = 1;const int ElementType = 2;const int MechanicType = 3;//简单工厂模式,怪物工厂class MonsterFactory {public:Monster * createMonster(int monstertype) {Monster * tempPM = nullptr;switch (monstertype){case UndeadType://UndeadType,ElementType,MechanicType都是程序员定义的表示怪物类型的值//1,2,3可以来源于从配置文件读取的值,//我们可以将 new M_Undead的代码全部都写在这里,//如果策划要改动构造方法,给构造方法里面加上"盔甲","鞋子","帽子","武器","盾牌"这些属性//我们只需要在这里改动,无需在业务逻辑层面改动构造方法。tempPM = new M_Undead(11, 22, 33);break;case ElementType:tempPM = new M_Element(44,55,66);//提示:如果元素怪物有额外的属性,或者参数,也可以在这里设置//tempPM.setxxx(xxx);break;case MechanicType:tempPM = new M_Mechanic(77,88,99);break;default:return tempPM;break;}//注意switch case 使用时候的 这个warning 提示:3 > c:\users\administrator\source\repos\designpattern\002simplefactory\002simplefactory.cpp(80) : warning C4715 : “_namespace1::MonsterFactory::createMonster” : 不是所有的控件路径都返回值//解决方案是加上如下的这一样return tempPM;}};
};void normalTest() {_namespace1::Monster *pm1 = new _namespace1::M_Undead(1, 2, 3);_namespace1::Monster *pm2 = new _namespace1::M_Element(4, 5, 6);_namespace1::Monster *pm3 = new _namespace1::M_Mechanic(7, 8, 9);delete pm1;delete pm2;delete pm3;}void simpleFactoryTest() {_namespace1::MonsterFactory monsfactory;_namespace1::Monster *pm4 = monsfactory.createMonster(_namespace1::UndeadType);_namespace1::Monster *pm5 = monsfactory.createMonster(_namespace1::ElementType);_namespace1::Monster *pm6 = monsfactory.createMonster(_namespace1::MechanicType);delete pm4;delete pm5;delete pm6;
}int main()
{_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);//程序退出时检测内存泄漏并显示到“输出”窗口//不使用工厂模式的一般写法normalTest();//那么这个不使用工厂模式的一般写法有啥问题呢?//或者说有啥缺点呢?//假设我们在每一个关卡都要 new 出来这些实例对象。//有一天策划找到我们说,机械怪物的生命力要加1,我们能想到的合适的办法是://将怪物的参数做成配置文件,游戏加载时候就将配置文件读取成一个一个的全局变量,然后new 的时候用这些全局变量//有一天策划又找到我们说:怪物还应该有个"盔甲","鞋子","帽子","武器","盾牌"这些属性,//那我们就要改动构造方法了,这个不改不行了,又因为我们在每一关都要new出来这些怪物,因此每个关卡的代码都要改动。//言外之意是:这种普通的写法 new +具体类名来创建对象是一种 依赖具体类型的紧耦合关系//那么怎么改动才合理呢?引入简单工厂模式//工厂模式:通过把创建对象的代码包装起来,做到创建对象的代码与具体的业务逻辑代码相隔离的目的。simpleFactoryTest();//从上面的代码可以看到,简单工厂模式确实实现了new 出来具体对象, 和 业务逻辑的分离,//但是不符合 "开闭原则"//"开闭原则"说的是代码扩展性问题——对扩展开放,对修改关闭(封闭);//假设过了两天,策划找到我们说:加一种怪物,新怪物类型:M_Beast(野兽类)//那我们要怎么改呢?首先肯定是加一个 M_Beast类了,继承Monster//然后MonsterFactory 中改动 createMonster方法完成。//很显然,我们要改动到原先的 createMonster 方法,这是违反了 "开闭原则的"。//那么如何改动才合理呢?这就要用到 "工厂方法" 模式std::cout << "Hello World!\n";
}
遗留问题
//从上面的代码可以看到,简单工厂模式确实实现了new 出来具体对象, 和 业务逻辑的分离,
//但是不符合 "开闭原则"
//"开闭原则"说的是代码扩展性问题——对扩展开放,对修改关闭(封闭);
//假设过了两天,策划找到我们说:加一种怪物,新怪物类型:M_Beast(野兽类)
//那我们要怎么改呢?首先肯定是加一个 M_Beast类了,继承Monster
//然后MonsterFactory 中改动 createMonster方法完成。
//很显然,我们要改动到原先的 createMonster 方法,这是违反了 "开闭原则的"。
//那么如何改动才合理呢?这就要用到 "工厂方法" 模式
简单工厂的UML 图
相关文章:
C++11 设计模式2. 简单工厂模式
简单工厂(Simple Factory)模式 我们从实际例子出发,来看在什么情况下,应用简单工厂模式。 还是以一个游戏举例 //策划:亡灵类怪物,元素类怪物,机械类怪物:都有生命值࿰…...
RabbitMQ-死信队列常见用法
目录 一、什么是死信 二、什么是死信队列 编辑 三、第一种情景:消息被拒绝时 四、第二种场景:. 消费者发生异常,超过重试次数 。 其实spring框架调用的就是 basicNack 五、第三种场景: 消息的Expiration 过期时长或队列TTL…...
2024/4/14周报
文章目录 摘要Abstract文献阅读题目创新点CROSSFORMER架构跨尺度嵌入层(CEL)CROSSFORMER BLOCK长短距离注意(LSDA)动态位置偏置(DPB) 实验 深度学习CrossFormer背景维度分段嵌入(DSW)…...
MySQL 社区版 安装总结
很早就安装过MySQL,没有遇到过什么问题,直接next就行了,这次在新电脑上安装却遇到了一些问题,记录一下。 安装的是MySQL社区版,下载地址是www.mysql.com,进入后选择DOWNLOAD页面,选择MySQL Com…...
二叉排序树的增删改查(java版)
文章目录 1. 基本节点2. 二叉排序树2.1 增加节点2.2 查找(就是遍历)就一起写了吧2.3 广度优先遍历2.4 删除(这个有点意思)2.5 测试样例 最后的删除,目前我测试的是正确的 1. 基本节点 TreeNode: class TreeNode{pri…...
linux下coredump问题的定位分析方法
(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu) 参考:https://blog.csdn.net/m0_73698480/article/details/130077852 最近定位了一段时间linux下的崩溃问题,又收集了一些思路,特整理记录一下。 常见coredump定位方法是:…...
第十届蓝桥杯省赛真题(C/C++大学B组)
目录 试题 A: 组队 试题 B: 年号字串 试题 C: 数列求值 试题 D: 数的分解 试题 E: 迷宫 试题 F: 特别数的和 试题 G:完全二叉树的权值 试题 H:等差数列 试题 I:后缀表达式(不一定对) 试题 J:灵能…...
Scrapy 爬取m3u8视频
Scrapy 爬取m3u8视频 【一】效果展示 爬取ts文件样式 合成的MP4文件 【二】分析m3u8文件路径 视频地址:[在线播放我独自升级 第03集 - 高清资源](https://www.physkan.com/ph/175552-8-3.html) 【1】找到m3u8文件 这里任务目标很明确 就是找m3u8文件 打开浏览器…...
LVGL简单记录
1、 vs中代码旁边有个小锁删除git 2、Visual Studio 试图编译已删除的文件, 如果这个文件也是你不再需要编译的文件,且已经从文件系统中删除,你需要从 .vcxproj 文件中移除或者注释掉这一行,以停止Visual Studio尝试去编译一个不…...
计算机网络——ARP协议
前言 本博客是博主用于复习计算机网络的博客,如果疏忽出现错误,还望各位指正。 这篇博客是在B站掌芝士zzs这个UP主的视频的总结,讲的非常好。 可以先去看一篇视频,再来参考这篇笔记(或者说直接偷走)。 …...
【C++]C/C++的内存管理
这篇博客将会带着大家解决以下几个问题 1. C/C内存分布 2. C语言中动态内存管理方式 3. C中动态内存管理 4. operator new与operator delete函数 5. new和delete的实现原理 6. 定位new表达式(placement-new) 1. C/C内存分布 我们先来看下面的一段代码和相关问题 int global…...
深入理解计算机网络分层结构
一、 为什么要分层? 计算机网络分层的主要目的是将复杂的网络通信过程分解为多个相互独立的层次,每个层次负责特定的功能。这样做有以下几个好处: 模块化设计:每个层次都有清晰定义的功能和接口,使得网络系统更易于设…...
亚马逊云科技CTO带你学习云计算降本增效秘诀
2023亚马逊云科技一年一度的重磅春晚--Re:invent上有诸多不同话题的主题Keynote,这次小李哥带大家复盘来自亚马逊CTO: Wener博士的主题演讲: 云架构节俭之道1️⃣节俭对于云计算为什么重要? ▶️企业基础设施投入大,利用好降本策略可以减少巨…...
快速上手Vue
目录 概念 创建实例 插值表达式 Vue响应式特性 概念 Vue是一个用于 构建用户界面 的 渐进式 框架 构建用户界面:基于数据渲染出用户看到的页面 渐进式:Vue相关生态:声明式渲染<组件系统<客户端路由<大规模状态管理<构建工具 V…...
java 目录整理
Java知识相关目录主要参考黑马程序员 风清扬老师的视屏,参考链接为 Java_黑马刘意(风清扬)2019最新版_Java入门视频_Java入门_Java编程_Java入门教程_黑马教程_黑马程序员_idea版_哔哩哔哩_bilibili 1、java 基础 java基本认识?java跨平台原理?jdk、jre、jvm的联系? 链接:…...
使用Python的Pillow库进行图像处理书法参赛作品
介绍: 在计算机视觉和图像处理领域,Python是一种强大而流行的编程语言。它提供了许多优秀的库和工具,使得图像处理任务变得轻松和高效。本文将介绍如何使用Python的wxPython和Pillow库来选择JPEG图像文件,并对选中的图像进行调整和…...
docker 容器指定utf-8编码
在运行 Docker 容器的时候,如果容器内应用需要使用 UTF-8 编码来正常处理中文,你可以通过设置环境变量来指定编码。 可以使用 -e 或者 --env 标志来设置环境变量。比如,设置 LANG 和 LC_ALL 环境变量为 C.UTF-8 或者 en_US.UTF-8:…...
单例模式以及常见的两种实现模式
单例模式是校招中最常考的设计模式之一. 设计模式其实就是类似于“规章制度”,按照这个套路来进行操作。 单例模式能保证某个类在程序中只存在唯一 一份实例。而不会创建出多个实例,如果创建出了多个实例,就会编译报错。而不会创建出多个实…...
Java hashCode() 和 equals()的若干问题解答
Java hashCode() 和 equals()的若干问题解答 本章的内容主要解决下面几个问题: 1 equals() 的作用是什么? 2 equals() 与 的区别是什么? 3 hashCode() 的作用是什么? 4 hashCode() 和 equals() 之间有什么联系? …...
高级IO——React服务器简单实现
3.4Reactor服务器实现 1.connect封装 每一个连接都要有一个文件描述符和输入输出缓冲区,还有读、写、异常处理的回调方法; 还包括指向服务器的回指指针; class connection; class tcpserver;using func_t std::function<void(s…...
Qt使用插件QPluginLoader 机制开发
简介: 插件(Plug-in,又称addin、add-in、addon或add-on,又译外挂)是一种遵循一定规范的应用程序接口编写出来的程序。 Qt 提供了2种APIs来创建插件: 一种高级API,用于为Qt本身编写插件:自定义数据库驱动程序,图像格…...
双子座 Gemini1.5和谷歌的本质
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
二百三十、MySQL——MySQL表的索引
1 目的 梳理一下目前MySQL维度表的索引情况,当然网上也有其他博客专门讲MySQL索引的,我这边只是梳理一下目前的索引状况而已 2单列索引 2.1 索引截图 2.2 建表语句 3 联合索引 3.1 索引截图 3.2 建表语句 4 参考的优秀博客 http://t.csdnimg.cn/ZF7…...
并发编程之ThreadLocal使用及原理
ThreadLocal主要是为了解决线程安全性问题的 非线程安全举例 public class ThreadLocalDemo {// 非线程安全的private static final SimpleDateFormat sdf new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");public static Date parse(String strDate) throws ParseExc…...
软件测试 测试开发丨Pytest结合数据驱动-yaml,熬夜整理蚂蚁金服软件测试高级笔试题
编程语言 languages: PHPJavaPython book: Python入门: # 书籍名称 price: 55.5 author: Lily available: True repertory: 20 date: 2018-02-17 Java入门: price: 60 author: Lily available: False repertory: Null date: 2018-05-11 yaml 文件使用 查看 yaml 文件 pych…...
软考数据库---2.SQL语言
主要记忆:表、索引、视图操作语句;数据操作;通配符、转义符;授权;存储过程;触发器 这部分等等整理一下: “”" 1、 数据定义语言。 SQL DDL提供定义关系模式和视图、 删除关系和视图、 修改关系模式的…...
基于顺序表实现通讯录
上篇我们讲了顺序表是什么,和如何实现顺序表。这篇文章我们将基于顺序表来实现通讯录。 文章目录 前言一、基于顺序表是如何实现的二、通讯录的头文件和实现文件三、通讯录的实现3.1 定义通讯录结构3.2 初始化通讯录3.3 销毁通讯录3.4 通讯录添加数据3.5 查找联系人…...
咸鱼之王_手游_开服搭建架设_内购修复无bug运营版
视频演示 咸鱼之王_手游_开服 游戏管理后台界面 源码获取在文章末尾 源码获取在文章末尾 源码获取在文章末尾 或者直接下面 https://githubs.xyz/y28.html 1.安装宝塔 yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh &…...
【JSON2WEB】14 基于Amis的CRUD开发30分钟速成
【JSON2WEB】系列目录 【JSON2WEB】01 WEB管理信息系统架构设计 【JSON2WEB】02 JSON2WEB初步UI设计 【JSON2WEB】03 go的模板包html/template的使用 【JSON2WEB】04 amis低代码前端框架介绍 【JSON2WEB】05 前端开发三件套 HTML CSS JavaScript 速成 【JSON2WEB】06 JSO…...
Java入门教程||Java 变量
Java 变量 Java教程 - Java变量 变量由标识符,类型和可选的初始化程序定义。变量还具有范围(可见性/生存期)。 Java变量类型 在Java中,必须先声明所有变量,然后才能使用它们。变量声明的基本形式如下所示࿱…...
网站如何选择关键词/绍兴seo网站管理
https://www.runoob.com/linux/linux-vim.html这个里面比较详细。 1、vim是一种文本编辑器,为啥要学? 答:1)所有的UNIX Like 系统都会内置vi文本编辑器,其他的文本编辑器不一定存在; 2)很多软…...
国家已明令禁止现货交易/cpu游戏优化加速软件
好的,我会用中文来回答你的问题。 首先,我需要确认一下你问的是否是 Transformer-XL 这个模型?Transformer-XL 是一个在长序列上的自注意力机器翻译模型,它在许多自然语言处理任务中表现出了很好的性能。 如果你是想问如何使用 Transformer-XL,那么首先你需要在 PyTorch 中…...
佛山高端外贸网站建设/长沙自动seo
整理一帖,方便速查网络通信常见端口汇总 端口号描述0端口无效端口,通常用于分析操作系统1端口传输控制协议端口服务多路开关选择器2端口管理实用程序3端口压缩进程5端口远程作业登录7端口回显9端口丢弃11端口在线用户13端口时间17端口每日引用18端口消息发送协议19端…...
北京专门做网站的公司/百度一下你就知道下载安装
之前在某一家银行也接触过java写的性能接口脚本,最近因项目,也需编写java接口性能测试脚本,脑袋一下懵逼了,有点不知道从何入手。随后上网查了相关资料,自己又稍微总结了一下,与大家共同分享哈~首先&#x…...
wordpress自动电影釆集/西安网络推广外包公司
CSDN话题挑战赛第2期 参赛话题:学习笔记 🖥️ NodeJS专栏:Node.js从入门到精通 🖥️ 博主的前端之路(源创征文一等奖作品):前端之行,任重道远(来自大三学长的万字自述&am…...
十堰网站制作公司电话/活动策划方案详细模板
一 配置Path步骤: a:右键点击计算机 → 选择属性 → 更改设置 → 点击高级 → 点击环境变量 → 创建名为JAVA_HOME的环境变量 → 将jdk所在的目录路径(bin所在的路径)配置到JAVA_HOME变量中 b: 用;与其他变量分隔 → 在path环境变量中添…...