命令模式 - Unity

文章目录

  • 命令模式
    • 结构
    • 实现
    • 应用场景
    • 优缺点
    • 与其他模式关系

命令模式

命令模式是对象行为型模式,通过将请求者和执行者之间的请求抽离出来封装成一个对象,从而使发出请求的职责和执行请求的职责分离。请求者和执行者通过命令对象来进行沟通,这样使得对命令对象进行存储,管理,传递等。

命令模式是行为型模式中十分常用的模式,在游戏开发中也十分重要。通过将大量的命令进行管理和存储,保证了命令的顺序和统一。在使用软件时,我们经常会用到撤销这个操作,软件是如何执行撤销的,显然是将你的每一步操作进行保存,通过读取每步操作进行还原。而这就是命令模式,其中每步操作就是一个命令。

例如,遥控器和电视机,遥控器是请求者,电视机是执行者,谁是命令呢,遥控器上的按钮就是命令。餐厅中一般会有一个菜单供顾客选择,服务员进行将菜单转递给厨师,厨师负责烧菜。其中菜单中选择的菜就是一个命令,服务员作为中间人(属于调用者),他不负责执行,负责传递和管理这些菜单(命令),而菜单最后会被厨师拿到,并开始执行任务(厨师就是执行者)。多么生动形象,这就是生活里的智慧。

结构

在这里插入图片描述
说明

  • 抽象命令(Command) - 一般只有一个执行命令方法的接口(也可以有撤销方法,当然如果需要的话)
  • 具体命令 (Concrete Command)- 抽象命令的具体实现类,本身不实现方法,作为实现者参数的载体,将工作委托给实现者。
  • 实现者(Receiver)- 实现业务逻辑的类,是最终完成方法的类,几乎所有对象都可以作为实现者。
  • 调用者(Invoker)- 有储存多个命令对象,不直接访问实现者,通过命令对象执行请求。(本身即不创建命令对象,也不执行业务方法),主要是对命令对象的管理,储存,执行等。

实现

这个例子可能比较繁琐,但大体并没有太复杂。
主要是实现 恢复 和 撤销 等功能。
通过命令驱动UI移动

移动枚举

    public enum MoveDirection{Left,Right,Up,Down,}

UI 移动 - 实现者(单例模式,只需要一份即可)

    public class UIMove{private static UIMove _instacne = new UIMove();//移动距离private const float MoveDistance = 100f;public static UIMove Instacne => _instacne;private UIMove() { }//移动public void Move(RectTransform rt, MoveDirection direction){Vector3 position = rt.position;rt.position = direction switch{MoveDirection.Left => new Vector3(position.x - MoveDistance, position.y, position.z),MoveDirection.Right => new Vector3(position.x + MoveDistance, position.y, position.z),MoveDirection.Up => new Vector3(position.x, position.y + MoveDistance, position.z),MoveDirection.Down => new Vector3(position.x, position.y - MoveDistance, position.z),_ => throw new ArgumentOutOfRangeException(nameof(direction)),};}}

命令接口 - 抽象命令

    public interface ICommand{// 执行void Execute();// 撤销void Undo();// 命令转换字符串string ToString();}

移动命令 - 具体命令

    public class MoveCommand : ICommand{//保持唯一的一份private readonly UIMove _receiver;private readonly RectTransform _transform;private readonly MoveDirection _direction;public MoveCommand(MoveDirection direction, RectTransform transform){_direction = direction;_transform = transform;_receiver = UIMove.Instacne; //单例实例}public void Execute() => _receiver.Move(_transform, _direction);public void Undo(){switch (_direction){case MoveDirection.Left:_receiver.Move(_transform, MoveDirection.Right);break;case MoveDirection.Right:_receiver.Move(_transform, MoveDirection.Left);break;case MoveDirection.Up:_receiver.Move(_transform, MoveDirection.Down);break;case MoveDirection.Down:_receiver.Move(_transform, MoveDirection.Up);break;default:throw new ArgumentOutOfRangeException(nameof(_direction));}}public override string ToString(){return _direction.ToString();}}

UI 移动调用 - 调用者

    public class UIMoveInvoker{private List<ICommand> _commands;private int _index = -1;public int Index => _index;public List<ICommand> CommandList => _commands;public UIMoveInvoker(){_commands = new List<ICommand>();}//执行移动命令public void Move(ICommand command){command.Execute();//如果命令不是最后一个,那就删除后面所有命令if (_index < _commands.Count - 1){_commands.RemoveRange(_index + 1, _commands.Count - _index - 1);}_commands.Add(command);_index++;}//恢复操作public void ReDo(){if (_index >= _commands.Count - 1) return;_index++;_commands[_index].Execute();}//撤销操作 public void UnDo(){if (_index < 0) return;_commands[_index].Undo();_index--;}}

客户端(使用 GUI 作为可视化处理)

    public class CommandExample : MonoBehaviour{[SerializeField] private GUIStyle style;[SerializeField] private RectTransform rectTransform;private UIMoveInvoker _invoker;private void Awake(){_invoker = new UIMoveInvoker();}private void Update(){if (Input.GetKeyDown(KeyCode.W)) _invoker.Move(new MoveCommand(MoveDirection.Up, rectTransform));else if (Input.GetKeyDown(KeyCode.S)) _invoker.Move(new MoveCommand(MoveDirection.Down, rectTransform));else if (Input.GetKeyDown(KeyCode.A)) _invoker.Move(new MoveCommand(MoveDirection.Left, rectTransform));else if (Input.GetKeyDown(KeyCode.D)) _invoker.Move(new MoveCommand(MoveDirection.Right, rectTransform));else if (Input.GetKeyDown(KeyCode.E)) _invoker.UnDo();else if (Input.GetKeyDown(KeyCode.R)) _invoker.ReDo();}private void OnGUI(){var list = _invoker.CommandList;var index = _invoker.Index;GUILayout.BeginVertical();GUILayout.Label((index == -1 ? ">>  " : "") + "Start", style);for (int i = 0; i < list.Count; i++){if (i == index) GUILayout.Label(">>  " + list[i].ToString(), style);else GUILayout.Label(list[i].ToString(), style);}GUILayout.EndVertical();}}

运行结果
在这里插入图片描述

解释
在这里插入图片描述

应用场景

  • 当系统需要支持命令的撤销和恢复时,可以使用命令模式将命令存储起来。
  • 当需要将操作进行参数化时,可以使用命令模式
  • 需要将请求者和执行者分离解耦,命令模式可以使调用者不直接交互。
  • 当系统需要执行一组操作时,命令模式可以定义宏命令来实现该功能。(可以使用组合模式来完成一个组合命令)

优缺点

优点

  • 单一职责,调用者和实现者分离。
  • 在添加新命令时,无需修改客户端,满足开闭原则
  • 可实现撤销和恢复功能
  • 可实现宏命令

缺点

  • 产生大量命令类,使系统复杂

与其他模式关系

  • 原型模式与命令模式搭配,可以对命令进行保存
  • 组合模式与命令模式搭配,可以使用组合命令
  • 策略模式和命令模式的不同,两者都是通过参数化对象。策略模式看重的上下文的算法切换,描述的是不同方式执行目的。命令模式则是将操作参数化为命令。通过参数化命令,来延时调用,保存记录,发送命令等。

。。。由于还没学完设计模式,其他模式先不比较,先写到着

​🐹​​🐹​​🐹​​🐹​​🐹​​🐹​​🐹​​🐹​​🐹​​🐹​​🐹​​🐹​​🐹​​🐹​​🐹​​🐹​​🐹​​🐹​​🐹​​🐹​


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部