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 图

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

「Java开发指南」如何利用MyEclipse启用Spring DSL?(一)

本教程将引导您通过启用Spring DSL和使用Service Spring DSL抽象来引导Spring和Spring代码生成项目&#xff0c;本教程中学习的技能也可以很容易地应用于其他抽象。在本教程中&#xff0c;您将学习如何&#xff1a; 为Spring DSL初始化一个项目创建一个模型包创建一个服务和操…

实体抽取全解析:技术与实战

目录 一、前言二、实体抽取技术概览基于规则的实体抽取基于统计的实体抽取基于深度学习的实体抽取 三、实体抽取的发展历程早期的实体抽取方法基于规则和词典的方法基于特征的机器学习方法 深度学习时代的实体抽取从传统模型到神经网络序列标注模型的兴起预训练语言模型的革命 …

如何修改 MySQL 8.0 的密码

在 MySQL 8.0 中修改用户密码是数据库管理的一项基本任务。本教程将引导您完成这一过程&#xff0c;确保即使是初学者也能理解并成功执行。 介绍 MySQL 是最流行的关系数据库管理系统之一。作为数据库管理员或开发人员&#xff0c;您可能需要更改用户的密码来保证账户安全。本…

QT 信号与槽的简单使用

文章目录 1.通过Singloat and Slots Editor 添加信号与槽2. 通过拖动动态添加3.通过转到槽方式添加&#xff08;自动关联&#xff09;4. 自定义信号与槽&#xff08;connect)4.1 connect方式4.2 自定义信号 1.通过Singloat and Slots Editor 添加信号与槽 点添加&#xff0c;然…

突破像素限制,尽显照片细腻之美——Topaz Gigapixel AI for Mac/Win

在这个数字化的时代&#xff0c;我们都热爱用照片记录生活中的美好瞬间。然而&#xff0c;有时候我们会发现&#xff0c;由于各种原因&#xff0c;照片的像素可能无法满足我们的需求。这时候&#xff0c;Topaz Gigapixel AI for Mac/Win 这款强大的照片放大工具应运而生。 Top…

open c UF_MODL_create_simple_hole 识别放置平面 UF_MODL_ask_face_data

在BLOCK上创建一个简单孔 UF_FEATURE_SIGN sign UF_NULLSIGN;double block_orig[3] { -25.0,-25.0,0.0 };char* block_len[3] { "50","50","30" };tag_t blk_obj;UF_MODL_create_block1(sign, block_orig, block_len, &blk_obj);tag_t bo…

PSPICE、Multisim和Saber哪个更适合电路仿真?没想到是它

PSPICE、Multisim和Saber这三个软件都是非常流行的模拟电路仿真工具&#xff0c;它们各自有各自的优缺点&#xff0c;我简单讲一下&#xff1a; PSPICE&#xff1a; 优点&#xff1a; 精度高&#xff1a;PSPICE是专业的电路仿真软件&#xff0c;可以进行高精度的模拟电路仿真…

兮兮牧场养殖小游戏积分兑换互动商城引流模式

刚注册的新会员必须要进入牧场才能激活所有功能 一、获得动物的途径的方式 第一种是邀请好友注册获得&#xff0c;第二种是看广告获得 邀诘好友注册获得动物明细: 1、从兮兮牧场的邀请好友的链接去邀请好友才能获得&#xff0c;其他邀请码无效 2、注册赠送小鸡一只; 3、邀…

基于Springboot + vue + MySQL +Tomcat 社区医院管理服务系统 (含源码)

目录 &#x1f4da; 前言 &#x1f4d1;摘要 &#x1f4d1;系统架构 &#x1f4da; 数据库设计 &#x1f4ac; 用户注册实体属性图 &#x1f4ac; 医生实体属性图 &#x1f4da; 系统功能的具体实现 &#x1f4ac; 前台模块 用户注册 医生信息 &#x1f4ac; 后台功能模…

Docker部署SpringBoot+Vue前后端分离项目

文章目录 1. 安装Docker1. 1 卸载旧版Docker1.2 配置yum仓库1.3 安装Docker1.4 添加自启动配置1.5 配置阿里云镜像加速1.6 测试 2. 安装Nginx2.1 拉取镜像2.2 安装Nginx2.3 测试 3. 安装MySQL3.1 拉取镜像3.2 安装MySQL3.3 连接MySQL 4. 部署SpringBoot项目4.1 Maven打包4.2 编…

centos7安装 on-my-zsh

如下&#x1f447; yum install -y zsh chsh -s /bin/zsh yum install -y git sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)" 重启即可生效啦~

Windows搭建LightPicture图片管理网站结合内网穿透实现公网访问本地图片

文章目录 1.前言2. Lightpicture网站搭建2.1. Lightpicture下载和安装2.2. Lightpicture网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 现在的手机越来越先进&#xff0c;功能也越来越多&#xff0c;而手机…

【LAMMPS学习】八、基础知识(2.2)类型标签

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语&#xff0c;以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各…

车载数据终端丨车载平板丨车载平板电脑为何在叉车上使用

车载终端平板是一种专门设计用于车辆和运输行业的电子设备。它们通常被安装在叉车、卡车、拖车和其他机动车辆上&#xff0c;以便驾驶员可以更方便地管理和控制运输任务。 为了了解车载终端平板为何能够在叉车上使用&#xff0c;我们需要考虑以下几个方面&#xff1a; 物理结构…

HarmonyOS4 页面路由

Index.ets: import router from ohos.routerclass RouterInfo {// 页面路径url: string// 页面标题title: stringconstructor(url: string, title: string) {this.url urlthis.title title} }Entry // 入口組件 Component struct Index {State message: string 页面列表// …

StylizedGS: Controllable Stylization for 3D Gaussian Splatting

StylizedGS: Controllable Stylization for 3D Gaussian Splatting StylizedGS&#xff1a;3D高斯溅射的可控样式化 Dingxi Zhang, Zhuoxun Chen, Yu-Jie Yuan, Fang-Lue Zhang, Zhenliang He, Shiguang Shan, and Lin Gao1 张定西&#xff0c;陈卓勋&#xff0c;袁玉洁&#x…

LabVIEW无线快速存取记录器(WQAR)测试平台

LabVIEW无线快速存取记录器&#xff08;WQAR&#xff09;测试平台 随着民用航空业的迅速发展&#xff0c;航空安全的保障日益成为公众和专业领域的关注焦点。无线快速存取记录器&#xff08;WirelessQuick Access Recorder, WQAR&#xff09;作为记录飞行数据、监控飞行品质的…

Kafka—ISR机制

ISR机制 Kafka 中的 ISR&#xff08;In-Sync Replicas&#xff09;机制是一种用于确保数据可靠性和一致性的重要机制。ISR 是一组副本&#xff0c;它包括分区的领导者&#xff08;Leader&#xff09;和追随者&#xff08;Follower&#xff09;副本&#xff0c;这些副本与领导者…

Redis(二十)五大经典类型源码

文章目录 面试题源码核心Redis基本的数据结构(骨架)Redis数据库的实现Redis服务端和客户端实现其他 K-V实现怎样实现键值对(key-value)数据库的传统五大基本数据类型和新五大数据类型 5大数据结构底层C语言源码分析示例redisObject五大数据结构解析定义Debug Object keyString …

用Python接单,一单800块虽然不多,但真的能够挣!

当今收入低于5000的人至少占到40%&#xff0c;完全不够养活一家人&#xff0c;而且很多小伙伴其实空余时间比较多&#xff0c;特别是大学生&#xff0c;零花钱又不够花&#xff0c;都想靠业余时间找点轻松的活增加收入。但是缺没门路&#xff0c;为此结合我多年编程开发经验&am…