当前位置:网站首页>命令模式 - 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();
}
}
运行结果
解释
应用场景
- 当系统需要支持命令的撤销和恢复时,可以使用命令模式将命令存储起来。
- 当需要将操作进行参数化时,可以使用命令模式
- 需要将请求者和执行者分离解耦,命令模式可以使调用者不直接交互。
- 当系统需要执行一组操作时,命令模式可以定义宏命令来实现该功能。(可以使用组合模式来完成一个组合命令)
优缺点
优点
- 单一职责,调用者和实现者分离。
- 在添加新命令时,无需修改客户端,满足开闭原则
- 可实现撤销和恢复功能
- 可实现宏命令
缺点
- 产生大量命令类,使系统复杂
与其他模式关系
- 原型模式与命令模式搭配,可以对命令进行保存
- 组合模式与命令模式搭配,可以使用组合命令
- 策略模式和命令模式的不同,两者都是通过参数化对象。策略模式看重的上下文的算法切换,描述的是不同方式执行目的。命令模式则是将操作参数化为命令。通过参数化命令,来延时调用,保存记录,发送命令等。
。。。由于还没学完设计模式,其他模式先不比较,先写到着
边栏推荐
- mysql使用笔记一
- LeetCode 1155. 掷骰子的N种方法 每日一题
- 让保险更“保险”!麒麟信安一云多芯云桌面中标中国人寿, 助力金融保险信息技术创新发展
- Nerf: the ultimate replacement for deepfake?
- LeetCode 213. Home raiding II daily question
- Proxmox VE重装后,如何无损挂载原有的数据盘?
- Repair method of firewall system crash and file loss, material cost 0 yuan
- LeetCode 300. 最长递增子序列 每日一题
- skimage学习(3)——Gamma 和 log对比度调整、直方图均衡、为灰度图像着色
- LeetCode 1477. Find two subarrays with sum as the target value and no overlap
猜你喜欢
Matplotlib绘制三维图形
How to add aplayer music player in blog
QT picture background color pixel processing method
Process from creation to encapsulation of custom controls in QT to toolbar (I): creation of custom controls
让保险更“保险”!麒麟信安一云多芯云桌面中标中国人寿, 助力金融保险信息技术创新发展
Reflections on "product managers must read: five classic innovative thinking models"
Mrs offline data analysis: process OBS data through Flink job
Sator launched Web3 game "satorspace" and launched hoobi
PLC:自动纠正数据集噪声,来洗洗数据集吧 | ICLR 2021 Spotlight
Matplotlib绘图界面设置
随机推荐
麒麟信安中标国网新一代调度项目!
What is cloud computing?
【Seaborn】组合图表、多子图的实现
【黄啊码】为什么我建议您选择go,而不选择php?
LeetCode 1049. Weight of the last stone II daily question
LeetCode 1981. 最小化目标值与所选元素的差 每日一题
Sator a lancé le jeu web 3 "satorspace" et a lancé huobi
Sator推出Web3游戏“Satorspace” ,并上线Huobi
Module VI
Mrs offline data analysis: process OBS data through Flink job
LeetCode 1477. Find two subarrays with sum as the target value and no overlap
麒麟信安携异构融合云金融信创解决方案亮相第十五届湖南地区金融科技交流会
Linux 安装mysql8.X超详细图文教程
Pycharm IDE下载
科普达人丨一文弄懂什么是云计算?
Rpcms method of obtaining articles under the specified classification
LeetCode 1696. 跳跃游戏 VI 每日一题
QT video transmission
Seaborn数据可视化
How to add aplayer music player in blog