命令模式
定义
命令模式(Command Pattern)——是将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
解释
正常来说,我们将一步操作分为调用方和执行方,这样二者各司其职,符合单一职责原则。
例如:将一个方块左移一步,执行方有一个Move方法,调用方传递参数并调用执行方的方法去执行。
命令模式是将调用方和执行方解藕,将:
调用方——>执行方
变化为
调用方——>命令——>执行方
这样,通过封装一个命令,我们解藕了调用方和执行方,并且可以保存下来命令,完成回退和复盘等等操作。
UML
成员
Invoker:调用方,负责命令的调用
Command:命令
Receiver:执行方,负责执行操作
ConcreteCommand:具体命令
ConcreteReceiver:具体执行者
代码
首先定义命令抽象和执行者抽象
namespace DesignModel.CommandPattern
{
//命令抽象
public interface ICommand
{
void Excute();
}
//执行抽象
public interface IReceiver
{
void DoSomething();
}
}
调用者实现
namespace DesignModel.CommandPattern
{
public class Invoker
{
ICommand command;
public void SetCommand(ICommand command)
{
this.command = command;
}
public void Action()
{
command.Excute();
}
}
}
具体的执行者实现
namespace DesignModel.CommandPattern
{
public class ConcreteReceiver : IReceiver
{
public void DoSomething()
{
Debug.Log("do something !");
}
}
}
具体的命令实现
namespace DesignModel.CommandPattern
{
public class ConcreteCommond : ICommand
{
private IReceiver receiver;
public ConcreteCommond(IReceiver receiver)
{
this.receiver = receiver;
}
public void Excute()
{
receiver.DoSomething();
}
}
}
客户端调用
using DesignModel.CommandPattern;
public class Client_CommandPattern_Example1 : MonoBehaviour
{
void Start ()
{
//创建一个命令的调用者(管理者)
Invoker invoker = new Invoker();
//创建一个命令的执行者
ConcreteReceiver receiver = new ConcreteReceiver();
//创建一条命令,并传递执行者
ICommand command = new ConcreteCommond(receiver);
//将命令交给调用者
invoker.SetCommand(command);
//调用者调用命令
invoker.Action();
}
}
命令模式的要点
优点
1.命令模式将调用者和执行者解藕,使得调用者不需要执行者如何去执行,而只需要调用相应的命令即可;
2.命令模式将调用过程封装为命令,使得该过程可以回溯;
3.方便扩展新的命令
缺点
1.当命令很多时,每条命令都必须创建一个子类,即便是很简单的命令,增加了系统的复杂性和实现难度;
2.新增具体命令时:
如果传递具体的执行者,则可能要修改具体执行者的代码,违反了开放封闭原则
如果传递的是抽象的执行者,则可能要修改抽象执行者,违反了开放封闭原则
注意:
1.命令持有具体的执行者还是抽象的执行者,取决于执行者是否使用模板方法,也就是
抽象:该命令适用于这一类的执行者
具体:该命令适用于这一个执行者
额外的例子
用命令模式完成一个W、A、S、D让物体移动,退格键回退操作的例子
命令和执行者抽象
namespace DesignModel.CommandPattern
{
//命令抽象,包含执行和逆执行方法
public interface IBaseCommand
{
void Execute();
void Reexecute();
}
//执行者抽象 这里抽象不做任何事情
public interface IBaseReceiver
{
}
}
命令管理者(调用者)实现,这里保存了一个已执行的命令的队列,以及当前被填充的命令,用于执行当前命令和回退命令
namespace DesignModel.CommandPattern
{
public class CommandManager
{
private IBaseCommand currentCommand; //当前命令
List<IBaseCommand> doneCommands = new List<IBaseCommand>(); //已完成的命令队列
/// <summary>
/// 设置命令
/// </summary>
/// <param name="command">命令</param>
public void SetCommand(IBaseCommand command)
{
currentCommand = command;
}
/// <summary>
/// 执行当前命令
/// </summary>
public void DoCommand()
{
currentCommand.Execute();
doneCommands.Add(currentCommand);
}
/// <summary>
/// 回退执行命令
/// </summary>
public void UnDoCommand()
{
IBaseCommand lastCommand;
if (doneCommands.Count > 0)
{
lastCommand = doneCommands[doneCommands.Count - 1];
lastCommand.Reexecute();
doneCommands.Remove(lastCommand);
}
}
}
}
一个具体的执行者,目前只有移动的方法
namespace DesignModel.CommandPattern
{
public class ObjController : MonoBehaviour, IBaseReceiver
{
public void Move(Vector3 move)
{
this.transform.position += move;
}
}
}
移动命令
namespace DesignModel.CommandPattern
{
public class MoveCommand : IBaseCommand
{
// 一个具体的命令执行者
private ObjController receiver;
private Vector3 move;
public MoveCommand(ObjController receiver, Vector3 move)
{
this.receiver = receiver;
this.move = move;
}
public void Execute()
{
Debug.Log("Move : " + move);
receiver.Move(move);
}
public void Reexecute()
{
Debug.Log("ReMove : " + move);
receiver.Move(-move);
}
}
}
一个按键监听类
public class KeyEventCtr : MonoBehaviour
{
public delegate void UpKeyPressHandler();
public event UpKeyPressHandler upKeyPressEvent;
public delegate void DownKeyPressHandler();
public event DownKeyPressHandler downKeyPressEevent;
public delegate void LeftKeyPressHandler();
public event LeftKeyPressHandler leftKeyPressEvent;
public delegate void RightKeyPressHandler();
public event RightKeyPressHandler rightKeyPressEvent;
public delegate void BackPressHandler();
public event BackPressHandler backPressEvent;
private void Update()
{
if (Input.GetKeyDown(KeyCode.A))
LeftKeyPress();
if (Input.GetKeyDown(KeyCode.D))
RightKeyPress();
if (Input.GetKeyDown(KeyCode.W))
UpKeyPress();
if (Input.GetKeyDown(KeyCode.S))
DownKeyPress();
if (Input.GetKeyDown(KeyCode.Backspace))
BackKeyPress();
}
public void UpKeyPress()
{
if (upKeyPressEvent != null)
upKeyPressEvent();
}
public void DownKeyPress()
{
if (downKeyPressEevent != null)
downKeyPressEevent();
}
public void LeftKeyPress()
{
if (leftKeyPressEvent != null)
leftKeyPressEvent();
}
public void RightKeyPress()
{
if (rightKeyPressEvent != null)
rightKeyPressEvent();
}
public void BackKeyPress()
{
if (backPressEvent != null)
backPressEvent();
}
}
客户端实现委托绑定
using DesignModel.CommandPattern;
public class Client_CommandPattern_MoveBox : MonoBehaviour
{
[SerializeField] private KeyEventCtr keyEventCtr;
[SerializeField] private ObjController receiver;
private CommandManager commandManager;
private void Start()
{
//一个管理者
commandManager = new CommandManager();
//添加事件监听
keyEventCtr.upKeyPressEvent += MoveUp;
keyEventCtr.downKeyPressEevent += MoveDown;
keyEventCtr.leftKeyPressEvent += MoveLeft;
keyEventCtr.rightKeyPressEvent += MoveRight;
keyEventCtr.backPressEvent += ReMove;
}
private void MoveUp()
{
commandManager.SetCommand(new MoveCommand(receiver, new Vector3(0, 1, 0)));
commandManager.DoCommand();
}
private void MoveDown()
{
commandManager.SetCommand(new MoveCommand(receiver, new Vector3(0, -1, 0)));
commandManager.DoCommand();
}
private void MoveRight()
{
commandManager.SetCommand(new MoveCommand(receiver, new Vector3(1, 0, 0)));
commandManager.DoCommand();
}
private void MoveLeft()
{
commandManager.SetCommand(new MoveCommand(receiver, new Vector3(-1, 0, 0)));
commandManager.DoCommand();
}
private void ReMove()
{
commandManager.UnDoCommand();
}
}
结果展示
有时间就把例子传到github上去