【设计模式】-创造篇-工厂方法

news2024/9/21 10:38:00

制造业是一个国家工业经济发展的重要支柱,而工厂则是其根基所在。程序设计中的工厂类往往是对对象构造、实例化、初始化过程的封装,而工厂方法(Factory Method)则可以升华为一种设计模式,它对工厂制造方法进行接口规范化,以允许子类工厂决定具体制造哪类产品的实例,最终降低系统耦合,使系统的可维护性、可扩展性等得到提升。

4.1 工厂的多元化与专业化

要理解工厂方法模式,我们还得从头说起。众所周知,要制造产品(实例化对象)就得用到关键字“new”,例如“Plane plane=new Plane(); ”,或许还会有一些复杂的初始化代码,这就是我们常用的传统构造方式。然而这样做的结果会使飞机对象的产生代码被牢牢地硬编码在客户端类里,也就是说客户端与实例化过程强耦合了。而事实上,我们完全不必关心产品的制造过程(实例化、初始化),而将这个任务交由相应的工厂来全权负责,工厂最终能交付产品供我们使用即可,如此我们便摆脱了产品生产方式的束缚,实现了与制造过程彻底解耦。

除此之外,工厂方法模式是基于多元化产品的构造方法发展而来的,它开辟了产品多元化的生产模式,不同的产品可以交由不同的专业工厂来生产,例如皮鞋由皮鞋工厂来制造,汽车则由汽车工厂来制造,专业化分工明确,如图4-1所示。

在这里插入图片描述

4.2 游戏角色建模

在制造产品之前,我们先得为它们建模。我们依旧以空战游戏来举例,通常这类游戏中主角飞机都拥有强大的武器装备,以应对敌众我寡的游戏局面,所以敌人的种类就应当多样化,以带给玩家更加丰富多样的游戏体验。于是我们增加了一些敌机、坦克,游戏画面如图4-2所示。

在这里插入图片描述
如图4-2所示,游戏中敌人的种类有飞机和坦克,虽然它们之间的区别比较大,但总有一些共同的属性或行为,例如一对用来描述位置状态的坐标,以及一个展示(绘制)方法,以便将自己绘制到相应的地图位置上。好了,现在我们使用抽象类来定义所有敌人的父类,请参看代码清单4-1。

在这里插入图片描述
如代码清单4-1所示,我们在敌人抽象类Enemy中第13行定义了一个显示方法show(),并声明其抽象方法,以交给子类去实现,并按照构造方法(第7行)中初始化的坐标位置将自己绘制到地图上。接下来是具体子类实现,也就是敌机类和坦克类,请参看代码清单4-2与代码清单4-3。

注意

真正的游戏不止这么简单,敌机绘图线程会在下一帧擦除画板并重绘到下一个坐标以实现动画效果,敌人抽象类可能还会有move()(移动)、attack()(攻击)、die()(死亡)等方法,本章例程中我们忽略这些细节。

在这里插入图片描述
在这里插入图片描述
如代码清单4-2与代码清单4-3所示,飞机类Airplane和坦克类Tank都继承了敌人抽象类Enemy,并且分别实现了各自独特的展示方法show(),其中坦克应该绘制在下层(但在地图层之上)图层,而飞机则绘制在上层图层,这样才能遮盖住下层的所有图层以达到期望的视觉效果。

4.3 简单工厂不简单

产品建模完成后,就应该考虑如何实例化和初始化这些敌人了。毋庸置疑,要使它们都出现在屏幕最上方,就得使其纵坐标y被初始化为0,而对于横坐标x该怎样初始化呢?如果让敌人出现于屏幕正中央的话,就得将其横坐标初始化为屏幕宽度的一半,显然,如此玩家只需要一直对准屏幕中央射击,这对游戏可玩性来说是非常糟糕的,所以我们最好让敌人的横坐标随机产生,这样才能给玩家带来更好的游戏体验。我们来看客户端如何进行设置,请参看代码清单4-4。
在这里插入图片描述
如代码清单4-4所示,我们在第4行假设屏幕宽度为100,然后在第7行生成一个从0到“屏幕宽度”的随机数,再以此为横坐标构造并初始化敌人(为保持简单不考虑敌人自身的宽度),这样敌人就会出现在随机的横坐标位置上了。接着往下看,我们在第11行构造坦克时做了同样的设置,最后的输出结果达到了我们的预期,飞机和坦克随机出现于屏幕顶部,游戏可玩性大大提高。

然而,制造随机出现的敌人这个动作貌似不应该出现在客户端类中,试想如果我们还有其他敌人也需要构造的话,那么同样的代码就会再次出现,尤其是当初始化越复杂的时候重复代码就会越多。如此耗时费力,何不把这些实例化逻辑抽离出来作为一个工厂类?沿着这个思路,我们来开发一个制造敌人的简单工厂类,请参看代码清单4-5。

在这里插入图片描述
如代码清单4-5所示,简单工厂类SimpleFactory将之前在客户端类里制造敌人的代码挪过来,并封装在第10行的制造方法create()方法中,这里我们在第13行加入了一些逻辑判断,使其可以根据传入的敌人种类(飞机或坦克)生产出相应的对象实例,并随机初始化其位置。如此一来,制造敌人这个任务就全权交由简单工厂来负责了,于是客户端便可以直接从简单工厂取用敌人了,请参看代码清单4-6。

在这里插入图片描述
如代码清单4-6所示,客户端类的代码变得异常简单、清爽,这就是分类封装、各司其职的好处。然而,这个简单工厂的确很“简单”,但并不涉及任何的模式设计范畴,虽然客户端中不再直接出现对产品实例化的代码,但羊毛出在羊身上,制造逻辑只是被换了个地方,挪到了简单工厂中而已,并且客户端还要告知产品种类才能产出,这无疑是另一种意义上的耦合。

除此之外,简单工厂一定要保持简单,否则就不要用简单工厂。随着游戏项目需求的演变,简单工厂的可扩展性也会变得很差,例如对于那段对产品种类的判断逻辑,如果有新的敌人类加入,我们就需要再修改简单工厂。随着生产方式不断多元化,工厂类就得被不断地反复修改,严重缺乏灵活性与可扩展性,尤其是对于一些庞大复杂的系统,大量的产品判断逻辑代码会被堆积在制造方法中,看起来好像功能强大、无所不能,其实维护起来举步维艰,简单工厂就会变得一点也不简单了。

4.4 制定工业制造标准

其实系统中并不是处处都需要调用这样一个万能的“简单工厂”,有时系统只需要一个坦克对象,所以我们不必大动干戈使用这样一个臃肿的“简单工厂”。另外,由于用户需求的多变,我们又不得不生成大量代码,这正是我们要调和的矛盾。

针对复杂多变的生产需求,我们需要对产品制造的相关代码进行合理规划与分类,将简单工厂的制造方法进行拆分,构建起抽象化、多态化的生产模式。下面我们就对各种各样的生产方式(工厂方法)进行抽象,首先定义一个工厂接口,以确立统一的工业制造标准,请参看代码清单4-7。

在这里插入图片描述
如代码清单4-7所示,工厂接口Factory其实就是工厂方法模式的核心了。我们在第3行中声明了工业制造标准,只要传入屏幕宽度,就在屏幕坐标内产出一个敌人实例,任何工厂都应遵循此接口。接下来我们重构一下之前的简单工厂类,将其按产品种类拆分为两个类,请参看代码清单4-8和代码清单4-9。

在这里插入图片描述
在这里插入图片描述
如代码清单4-8和代码清单4-9所示,飞机工厂类AirplaneFactory与坦克工厂类TankFactory的代码简洁、明了,它们都以关键字implements声明了本类是实现工厂接口Factory的工厂实现类,并且在第4行给出了工厂方法create()的具体实现,其中飞机工厂制造飞机,坦克工厂制造坦克,各自有其独特的生产方式。

除了飞机和坦克,应该还会有其他的敌人,当玩家抵达游戏关底时总会有Boss出现,这时候我们该如何扩展呢?显而易见,基于此模式继续我们的扩展即可,先定义一个继承自敌人抽象类Enemy的Boss类,相应地还有Boss的工厂类,同样实现工厂方法接口,请分别参看代码清单4-10和代码清单4-11。

在这里插入图片描述
在这里插入图片描述
这里要注意代码清单4-11,因为Boss出现的坐标总是处于屏幕的中央位置,所以关底Boss工厂类BossFactory在初始化时在第6行设置Boss对象的横坐标为屏幕宽度的一半,而不是随机生成横坐标。“万事俱备,只欠东风”,客户端开始运行游戏了,请参看代码清单4-12。

在这里插入图片描述
在这里插入图片描述
如代码清单4-12所示,我们在第9行的循环体中调用坦克工厂类生成敌人,结果制造出的产品肯定是5辆坦克,接着又在第12行将工厂接口替换为飞机工厂类,结果5架飞机出现在屏幕上。抵达关底后,在第18行我们又将工厂接口替换为关底Boss工厂类,结果关底Boss出现并与玩家进行战斗,具体结果如第22行开始的输出所示。显而易见,多态化后的工厂多样性不言而喻,每个工厂的生产策略或方式都具备自己的产品特色,不同的产品需求都能找到相应的工厂来满足,即便没有,我们也可以添加新工厂来解决,以确保游戏系统具有良好的兼容性和可扩展性。

4.5 劳动分工

至此,以工厂方法模式构建的空战游戏就完成了,之后若要加入新的敌人类,只需添加相应的工厂类,无须再对现有代码做任何更改。不同于简单工厂,工厂方法模式可以被看作由简单工厂演化而来的高级版,后者才是真正的设计模式。在工厂方法模式中,不仅产品需要分类,工厂同样需要分类,与其把所有生产方式堆积在一个简单工厂类中,不如把生产方式放在具体的子类工厂中去实现,这样做对工厂的抽象化与多态化有诸多好处,避免了由于新加入产品类而反复修改同一个工厂类所带来的困扰,使后期的代码维护以及扩展更加直观、方便。下面我们来看工厂方法模式的类结构,如图4-3所示。

在这里插入图片描述
工厂方法模式的各角色定义如下。

■ Product(产品):所有产品的顶级父类,可以是抽象类或者接口。对应本章例程中的敌人抽象类。

■ ConcreteProduct(子产品):由产品类Product派生出的产品子类,可以有多个产品子类。对应本章例程中的飞机类、坦克类以及关底Boss类。

■ Factory(工厂接口):定义工厂方法的工厂接口,当然也可以是抽象类,它使顶级工厂制造方法抽象化、标准统一化。

■ ConcreteFactory(工厂实现):实现了工厂接口的工厂实现类,并决定工厂方法中具体返回哪种产品子类的实例。

工厂方法模式不但能将客户端与敌人的实例化过程彻底解耦,抽象化、多态化后的工厂还能让我们更自由灵活地制造出独特而多样的产品。其实工厂不必万能,方便面工厂不必生产汽车,手机工厂也不必生产牛仔裤,否则就会通而不精,妄想兼备所有产品线的工厂并不是好的工厂。反之,每个工厂都应围绕各自的产品进行生产,专注于自己的产品开发,沿用这种分工明确的工厂模式才能使各产业变得越来越专业化,而不至于造成代码逻辑泛滥,从而降低产出效率。正所谓“闻道有先后,术业有专攻”,正如英国经济学家亚当·斯密提出的劳动分工理论一样,如图4-4所示,明确合理的劳动分工才能真正地促进生产效率的提升。

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

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

相关文章

Vector - VT System - 板卡_VT7001

接上篇我们介绍完了背板VT8006和VT8012,今天我们来介绍下电源模块板卡VT7001,这个板卡是我们在不需要程控电源的时候依然能够实现精细化的电压、电流控制及检查的板卡,并且在脚本开发中无需考虑时延的一个模块。我们使用的大部分设备无论是继…

14---实现文件上传和下载(头像上传功能)

1、建Files表 接下来开始完成文件管理的内容,首先是数据库建Files表 DROP TABLE IF EXISTS file;CREATE TABLE file (id int(11) NOT NULL AUTO_INCREMENT COMMENT id,name varchar(255) DEFAULT NULL COMMENT 文件名称,type varchar(255) DEFAULT NULL COMMENT 文…

Detection of Individual Tree Crowns in Airborne Lidar Data

Abstract 激光扫描提供了一种收集林分信息的好方法。本文介绍了一种在落叶林和混合温带森林中的小足迹光检测和测距(激光雷达)数据中自动描绘单棵树的方法。在光栅化激光数据中,可能的树顶用局部最大值滤波器检测。之后,结合浇注…

【Bootstrap】基础知识和环境配置

目录 一、Bootstrap基础 1. 概念 2. 特点 3. 组成 3.1 基本结构 3.2 丰富的CSS样式库 3.3 布局组件 3.4 插件 二、bootstrap的环境配置 1. 在页面中引入本地文件 2. 使用CDN加速器 一、Bootstrap基础 1. 概念 Bootstrap是一个基于HTML、CSS和JavaScript语言编写的…

动手学习深度学习-《自动求导》

向量链式求导法则 标量链式法则: 扩展到向量: yyy是关于标量uuu的一个标量,x\bf{x}x是一个向量 yyy是关于向量u\bf{u}u的一个标量,x\bf{x}x是一个向量 y\bf{y}y是关于向量u\bf{u}u的一个向量,x\bf{x}x是一个向量…

Nginx+keepalived 实现高可用,防盗链及动静分离配置详解

一、Nginx Rewrite 规则 1. Nginx rewrite规则 Rewrite规则含义就是某个URL重写成特定的URL&#xff08;类似于Redirect&#xff09;&#xff0c;从某种意义上说为了美观或者对搜索引擎友好&#xff0c;提高收录量及排名等。 语法&#xff1a; rewrite <regex> <r…

Oracle11g彻底卸载教程(详细版)

目前项目结束,暂时不需要Oracle了,为清理电脑并便于后续重装,现记录彻底卸载的步骤如下: 1、按下“Windows”+“R”键,在运行窗中,输入compmgmt.msc,进入计算机管理------>服务,停止所有Oracle服务(对于状态为 正在进行 的服务,右键------>停止)。 2、在开始…

遗传算法解决旅行商问题(TSP)

遗传算法解决旅行商问题 作者&#xff1a;Cukor丘克环境&#xff1a;MatlabR202a vscode 问题描述 旅行商问题&#xff08;TSP&#xff09;. 一个商人欲从自己所在的城市出发&#xff0c;到若干个城市推销商品&#xff0c;然后回到其所在的城市。如何选择一条周游路线&…

SpringBoot3初体验 - 第457篇

历史文章&#xff08;文章累计450&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 5个月的精华&#xff1a;Spring/SpringB…

【JavaScript】多维数组及数组扁平化

文章目录【JavaScript】多维数组及数组扁平化一. 概念二. 数组扁平化方式一&#xff1a;使用flat()方法方式二&#xff1a;使用递归方式三&#xff1a;使用reduce()方式处理三. 对象数组的联合操作【JavaScript】多维数组及数组扁平化 一. 概念 (1) 一维数组&#xff1a;数组…

深度学习Week13-火灾温度预测(LSTM)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f366; 参考文章&#xff1a;第R2周&#xff1a;LSTM-火灾温度预测&#xff08;训练营内部可读&#xff09;&#x1f356; 作者&#xff1a;K同学啊任务说明&#xff1a;数据集中提供了火灾温度&…

c++知识点总结

文章目录1.引用2. 重载3. extern “C”4.构造函数5.析构函数6.类和对象7.面向对象模型8.继承9.多态10.函数模板11.类模板12.STL1.引用 不要返回局部变量的引用&#xff0c;调用函数执行后局部变量会销毁 2. 重载 是C多态的特性&#xff08;静态多态&#xff09;。同一个函数名代…

正点原子STM32(基于HAL库)

正点原子B站视频地址&#xff1a;https://www.bilibili.com/video/BV1bv4y1R7dp?p1&vd_sourcecc0e43b449de7e8663ca1f89dd5fea7d 目录单片机简介Cortex-M介绍初识STM32STM32命名规则STM32 选型STM32 设计数据手册最小系统IO 分配STM32启动过程分析启动模式启动文件分析启动…

基于彩色的图像分割

图像分割就是把图像分成若干个特定的、具有独特性质的区域并提出感兴趣目标的技术和过程。它是由图像处理到图像分析的关键步骤。现有的图像分割方法主要分以下几类&#xff1a;基于阈值的分割方法、基于区域的分割方法、基于边缘的分割方法以及基于特定理论的分割方法等。从数…

小程序03/ uni-app认识目录结构 、uni-app应用生命周期 和 生命周期钩子、uni-app页面生命周期 和 生命周期钩子

一.uni-app认识目录结构 二.uni-app应用生命周期 和 生命周期钩子 位置: uni-app 在App.vue中监听 在页面监听无效 说明: App.vue是uni-app的主组件 所有页面都是在App.vue 下进行切换的、是页面入口文件 但是App.vue 本身不是页面 这里不能编写视图元素 也就是没有<tmpl…

Git(一) - Git 概述

01_尚硅谷_Git_课程介绍_哔哩哔哩_bilibili Git 是一个免费的、开源的分布式版本控制系统&#xff0c;可以高效的处理从小型到大型的各种项目。 一、何为版本控制 版本控制是一种记录文件内容变化&#xff0c;以便将来查阅特定版本修订情况的系统。 版本控制其实最主要的是可以…

java 瑞吉外卖优化day2 Nginx

Nginx概述 下载与安装 可以到Nginx官方网站下载Nginx的安装包&#xff0c;地址为&#xff1a;https://nginx.org/en/download.html 安装过程&#xff1a; 1、安装依赖包 yum -y install gcc pcre-devel zlib-devel openssl openssl-devel 先yum install wget &#xff0c;…

随机数 - 时间种子的方案与实践

1.应用场景 主要弄清楚设置随机数种子的方法&#xff0c;可用于游戏开发当中的时间种子从而产生合理的随机数&#xff0c;避免出现bug。 2.学习/操作 1.文档阅读 07 | 带你快速上手 Lua-极客时间 2.整理输出 2.1 什么是种子 现在很多朋友下载东西时都会用bt种子文件&#xff0…

5分钟带你学习 linux 收发邮件步骤详解 at命令详解 crontab命令详解 附加at crontab命令练习

linux 收发邮件步骤详解 1.安装软件yum install mailx -yyum install sendmail -y 2.启动服务sendmailsystemctl start sendmail 3.更改配置vim /etc/mail.rc at命令详解 实例&#xff1a; crontab命令详解 实例&#xff1a; linux 收发邮件步骤详解 1.安装软件 yum…

拜占庭将军问题

前言 在分布式系统中交换信息, 部分成员可能出错导致发送了错误的信息, 在这种情况下如何达成共识. 这就是拜占庭将军问题所要解决的. 问题的简要描述如下: 3个军队协同作战(为了简单易懂, 以3个军队描述)每支军队的作战策略有两种"进攻"和"撤退"每个军…