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

ops/2024/10/20 14:17:27/

Unity%20%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20%E4%B9%8B%20%E8%A1%8C%E4%B8%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F-%E3%80%90%E5%91%BD%E4%BB%A4%E6%A8%A1%E5%BC%8F%E3%80%91%E3%80%90%E8%B4%A3%E4%BB%BB%E9%93%BE%E6%A8%A1%E5%BC%8F%E3%80%91">Unity 设计模式 之 行为型模式-【命令模式】【责任链模式

目录

Unity%20%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20%E4%B9%8B%20%E8%A1%8C%E4%B8%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F-%E3%80%90%E5%91%BD%E4%BB%A4%E6%A8%A1%E5%BC%8F%E3%80%91%E3%80%90%E8%B4%A3%E4%BB%BB%E9%93%BE%E6%A8%A1%E5%BC%8F%E3%80%91-toc" style="margin-left:0px;">Unity 设计模式 之 行为型模式-【命令模式】【责任链模式

一、简单介绍

二、命令模式(Command Pattern)

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

2、使用命令模式的好处

3、使用时的注意事项

Unity%20%E4%B8%AD%E4%BD%BF%E7%94%A8%20%E5%91%BD%E4%BB%A4%E6%A8%A1%E5%BC%8F-toc" style="margin-left:40px;">三、在 Unity 中使用 命令模式

1、定义命令接口

2、定义接收者类

3、实现具体命令类

4、实现请求者类

5、创建游戏管理器

Unity%20%E4%B8%AD%E6%B5%8B%E8%AF%95-toc" style="margin-left:120px;">6、在 Unity 中测试

7、示例分析

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

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

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

4、使用时的注意事项

1、定义请求类

2、定义处理者接口

3、实现具体处理者类

4、创建请求者类

Unity%20%E4%B8%AD%E6%B5%8B%E8%AF%95-toc" style="margin-left:120px;">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%20%E4%B8%AD%E4%BD%BF%E7%94%A8%20%E5%91%BD%E4%BB%A4%E6%A8%A1%E5%BC%8F">三、在 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(); // 撤销上一个命令}}
}

Unity%20%E4%B8%AD%E6%B5%8B%E8%AF%95">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);}}
}

Unity%20%E4%B8%AD%E6%B5%8B%E8%AF%95">5、在 Unity 中测试

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

6、示例分析

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

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


http://www.ppmy.cn/ops/118376.html

相关文章

OpenMV与STM32通信全面指南

目录 引言 一、OpenMV和STM32简介 1.1 OpenMV简介 1.2 STM32简介 二、通信协议概述 三、硬件连接 3.1 硬件准备 3.2 引脚连接 四、软件环境搭建 4.1 OpenMV IDE安装 4.2 STM32开发环境 五、UART通信实现 5.1 OpenMV端编程 5.2 STM32端编程 六、SPI通信实现 6.1 …

低代码可视化-UniApp二维码可视化-代码生成器

市面上提供了各种各样的二维码组件&#xff0c;做了一简单的uniapp二维码组件&#xff0c;二维码实现依赖davidshimjs/qrcodejs。 组件特点 跨浏览器支持&#xff1a;利用Canvas元素实现二维码的跨浏览器兼容性&#xff0c;兼容微信小程序、h5、app。 无依赖性&#xff1a;QR…

什么是 JWT?它是如何工作的?

松哥最近辅导了几个小伙伴秋招&#xff0c;有小伙伴在面小红书时遇到这个问题&#xff0c;这个问题想回答全面还是有些挑战&#xff0c;松哥结合之前的一篇旧文和大伙一起来聊聊。 一 无状态登录 1.1 什么是有状态 有状态服务&#xff0c;即服务端需要记录每次会话的客户端信…

高刷显示器哪个好?540Hz才有资格称高刷

高刷显示器哪个好&#xff1f;说实话&#xff0c;540Hz这些才能成为高刷显示器&#xff0c;什么200,240的&#xff0c;都不够高&#xff0c;什么是从容&#xff0c;有我不用才叫从容。下面我们一起来看看540Hz的高刷显示器都有哪些吧&#xff01; 1.高刷显示器哪个好 - 蚂蚁电…

力扣题解2286

大家好&#xff0c;欢迎来到无限大的频道 今天继续给大家带来力扣题解 题目描述&#xff08;困难&#xff09;&#xff1a; 以组为单位订音乐会的门票 一个音乐会总共有 n 排座位&#xff0c;编号从 0 到 n - 1 &#xff0c;每一排有 m 个座椅&#xff0c;编号为 0 到 m - 1…

Leetcode 213. 打家劫舍 II

原题链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋&#xff0c;每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 &#xff0c;这意味着第一个房屋和最后一个房屋是紧挨着的。同时&#xff0c;相邻的…

【含文档】基于Springboot+微信小程序 的高中信息技术课程在线测试系统(含源码+数据库+lw)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 当游客…

每天学习一个技术栈 ——【Django Channels】篇(1)

在当今快速发展的技术领域&#xff0c;掌握多种技术栈已经成为开发者提升竞争力的关键。随着实时应用需求的不断增加&#xff0c;如何高效地处理并发请求和实时通信变得尤为重要。在众多解决方案中&#xff0c;Django Channels作为Django框架的强大扩展&#xff0c;能够轻松实现…