Unity 设计模式 之 行为型模式-【命令模式】【责任链模式】

Unity 设计模式 之 行为型模式-【命令模式】【责任链模式】

目录

Unity 设计模式 之 行为型模式-【命令模式】【责任链模式】

一、简单介绍

二、命令模式(Command Pattern)

1、什么时候使用命令模式

2、使用命令模式的好处

3、使用时的注意事项

三、在 Unity 中使用 命令模式

1、定义命令接口

2、定义接收者类

3、实现具体命令类

4、实现请求者类

5、创建游戏管理器

6、在 Unity 中测试

7、示例分析

四、责任链模式(Chain of Responsibility Pattern)

1、什么时候使用责任链模式

2、使用责任链模式的好处

4、使用时的注意事项

1、定义请求类

2、定义处理者接口

3、实现具体处理者类

4、创建请求者类

5、在 Unity 中测试

6、示例分析


一、简单介绍

设计模式 是指在软件开发中为解决常见问题而总结出的一套 可复用的解决方案。这些模式是经过长期实践证明有效的 编程经验总结,并可以在不同的项目中复用。设计模式并不是代码片段,而是对常见问题的 抽象解决方案,它提供了代码结构和模块间交互的一种设计思路,帮助开发者解决特定的设计问题。

设计模式的特点:

  1. 通用性:设计模式针对的是软件开发中常见的设计问题,适用于各种软件工程项目。
  2. 可复用性:设计模式可以在不同项目和环境下被重复使用,提高代码的可维护性和扩展性。
  3. 可扩展性:设计模式有助于让代码结构更加灵活,易于扩展和修改。
  4. 模块化:通过设计模式,可以减少代码的耦合性,增强模块间的独立性。
  5. 提高沟通效率:设计模式为开发者提供了一种通用的设计语言,使得团队成员能够快速理解并讨论设计方案。

二、命令模式(Command Pattern)

命令模式(Command Pattern)是一种行为型设计模式,它将请求封装成对象,从而使得可以使用不同的请求、队列或日志请求,以及支持可撤销的操作。命令模式通常包含四个主要角色:命令(Command)接收者(Receiver)请求者(Invoker)客户端(Client)。命令对象持有请求的详细信息,而接收者则执行这些请求。

1、什么时候使用命令模式

  1. 需要支持撤销/重做功能时:例如文本编辑器、绘图软件等需要频繁撤销和重做操作的应用。
  2. 需要将请求发送者与接收者解耦时:当请求的发送者和接收者之间的依赖关系需要降低时,可以使用命令模式。
  3. 需要记录请求日志时:当需要跟踪请求的历史以进行审计时,命令模式提供了一种优雅的解决方案。
  4. 需要实现异步请求时:可以将请求放入队列中,并在未来的某个时间点执行。

2、使用命令模式的好处

  1. 解耦请求与执行:命令模式将请求的发送者与接收者解耦,使得两者之间的关系更加灵活。
  2. 支持撤销操作:通过将操作封装成命令对象,可以轻松实现撤销(Undo)和重做(Redo)功能。
  3. 支持日志请求:可以将请求的历史记录进行存储,方便后续审计或分析。
  4. 可以实现队列请求:可以将命令对象放入队列中,按顺序执行或延迟执行。

3、使用时的注意事项

  1. 复杂性:命令模式可能会引入额外的复杂性,尤其是在命令对象数量较多时。需要确保命令的设计和管理是清晰的。
  2. 性能考虑:由于命令模式会创建多个命令对象,因此可能会增加内存开销。在性能敏感的场合,需要谨慎使用。
  3. 过度设计:对于简单的应用,使用命令模式可能会导致过度设计。在这些情况下,直接的方法调用可能更为简单和有效。

命令模式通过将请求封装为对象,实现了解耦、可撤销性和请求队列等功能。它适用于需要支持撤销、日志记录或异步请求的场合,但在设计时需要考虑到可能引入的复杂性和性能问题。

三、在 Unity 中使用 命令模式

在 Unity 中实现 命令模式 的示例,我们可以创建一个简单的场景,其中玩家可以使用命令来控制一个角色的移动和跳跃。这个示例将演示如何使用命令模式来处理这些操作。

使用命令模式控制角色

参考类图如下:

1、定义命令接口

首先,我们定义一个命令接口,描述执行命令的基本操作。

public interface ICommand
{void Execute();void Undo();
}
2、定义接收者类

接下来,我们定义一个接收者类,代表要执行命令的对象(如角色)。

using UnityEngine;public class PlayerCharacter : MonoBehaviour
{public void Move(Vector3 direction){transform.position += direction;Debug.Log($"Moved to: {transform.position}");}public void Jump(float height){transform.position += Vector3.up * height;Debug.Log($"Jumped to: {transform.position}");}
}
3、实现具体命令类

然后,我们实现具体的命令类,分别用于角色的移动和跳跃操作。

public class MoveCommand : ICommand
{private PlayerCharacter player;private Vector3 direction;public MoveCommand(PlayerCharacter player, Vector3 direction){this.player = player;this.direction = direction;}public void Execute(){player.Move(direction);}public void Undo(){player.Move(-direction); // 反向移动}
}public class JumpCommand : ICommand
{private PlayerCharacter player;private float height;public JumpCommand(PlayerCharacter player, float height){this.player = player;this.height = height;}public void Execute(){player.Jump(height);}public void Undo(){player.Move(-Vector3.up * height); // 反向跳跃}
}
4、实现请求者类

我们实现一个请求者类,用于调用命令。

using System.Collections.Generic;
using UnityEngine;public class CommandInvoker : MonoBehaviour
{private Stack<ICommand> commandHistory = new Stack<ICommand>();public void ExecuteCommand(ICommand command){command.Execute();commandHistory.Push(command);}public void UndoCommand(){if (commandHistory.Count > 0){ICommand lastCommand = commandHistory.Pop();lastCommand.Undo();}}
}
5、创建游戏管理器

最后,我们创建一个游戏管理器类,处理用户输入并执行命令。

using UnityEngine;public class GameManager : MonoBehaviour
{public PlayerCharacter playerCharacter;private CommandInvoker commandInvoker;void Start(){commandInvoker = gameObject.AddComponent<CommandInvoker>();}void Update(){if (Input.GetKeyDown(KeyCode.W)){var moveCommand = new MoveCommand(playerCharacter, Vector3.forward);commandInvoker.ExecuteCommand(moveCommand);}else if (Input.GetKeyDown(KeyCode.S)){var moveCommand = new MoveCommand(playerCharacter, Vector3.back);commandInvoker.ExecuteCommand(moveCommand);}else if (Input.GetKeyDown(KeyCode.Space)){var jumpCommand = new JumpCommand(playerCharacter, 2.0f);commandInvoker.ExecuteCommand(jumpCommand);}else if (Input.GetKeyDown(KeyCode.U)){commandInvoker.UndoCommand(); // 撤销上一个命令}}
}

6、在 Unity 中测试
  1. 创建一个带有 Collider 的 Cube 作为玩家角色,并附加 PlayerCharacter 脚本。
  2. 创建一个空的 GameObject,命名为 GameManager,并附加 GameManager 脚本。
  3. 运行游戏,使用 WS 键控制前后移动,使用 Space 键跳跃,使用 U 键撤销上一个命令。
7、示例分析
  • 命令(ICommand):定义执行和撤销的基本操作。
  • 接收者(PlayerCharacter):实现角色的具体操作。
  • 具体命令(MoveCommand 和 JumpCommand):封装角色的移动和跳跃逻辑。
  • 请求者(CommandInvoker):负责执行和撤销命令。

这个示例展示了如何在 Unity 中实现 命令模式,通过封装角色的操作为命令对象,使得用户能够灵活地控制角色,同时支持撤销操作。这种模式在需要记录和管理用户输入的场景中非常有用。

四、责任链模式(Chain of Responsibility Pattern)

责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它通过将请求的发送者和接收者解耦,使得多个对象都有机会处理请求。将这些对象连接成一条链,并沿着这条链传递请求,直到有对象处理它为止。该模式通常包含四个主要角色:请求(Request)处理者(Handler)具体处理者(ConcreteHandler)客户端(Client)

1、什么时候使用责任链模式

  1. 需要多个对象处理请求时:当一个请求需要被多个对象处理时,可以使用责任链模式。
  2. 处理请求的逻辑不明确时:当不确定哪个对象能够处理请求,或者希望多个对象有机会处理请求时,可以采用责任链模式。
  3. 请求处理的顺序可能变化时:如果请求的处理顺序可能发生变化,责任链模式可以提供更好的灵活性。
  4. 需要支持可扩展性时:当系统可能需要增加新处理者而不影响现有代码时,责任链模式是一个良好的选择。

2、使用责任链模式的好处

  1. 降低耦合度:发送者和接收者之间的关系被解耦,发送者不需要知道哪个具体的处理者来处理请求。
  2. 灵活性:可以动态添加或修改处理者,使得系统更加灵活和可扩展。
  3. 简化代码:通过将请求处理逻辑分散到多个处理者中,可以使得每个处理者的代码更加简洁,提高代码的可读性。
  4. 动态处理请求:可以通过改变责任链的结构和顺序来改变请求的处理方式。

4、使用时的注意事项

  1. 请求处理的顺序:责任链模式可能导致请求处理的顺序变得不易理解,因此需要明确责任链的顺序。
  2. 链的长度:过长的责任链可能导致性能下降,尤其是在链中的处理者都不处理请求的情况下。
  3. 处理失败的情况:需要设计好处理失败的情况,以免请求在链中被无效地传递。
  4. 调试困难:由于请求在链中可能被多个处理者处理,调试请求的流程可能会比较困难。

责任链模式通过将请求和处理者解耦,实现了请求的灵活处理和动态配置。适用于多个对象可能处理请求的场景,但在使用时需要考虑链的顺序、长度及调试难度等因素。

五、在 Unity 中使用 责任链模式

在 Unity 中实现 责任链模式 的示例,我们可以创建一个简单的游戏事件处理系统,其中多个处理者可以处理玩家的不同输入事件,例如跳跃、攻击和防御。这个示例将演示如何使用责任链模式来管理这些事件。

使用责任链模式处理玩家输入事件

参考类图如下:

1、定义请求类

首先,我们定义一个请求类,表示输入事件。

public class InputRequest
{public string Action { get; }public InputRequest(string action){Action = action;}
}

2、定义处理者接口

接下来,我们定义一个处理者接口,包含处理请求的方法。

public interface IInputHandler
{void SetNextHandler(IInputHandler nextHandler);void Handle(InputRequest request);
}

3、实现具体处理者类

然后,我们实现几个具体的处理者类,分别用于处理不同的输入事件。

using UnityEngine;public class JumpHandler : IInputHandler
{private IInputHandler nextHandler;public void SetNextHandler(IInputHandler nextHandler){this.nextHandler = nextHandler;}public void Handle(InputRequest request){if (request.Action == "Jump"){Debug.Log("Handling Jump action");}else{nextHandler?.Handle(request);}}
}public class AttackHandler : IInputHandler
{private IInputHandler nextHandler;public void SetNextHandler(IInputHandler nextHandler){this.nextHandler = nextHandler;}public void Handle(InputRequest request){if (request.Action == "Attack"){Debug.Log("Handling Attack action");}else{nextHandler?.Handle(request);}}
}public class DefendHandler : IInputHandler
{private IInputHandler nextHandler;public void SetNextHandler(IInputHandler nextHandler){this.nextHandler = nextHandler;}public void Handle(InputRequest request){if (request.Action == "Defend"){Debug.Log("Handling Defend action");}else{nextHandler?.Handle(request);}}
}

4、创建请求者类

我们创建一个请求者类,负责发送输入请求。

using UnityEngine;public class InputInvoker : MonoBehaviour
{private IInputHandler inputHandlerChain;void Start(){// 设置责任链JumpHandler jumpHandler = new JumpHandler();AttackHandler attackHandler = new AttackHandler();DefendHandler defendHandler = new DefendHandler();jumpHandler.SetNextHandler(attackHandler);attackHandler.SetNextHandler(defendHandler);inputHandlerChain = jumpHandler;}void Update(){if (Input.GetKeyDown(KeyCode.Space)){InputRequest jumpRequest = new InputRequest("Jump");inputHandlerChain.Handle(jumpRequest);}else if (Input.GetKeyDown(KeyCode.A)){InputRequest attackRequest = new InputRequest("Attack");inputHandlerChain.Handle(attackRequest);}else if (Input.GetKeyDown(KeyCode.D)){InputRequest defendRequest = new InputRequest("Defend");inputHandlerChain.Handle(defendRequest);}else if (Input.GetKeyDown(KeyCode.X)){InputRequest unknownRequest = new InputRequest("Unknown");inputHandlerChain.Handle(unknownRequest);}}
}

5、在 Unity 中测试

  1. 创建一个空的 GameObject,命名为 InputManager,并附加 InputInvoker 脚本。
  2. 运行游戏,按 SpaceADX 键测试不同的输入事件。

6、示例分析

  • 请求(InputRequest):封装输入事件的信息。
  • 处理者(IInputHandler 和具体处理者类):定义如何处理不同的输入事件,形成责任链。
  • 请求者(InputInvoker):负责接收输入并将其传递给责任链中的处理者。

这个示例展示了如何在 Unity 中实现 责任链模式,通过将输入事件处理逻辑分散到多个处理者中,使得系统灵活且可扩展。每个处理者负责处理特定的输入事件,并可以根据需求轻松添加或修改处理者。

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

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

相关文章

FME学习笔记

读取数据 方法一&#xff1a;add reader 通过读模块来进行数据的读取 方法二&#xff1a;FeatureReader Parameters 通过转换器来进行数据的读取 可以通过空间范围进行筛选 在FME中&#xff0c;所有数据处理都要用到的&#xff0c;绝对的重点&#xff1a;转换器&#xff…

【Python】PyCharm: 强大的 Python 开发环境

⭕️宇宙起点 &#x1f4e2; 引言&#x1f3ac; 什么是 PyCharm&#xff1f;&#x1f528; PyCharm 的核心特性1. 智能代码编辑2. 调试和测试3. 项目和代码结构导航4. 集成 AI 助手5. 远程开发6. 集成数据库7. 科学工具8. 版本控制集成9. Web 开发 &#x1f4e6; 安装 PyCharm&…

黑马智数Day4-1

新增月卡 配置路由完成跳转 {path: /cardAdd,component: () > import(/views/car/car-card/add-card) }<el-button type"primary" click"$router.push(/cardAdd)">添加月卡</el-button> 车辆信息表单验证 <el-form :model"carInf…

Bug:ThreadPoolTaskScheduler搭配CronTask完成定时任务,关闭scheduler后CronTask任务仍然执行?

【问题】执行下面代码后&#xff0c;关闭ThreadPoolTaskScheduler&#xff0c;CronTask仍然继续执行。 Configuration public class config {Beanpublic String getString() throws InterruptedException {Runnable runnable () -> {try {System.out.println("hello r…

《程序猿之设计模式实战 · 适配器模式》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

【后端开发】JavaEE初阶—线程安全问题与加锁原理(超详解)

前言&#xff1a; &#x1f308;上期博客&#xff1a;【后端开发】JavaEE初阶—Theard类及常见方法—线程的操作&#xff08;超详解&#xff09;-CSDN博客 &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f308;小编会在后端开发的学习中不…

关于javascript中防抖和节流的使用详解

防抖&#xff08;Debounce&#xff09;和节流&#xff08;Throttle&#xff09;是两种常见的优化技巧&#xff0c;通常用于控制函数在短时间内频繁触发的场景&#xff0c;尤其是在处理用户输入、滚动、窗口大小调整等事件时。它们的主要目的是减少不必要的函数调用&#xff0c;…

超详细超实用!!!AI编程之cursor编写设计模式开闭原则实例(四)

云风网 云风笔记 云风知识库 一、设计模式开闭原则定义 当应用的需求改变时&#xff0c;在不修改软件实体&#xff08;项目模块、类、接口方法&#xff09;的源代码或者二进制代码的前提下&#xff0c;可以扩展模块的功能&#xff0c;使其满足新的需求。即软件实体应当对扩展开…

【Linux】nginx连接前端项目

文章目录 一、项目编译1.编译文件2.dist文件 二、Linux nginx配置三、启动nginx 一、项目编译 1.编译文件 2.dist文件 二、Linux nginx配置 在Xshell软件中&#xff0c;点击CtrlAltF进入文件传输找到地址&#xff1a;/usr/local/nginx/html将dist文件传入 找到nginx.conf&…

git add成功后忘记commit的文件丢了?

本文目标&#xff1a;开发人员&#xff0c;在了解git fsck命令用法的条件下&#xff0c;进行git add成功但由于误操作导致丢失的文件找回&#xff0c;达到找回丢失文件的程度。 文章目录 1 痛点2 解决方案3 总结/练习 1 痛点 开发过程中&#xff0c;分支太多&#xff08;基线分…

CREO教程——2 绘制标准图纸

CREO教程——2 绘制标准图纸 说明&#xff1a;继承第一章设置好的配置文件&#xff0c;这一章进行学习分享如何定制自己的图纸图框&#xff0c;参考国家标准距&#xff0c;定制属于设计师或单位的通用图框。 1.设置工作目录 1.1设置工作目录 1.打开软件设置工作目录&#x…

u盘格式化怎么恢复数据?四款工具来救急!

工作中真的没少碰到过那些让人头疼的数据丢失问题&#xff0c;特别是U盘里的宝贝数据一不小心就“蒸发”了&#xff0c;简直让人欲哭无泪。不过别担心&#xff0c;我作为一个数据恢复的新手小白&#xff0c;最近可是亲测了几款超给力的数据恢复软件&#xff0c;今天就来跟大家分…

19c-TNS-12541: TNS:no listener

有套19c单机&#xff0c;没应用任何的补丁&#xff0c;使用lsnrctl status查看监听是异常的&#xff0c;但是lsnrctl start发现监听已运行&#xff0c;当前业务连接都正常&#xff0c; orcl:/home/oracledb> lsnrctl status LSNRCTL for Linux: Version 19.0.0.0.0 - Pro…

打造灵活DateTimePicker日期时间选择器组件:轻松实现时间的独立清除功能

element ui中日期和时间选择器&#xff08;DateTimePicker&#xff09;是一个常见且重要的组件。它允许用户轻松地选择日期和时间&#xff0c;极大地提升了用户体验。然而&#xff0c;在某些场景下&#xff0c;用户可能需要更细粒度的控制&#xff0c;例如单独清除已选择的时间…

下载与安装|Inventor 2025百度云资源分享附教程

如大家所了解的&#xff0c;Inventor是一款专业的三维可视化实体建模软件&#xff0c;主要用于各类二维机械制图、三维制图的设计和开发等操作&#xff0c;可以广泛地应用于零件设计、钣金设计、装配设计等领域。 不同领域的应用证明了Inventor具有强大的兼容性&#xff0c;基…

监控易监测对象及指标之:全面监控Oracle ODBC数据库

在数字化时代&#xff0c;数据库作为存储和管理企业核心数据的基石&#xff0c;其稳定性和性能直接关系到业务的连续性和效率。Oracle数据库以其强大的功能和稳定性&#xff0c;广泛应用于各行各业。为了确保Oracle数据库的稳定运行和高效性能&#xff0c;对其进行全面监控显得…

备战软考Day04-计算机网络

1、计算机网络的分类 2、七层网络体系结构 3、网络的设备与标准 4、TCP/IP协议族 TCP/IP作为Internet的核心协议&#xff0c;被广泛应用于局域网和广域网中&#xff0c;目前已成为事实上的国际标准 1、TCP/IP分层模型 TCP/IP协议是Internet的基础和核心&#xff0c;和OSI参考…

git命令将已经commit的代码push到其他分支

文章目录 一&#xff1a;对于多分支的代码库&#xff0c;将提交记录从一个分支转移到另一个分支是常见需求方法1&#xff1a;撤销commit操作方法2&#xff1a;实用命令git cherry-pick 来移动commit 二、不小心revert导致代码消失的问题 一&#xff1a;对于多分支的代码库&…

【Diffusion分割】FDiff-Fusion:基于模糊学习的去噪扩散融合网络

FDiff-Fusion: Denoising diffusion fusion network based on fuzzy learning for 3D medical image segmentation 摘要&#xff1a; 近年来&#xff0c;去噪扩散模型在图像分割建模中取得了令人瞩目的成就。凭借其强大的非线性建模能力和优越的泛化性能&#xff0c;去噪扩散模…

好用的todolist待办清单软件下载推荐

在快节奏的现代生活中&#xff0c;时间管理变得尤为重要。todolist待办清单管理软件&#xff0c;作为一种高效的任务管理工具&#xff0c;它帮助我们记录、跟踪和管理日常任务&#xff0c;从而提升个人效率。 在众多的待办软件中&#xff0c;敬业签以其出色的用户体验脱颖而出…