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

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)

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

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

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

相关文章

Dockerfile详解#如何编写自己的Dockerfile

文章目录 前言编写规则指令详解FROM:基础镜像LABEL:镜像描述信息MAINTAINER:添加作者信息COPY:从宿主机复制文件到镜像中ADD:从宿主机复制文件到镜像中WORKDIR:设置工作目录 前言 Dockerfile是编写docker镜…

Spring AOP从入门到精通

目录 1. AOP的演化过程 1. 代理模式 2. 动态代理 2.1 JDK动态代理 2.2 Cglib动态代理 3. Spring模式 3.1 ProxyFactory 3.2 ProxyFactoryBean 3.3 AbstractAutoProxyCreator 2. Spring AOP抽象 1. 核心术语 1.1 连接点(JoinPoint) 1.2 切点(Pointcut) 1.3 增强(Ad…

JAVA 多线程并发(一)

1.JAVA 并发知识库 2.JAVA 线程实现/创建方式 2.1. 继承 Thread 类 Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过 Thread 类的 start()实例方法。start()方法是一个 native 方法,它将启动一个新线…

使用JMeter创建数据库测试

好吧!我一直觉得我不聪明,所以,我用最详细,最明了的方式来书写这个文章。我相信,我能明白的,你们一定能明白。 我的环境:MySQL:mysql-essential-5.1.51-win32 jdbc驱动&#xff1a…

支持生成接口文档!Apipost IDEA插件使用体验

前言 Idea 是一款功能强大的集成开发环境(IDE),它可以帮助开发人员更加高效地编写、调试和部署软件应用程序,Idea 还具有许多插件和扩展,可以根据开发人员的需要进行定制和扩展,从而提高开发效率,今天我们就来介绍一款…

交易历史记录20231207 记录

昨日回顾&#xff1a; select top 10000 * from dbo.全部&#xff21;股20231207_ALL where 连板天 >1 and DDE大单净量>0 and DDE散户数量<0 and RSI> 80 and 五指标共振>0 and 涨停基因>20 and CONVERT(datetime,最后涨停时间,120) <CONVERT(d…

富时中国A50指数暴跌

近年来&#xff0c;中国股市的波动一直备受关注&#xff0c;而富时中国A50指数更是其中一项备受瞩目的指标之一。然而&#xff0c;近期却出现了一场引人瞩目的暴跌&#xff0c;引发了广泛的关注和讨论。 富时中国A50指数简介 富时中国A50指数&#xff0c;作为富时罗素指数系列…

Linux:缓冲区的概念理解

文章目录 缓冲区什么是缓冲区&#xff1f;缓冲区的意义是什么&#xff1f;缓冲区的刷新方式 理解缓冲区用户缓冲区和内核缓冲区缓冲区在哪里&#xff1f; 本篇主要总结的是关于缓冲区的概念理解&#xff0c;以及再次基础上对文件的常用接口进行一定程度的封装 缓冲区 什么是缓…

linux文件查找

grep: 文件内容过滤 [rootzaotounan ~]# grep 文件内容 路径 #从某个路径下的文件中过滤拥有文件内容的字段 ​ [rootzaotounan ~]# grep -r #递归查找 查找命令配置文件位置 查找命令位置 [rootzaotounan ~]# which 命令名 ​ 查找配置文件位置 [rootzaotounan ~]# wherei…

el-select的多选multible带全选组件二次封装(vue2,elementUI)

1.需求 Select 选择器 多选需要增加 全选 和 取消全选 功能&#xff0c;前端框架为vue2&#xff0c;UI组件为elementUI。 2. 代码 html部分 <template><el-tooltip effect"dark" :disabled"defaultValue.length < 0" :content"defaul…

应用程序中实现用户隐私合规和数据保护合规的处理方案及建议

随着移动互联网的发展&#xff0c;用户隐私合规和数据保护合规已经成为应用开发过程中不可忽视的重要环节。为了帮助开发者实现隐私和数据保护合规&#xff0c;本文将介绍一些处理方案和建议。 图片来源&#xff1a;应用程序中实现用户隐私合规和数据保护合规的处理方案及建议 …

IOday5作业

使用两个线程完成两个文件的拷贝&#xff0c;分支线程1完成前一半内容拷贝&#xff0c;分支线程2完成后一半内容的拷贝&#xff0c;主线程完成资源的回收 #include<myhead.h> //定义结构体 struct file {const char* srcfile;//背拷贝文件路径const char* destfile;//拷…

LeetCode Hot100 200.岛屿数量

题目&#xff1a; 给你一个由 1&#xff08;陆地&#xff09;和 0&#xff08;水&#xff09;组成的的二维网格&#xff0c;请你计算网格中岛屿的数量。 岛屿总是被水包围&#xff0c;并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 此外&#xff0c;你可以…

方法-TensorBoard如何在PC端远程可视化服务器的训练结果

本专栏为深度学习的一些技巧,方法和实验测试,偏向于实际应用,后续不断更新,感兴趣童鞋可关,方便后续推送 简介 TensorBoard 是 TensorFlow 的可视化工具&#xff0c;可以帮助用户实时监控深度神经网络的训练过程,基于PyTorch的训练过程也可以可视化。假设我在服务器上进行模型…

名创优品股份有限公司

用户简介 名创优品股份有限公司&#xff08;以下简称&#xff1a;名创优品&#xff09;奉行“简约、自然、富质感”的生活哲学和“回归自然&#xff0c;还原产品本质”的设计主张&#xff0c;秉承“尊重消费主义者”的品牌精神&#xff0c;致力于为全球消费者提供真正“优质、…

2-3、LOOP和CX

语雀原文链接 文章目录 1、loop示例pg 1、loop示例 编写loop.asm assume cs:code code segmentmov ax,2mov cx,11 s: add ax,axloop smov ax,4c00hint 21h code ends end编译链接 C:\>masm c:\loop; Microsoft (R) Macro Assembler Version 5.00 Copyright (C) Microsof…

2-4、DEBUG和源程序区别

语雀原文链接 文章目录 1、DEBUG 和 汇编编译器MASM区别1&#xff1a;默认进制不同区别2&#xff1a;[地址]示例1&#xff1a;debug示例2&#xff1a;[0]示例3&#xff1a;[寄存器]示例4&#xff1a;ds:[0]小结 区别3&#xff1a;源程序数据不能以字母开头 1、DEBUG 和 汇编编…

盒马补贴量价-2021KDD

概述&#xff1a; 电商商品定价三个关键问题&#xff1a; 在只有观测数据的时候&#xff0c;怎么构建价格弹性&#xff0c;现在来看这就是一个反事实推断的问题&#xff0c;不仅是如何做的问题&#xff0c;还有如何评估的问题。长周期的规划决策问题怎么建模 & 求解&#…

再识二叉树

1. 二叉树的存储 二叉树的存储结构分为&#xff1a;顺序存储和类似于链表的链式存储。 其中二叉树的链式存储是通过一个一个的节点引用起来的&#xff0c;常见的表示方式有二叉和三叉表示方式&#xff08;这里本主主要讲的是链式存储&#xff09;&#xff0c;具体代码如下&…

「海蓝色」海关可视化监管平台,助力海关体系实现规范化程序管理

海关监管是国家对进出境货物、旅客和邮件进行检查和控制的重要机构&#xff0c;其职责是保障国家的安全和经济利益。海关监管的核心目标是防止非法进出境活动&#xff0c;包括走私、偷逃税款等行为。海关监管通过检查和核实货物的品质、数量和价值&#xff0c;确保货物符合相关…