当前位置:网站首页>命令模式 - Unity

命令模式 - Unity

2022-07-07 15:38:00 有趣就行

命令模式

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

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

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

结构

在这里插入图片描述
说明

  • 抽象命令(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();
        }
    }

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

解释
在这里插入图片描述

应用场景

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

优缺点

优点

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

缺点

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

与其他模式关系

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

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

​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

原网站

版权声明
本文为[有趣就行]所创,转载请带上原文链接,感谢
https://blog.csdn.net/m0_52361859/article/details/125377332