用23种设计模式打造一个cocos creator的游戏框架----(九)访问者模式

news2024/11/16 21:43:34

1、模式标准

模式名称:访问者模式

模式分类:行为型

模式意图:将数据操作与数据结构分离,使得在不修改数据结构的前提下,可以添加或改变对数据的操作。

结构图:

适用于:

  1. 当你需要对一个复杂对象结构执行一些操作,并且你希望实现这些操作与该对象结构的类无关时。访问者模式使得你可以在不修改现有类的情况下添加新的操作。

  2. 当你需要清洁并且有逻辑的方式来操作复杂的对象结构时。将相关的操作逻辑放在一个访问者类中,使得你可以将复杂的操作逻辑与数据结构的类分离。

  3. 当你需要在不同的类上执行类似的操作,而不希望污染这些类的接口时。访问者模式允许你在不影响其他类的情况下添加新的操作。

2、分析与设计  

访问者模式,简单描述就是一个访问者访问了你,对你提了一些建议和操作。这里打个比方,比如本来你只会发1234567乐符发音,音乐老师访问者过来说,听他的指挥,你说好的(accept),之后你就听从音乐老师的指挥,哼出了一个曲调。老师一走,你就啥都不会了。

在游戏设计中,不确定其他人是如何用这个访问者模式的,但是我的第一感觉是是否能用在攻击上面,比如单位A对单位B发动了一个火球攻击,火球在遇到单位B时,对其产生了伤害。这个火球只是一个特效,火球的伤害就是伤害访问者。

来问一下GPT是如何说的

在游戏开发中,访问者模式可以被用于处理各种复杂的游戏逻辑,包括你所描述的攻击和属性修改的场景。让我们来看一下这种方式的一些可能优点和缺点。

优点

  1. 分离游戏逻辑:访问者模式可以将攻击逻辑和单位属性的修改逻辑从实体类(如单位类)中分离出来,使得代码更易于理解和维护。

  2. 易于添加新的操作:如果你在未来需要添加新的攻击方式或者修改属性的方式,你可以通过添加新的访问者类来实现,而不需要修改现有的单位类。

缺点

  1. 可能会破坏封装:访问者模式需要访问者类能够访问被访问元素的内部状态(在这个案例中是单位的属性),这可能会破坏被访问元素的封装。

  2. 可能导致代码更复杂:如果游戏的逻辑不是很复杂,使用访问者模式可能会使代码变得不必要的复杂。

总的来说,将一次攻击作为攻击访问者,然后在攻击访问者中计算对被攻击单位的属性的修改,是一种可行的方式。但是,是否应该使用这种方式,取决于你的具体需求,包括你的游戏的复杂性,你对易于理解和维护的需求,以及你对封装的考虑等。

会破坏封装?什么意思,继续问

“这句话有些不理解,“可能会破坏被访问元素的封装””

“封装”是面向对象编程中的一个重要概念,它指的是将对象的内部状态和实现细节隐藏起来,只暴露出一组公开的接口供外部使用。封装有助于保护对象的内部状态不被外部直接修改,也有助于减少代码之间的耦合,使得代码更易于理解和维护。

在访问者模式中,访问者类需要访问被访问元素的内部状态,以便执行一些操作。这就意味着被访问元素需要提供一些公开的接口来暴露其内部状态,这可能会破坏被访问元素的封装。具体来说,一些本应该是私有的数据或者实现细节可能会被暴露出来,这可能使得被访问元素变得更加脆弱,也可能使得代码更难以理解和维护。

例如,假设你有一个Unit类,它有一些私有的属性,如health(生命值)和armor(护甲)。在一般情况下,你可能只会提供一些公开的方法来修改这些属性,如decreaseHealthincreaseArmor,而不会直接暴露这些属性。但是,如果你使用访问者模式,你可能需要提供一些公开的接口来让AttackVisitor能够直接访问和修改healtharmor,这就破坏了Unit的封装。

你有什么好的建议吗?

 对于这个问题,一种可能的解决方案是使用 "双重分派"(double dispatch)。在这种模式下,你可以在被访问者(如单位)中定义一个接受访问者的方法,并在这个方法中调用访问者的相应方法。这样,访问者可以在被访问者的上下文中执行操作,而不需要直接访问被访问者的内部状态。下面是一个简化的示例:

class Unit {
    constructor(health, armor) {
        this.health = health;
        this.armor = armor;
    }

    accept(visitor) {
        visitor.visitUnit(this);
    }

    decreaseHealth(amount) {
        this.health -= amount;
    }

    increaseArmor(amount) {
        this.armor += amount;
    }
}

class AttackVisitor {
    constructor(damage) {
        this.damage = damage;
    }

    visitUnit(unit) {
        unit.decreaseHealth(this.damage);
        // 更复杂的攻击逻辑可以在这里添加
    }
}

let unit = new Unit(100, 50);
let attackVisitor = new AttackVisitor(10);
unit.accept(attackVisitor);

 

在上面的代码中,AttackVisitor可以通过调用visitUnit方法来修改Unit的状态,而不需要直接访问Unithealtharmor属性。这样,Unit的封装性就得到了保护。

这种方法的一个缺点是,如果你有多种类型的单位,你可能需要在AttackVisitor中为每种单位类型定义一个不同的visit方法。然而,这种问题可以通过使用一些设计模式,如"策略模式"或"组合模式",来解决。

这种方法的一个优点是,你可以轻松地添加新的访问者,如DefenseVisitor,而不需要修改Unit类。这是访问者模式的一个主要优点。

总得来说的是可行的方案。

意图:将数据操作(伤害操作)与数据结构(受攻击者)分离,使得在不修改数据结构(受攻击者)的前提下,可以添加或改变对数据的操作(伤害操作)。

3、开始打造

import { IUnitItem } from "../items/IItem"

export interface IAttackVisitor {
    // 普通单位
    visitNormalUnitItem(unitItem: IUnitItem): void
    // 英雄单位
    visitHeroUnitItem(unitItem: IUnitItem): void
}

 

import { IUnitItem } from "../items/IItem";
import { IAttackVisitor } from "./IAttackVisitor";

// 单体攻击
export class MonomerAttackVisitor implements IAttackVisitor {
    damage: number = 0
    constructor(damage: number) {
        this.damage = damage
    }
    // 普通单位
    visitUnitItem(unitItem: IUnitItem): void {
        unitItem.subHp(this.damage)
    }
    // 英雄单位,受到伤害值为0.8
    visitHeroUnitItem(unitItem: IUnitItem): void {
        unitItem.subHp(this.damage * 0.8)
    }
}

 

import { IUnitItem } from "../items/IItem";
import { IAttackVisitor } from "./IAttackVisitor";

// 群体(衍射)攻击
export class GroupAttackVisitor implements IAttackVisitor {
    damage: number = 0
    constructor(damage: number) {
        this.damage = damage
    }
    // 普通单位
    visitUnitItem(unitItem: IUnitItem): void {
        unitItem.subHp(this.damage)
        // todo 周围单位也受到伤害
    }
    // 英雄单位,受到伤害值为0.8
    visitHeroUnitItem(unitItem: IUnitItem): void {
        unitItem.subHp(this.damage * 0.8)
        // todo 周围单位也受到伤害
    }
}
export interface IUnitItem {
    hp: number
    subHp(hp: number): void
    accept(visitor: IAttackVisitor): void
}
export class UnitItem extends Component implements IItem, IUnitItem {

    accept(visitor: IAttackVisitor) {
        visitor.visitUnitItem(this)
    }


}

4、开始使用 

export class UnitItem  extends Component implements IItem, IUnitItem {

    ad: number = 100;

    accept(visitor: IAttackVisitor) {
        visitor.visitUnitItem(this)
    }

    attack(unitItem: UnitItem) {
        let damage = this.ad
        let attackVisitor = new MonomerAttackVisitor(damage)
        unitItem.accept(attackVisitor)
    }

}

let unitItem001 = xhgame.itemFactory.createUnitItem('shibing_001')
let unitItem002 = xhgame.itemFactory.createUnitItem('shibing_002')

unitItem001.attack(unitItem002)

有了访问者模式后,伤害管理变得更简单,目前只是简单版本,后续还得继续完善

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

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

相关文章

遇到运维故障,有没有排查和解决故障的正确流程?

稳定是偶然,异常才是常态,用来标注IT运维工作再适合不过。 因为对于IT运维来说,工作最常遇到的就是不稳定性带来的各种故障,经常围绕发现故障、响应故障、定位故障、恢复故障这四大步。 故障处理是最心跳的事情,没有…

OpenCV-Python15:图像阈值处理

目录 目标 图像阈值及分割算法介绍 简单阈值算法 自适应阈值算法 Otsus 二值化算法 Otsus 二值化原理 目标 通过本文你将学到图像二值化、简单阈值处理、自适应阈值、Otsus 二值化等。将学习的函数有cv2.threshold,cv2.adaptiveThreshold 等。 图像阈值及分割算法介…

GZ029 智能电子产品设计与开发赛题第9套

2023年全国职业院校技能大赛高职组 “GZ029智能电子产品设计与开发”赛项赛卷九 题目:模拟工业传送带物品检测系统的设计与开发 1 竞赛任务 在智能电视机上播放工业传送带传输物品视频,模拟工业传送带物品检测系统(以下简称物品检测系统&…

训练效率提升40倍!开源图片生3D模型,Stable Zero123来啦

12月14日,著名生成式AI开源平台Stability.ai在官网开源了,图片生成高质量3D模型——Stable Zero123。 Stable Zero123是基于今年3月,丰田研究院和哥伦比亚大学联合开源的Zero123模型开发而成。主要通过更改渲染数据集和分数蒸馏对模型进行了…

leetcode -- 209 长度最小的子数组[滑动窗口/c++]

原题链接:209. 长度最小的子数组 - 力扣(LeetCode) 算法原理: 滑动窗口其实就是同向双指针,因为计算结果的单调性,在符合条件的情况下,左右指针不必往回回溯,而实现优化的效果。 滑…

分布式数据库-课程总结

文章目录 参考资源HBase列族数据库简介与关系数据库的对比存储对比操作语句表级别语句表数据语句 数据库的原理存储结构架构 MongoDB文档数据库简介与关系数据库的对比存储对比操作语句 见PPT 命名规范数据库的原理 Redis键值内存数据库Neo4j图数据库 参考资源 http://172.16.…

佛山数字孪生赋能工业智能制造,助力制造业企业数字化转型

佛山数字孪生赋能工业智能制造,助力制造业企业数字化转型。数字孪生驱动的仿真服务可以模拟产品的各种真实功能,为不同的用户切换不同的应用场景。产品介绍、咨询和体验服务都可以通过产品数字孪生来完成。产品数字孪生在交易时可以交付给客户。产品销售…

HTTP协议请求详解

✏️✏️✏️今天给大家分享的是 HTTP 请求部分的基础知识。 清风的CSDN博客 🛩️🛩️🛩️希望我的文章能对你有所帮助,有不足的地方还请各位看官多多指教,大家一起学习交流! ✈️✈️✈️动动你们发财的小…

大数据与人工智能——神经网络是如何工作的?

大数据与人工智能——神经网络是如何工作的? 我们习惯于去了解所使用工具、中间件的底层原理,本文则旨在帮助大家了解AI模型的底层机制,让大家在学习或应用各种大模型时更加得心应手,更加适合没有AI基础的小伙伴们。 一、GPT与神…

【改进YOLOv8】车辆测距预警系统:融合空间和通道重建卷积SCConv改进YOLOv8

1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 研究背景与意义: 随着交通工具的普及和道路交通的不断增加,车辆安全问题日益凸显。特别是在高速公路等高速道路上,车辆之间的距离和速度差异较…

可数集合(可列集合、可列无限集)

凡是和全体正整数所构成的集合对等的集合都称为可数集合、或者叫可列集合、可列无限集。 由于可以按大小顺序排成一个无穷序列,因此一个集合A是可数集合的充要条件为:A可以排成一个无穷序列 可数集合是无限集合。

服务器常用命令介绍和负载监控的工具插件推荐

先赞后看,养成习惯!!!❤️ ❤️ ❤️ 码字不易,如果喜欢可以关注我哦! 如果本篇文章对你有所启发,欢迎访问我的个人博客 命令 服务器相关 5个常用命令 top Top命令不仅显示了当前内核服务的…

解决固定资产盘点问题,易点易动来帮忙!

固定资产盘点是企业管理中不可或缺的环节,然而,很多企业在固定资产盘点方面面临一系列问题: 盘点过程繁琐:传统的手动盘点方式需要耗费大量人力和时间,容易出现疏漏和错误,效率低下; 数据记录不…

2022最新云存储网盘系统,文件分享系统与文件存储系统。

资源入口 2022 最新云存储网盘系统, 文件分享系统与文件存储系统。 测试环境:Apache MySQL5.6 PHP7.0 安装 PHP 扩展 exif、fileinfo 从 PHP 禁用函数中 删除 shell_exec、proc_open、putenv 这三个 PHP 函数 PS:整体还不错的系统,注意的…

年薪30w+”,程序员涨薪秘诀竟然是它!

“程序员能纯靠技术渡过中年危机吗?” 知乎上的这个提问,吸引了大批码农留言,热赞均表示“能,很难”。因为难逃这两种结局: • 没精力学习,技术一迭代,被淘汰。 • 有技术,90%公…

爱名网被评为“最佳安全保障注册服务机构”

12月12日,由中国互联网络信息中心主办,中国科学院计算机网络信息中心、中国工业互联网研究院、中国互联网协会联合主办,人民邮电报社承办的第四届中国互联网基础资源大会(CNIRC)在北京举办。 在本届大会上&#xff0c…

官宣 鸿雁成为2023汇丰世界羽联世界巡回赛总决赛官方供应商

全屋智能和羽毛球运动能擦出怎样的火花? 鸿雁给你答案! 12月13日,2023汇丰世界羽联世界巡回赛总决赛将在杭州举行。 鸿雁签约成为2023汇丰世界羽联世界巡回赛总决赛官方供应商,将携手世界羽联,为广大羽毛球爱好者们…

虹科技术 | IO-Link Wireless如何赋能工厂车间迈向无线自动化?

大规模定制、卓越运营和商业智能正在从根本上改变制造业,为了在竞争中立于不败之地,制造商需要更加灵活、通用、可扩展和具有成本效益的机器和生产线。随着制造商向工业 4.0 迈进,更好的适应性、更高的吞吐量和更短的停机时间是他们的共同要求…

electron中获取mac地址

electron中获取mac地址 引入为了方便做单点登录,我们往往需要使用某个唯一标识来标记客户端的设备,mac地址就是一个还不错的选择思路 我们可以使用Node.js的内置模块os,调用其中的networkInterfaces方法。该方法会返回一个包含网络接口信息…

Leetcode 51 N 皇后

题意理解: N皇后问题指的是在一个nn的棋盘上,防止皇后棋子,每行、每列、每45斜角只能有一个皇后存在。 这是一道困难的题:困难在于: 如何处理棋盘,如何表示棋子。 将期盼看作是2维数组,一行一行…