当前位置:网站首页>命令模式 - 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();
}
}
运行结果
解释
应用场景
- 当系统需要支持命令的撤销和恢复时,可以使用命令模式将命令存储起来。
- 当需要将操作进行参数化时,可以使用命令模式
- 需要将请求者和执行者分离解耦,命令模式可以使调用者不直接交互。
- 当系统需要执行一组操作时,命令模式可以定义宏命令来实现该功能。(可以使用组合模式来完成一个组合命令)
优缺点
优点
- 单一职责,调用者和实现者分离。
- 在添加新命令时,无需修改客户端,满足开闭原则
- 可实现撤销和恢复功能
- 可实现宏命令
缺点
- 产生大量命令类,使系统复杂
与其他模式关系
- 原型模式与命令模式搭配,可以对命令进行保存
- 组合模式与命令模式搭配,可以使用组合命令
- 策略模式和命令模式的不同,两者都是通过参数化对象。策略模式看重的上下文的算法切换,描述的是不同方式执行目的。命令模式则是将操作参数化为命令。通过参数化命令,来延时调用,保存记录,发送命令等。
。。。由于还没学完设计模式,其他模式先不比较,先写到着
边栏推荐
- 《产品经理必读:五种经典的创新思维模型》的读后感
- The server is completely broken and cannot be repaired. How to use backup to restore it into a virtual machine without damage?
- LeetCode 312. Poke balloon daily
- LeetCode 1654. 到家的最少跳跃次数 每日一题
- skimage学习(3)——使灰度滤镜适应 RGB 图像、免疫组化染色分离颜色、过滤区域最大值
- 麒麟信安云平台全新升级!
- Flask搭建api服务
- From Devops to mlops: how do it tools evolve to AI tools?
- MRS离线数据分析:通过Flink作业处理OBS数据
- 【饭谈】如何设计好一款测试平台?
猜你喜欢

PLC:自动纠正数据集噪声,来洗洗数据集吧 | ICLR 2021 Spotlight

Process from creation to encapsulation of custom controls in QT to toolbar (I): creation of custom controls

Sator launched Web3 game "satorspace" and launched hoobi

麒麟信安携异构融合云金融信创解决方案亮相第十五届湖南地区金融科技交流会

With the latest Alibaba P7 technology system, mom doesn't have to worry about me looking for a job anymore

鲲鹏开发者峰会2022 | 麒麟信安携手鲲鹏共筑计算产业新生态

Matplotlib绘图界面设置

The process of creating custom controls in QT to encapsulating them into toolbars (II): encapsulating custom controls into toolbars

Seaborn data visualization
![[image sensor] correlated double sampling CDs](/img/1c/3a641ad47ff91536db602dedc82705.png)
[image sensor] correlated double sampling CDs
随机推荐
LeetCode 1477. 找两个和为目标值且不重叠的子数组 每日一题
skimage学习(3)——使灰度滤镜适应 RGB 图像、免疫组化染色分离颜色、过滤区域最大值
Biped robot controlled by Arduino
QT中自定义控件的创建到封装到工具栏过程(一):自定义控件的创建
mysql实现两个字段合并成一个字段查询
Sator推出Web3游戏“Satorspace” ,并上线Huobi
LeetCode 1981. Minimize the difference between the target value and the selected element one question per day
The mail server is listed in the blacklist. How to unblock it quickly?
自定义View必备知识,Android研发岗必问30+道高级面试题
[Seaborn] implementation of combined charts and multi subgraphs
Problems encountered in Jenkins' release of H5 developed by uniapp
LeetCode 1155. N ways to roll dice one question per day
赋能智慧电力建设 | 麒麟信安高可用集群管理系统,保障用户关键业务连续性
[fan Tan] after the arrival of Web3.0, where should testers go? (ten predictions and suggestions)
Test case management tool recommendation
The server is completely broken and cannot be repaired. How to use backup to restore it into a virtual machine without damage?
Seaborn data visualization
[video / audio data processing] Shanghai daoning brings you elecard download, trial and tutorial
Flash build API service
第九届 蓝桥杯 决赛 交换次数
