在Command里,包含了我要操作、处理的对象。比如粘贴命令中,肯定包含文档Document本身 。
Command模式代码结构很简单,定义一个Command接口,包含了一个Execute()的方法:
namespace PureMVC.Interfaces
{
public interface ICommand : INotifier
{
void Execute(INotification Notification);
}
}
这是PureMVC Framework中ICommand接口的实现,只有一个Execute,包含了一个INotification,这是一个消息体:
namespace PureMVC.Interfaces
{
public interface INotification
{
string Name { get; }
object Body { get; set; }
string Type { get; set; }
string ToString();
}
}
我们在使用中,一定会涉及到需要参数化的地方,INotification便是负责参数的部分。
INotifier这是通知的接口,在Command中,我们也会去执行其它的命令,在PureMVC Framework中,命令的执行是通过INotifier实现,在上面的例子中,不必去关心,不同的框架实现基本上的思路是一样的,只需要知道,Command中,至少会有一个Execute方法。
Command也支持撤消的操作,对于这种需求,我们通常也会有一个UnExecute,用于撤消原来的操作,但这时候,我们要撤销,就需要记录在撤销前的操作,这里就会涉及到Memento备忘录模式,用于保存对象的内部状态,如果对象比较复杂,又会涉及到深浅拷贝的问题,这里又会涉及到Prototype原型设计模式。
存储的操作也可以定义到Command中,使执行和撤消的操作进行分离。 撤消这里又叫rollback or discard回滚,后退,比如我们在浏览网页的时候,返回上一页。 有时候,我们要依次执行多条不同的命令,可以定义一个MacroCommand,比如在初次进入游戏时,检测资源更新,初始化各种管理器,加载游戏的静态数据,读取存档,加载首场景等必须资源,这些不同的请求,都在不同的Command中,可以通过MacroCommand来 依次的执行。
MacroCommand实现,只需要提供一个List:
public class GameStartCommand : MacroCommand
{
protected override void InitializeMacroCommand()
{
AddSubCommand(() => new InitAudioManagerCommand());
AddSubCommand(() => new InitUIManagerCommand());
AddSubCommand(() => new LoadPlayerPrefsCommand());
AddSubCommand(() => new StartLoginCommand());
}
}
Command结构:
Client->当前运行的程序
Invoker->执行命令,调用者,Invoker并不知道具体的Command(ConcreteCommand),只知道ICommand interface.
Receiver->定义在Command中,由Receiver执行具体的Action
上图可以看到Command做了一层简单的封装,具体的逻辑还是由Reciever->Action()执行。
我们在使用PureMVC Framework中,具体的请求逻辑基本上都是在Command中写的,除非一些通用的操作,我们会定义在 Reciever中。
其它具体的请求,均在Command定义,这样会更加的合理,请求之间解耦。
涉及到请求的变更,只要不涉及到公共的部分,只会修改某一个具体的Command,从而不会影响到其它的请求。
增加新的Command很容易,它无需去改变已有的类。
对于没有取消操作和无参数的Command,我们可以简单的使用范型或是委托实同现,减少子类的生成,提高代码的重用性,前提是,具体的Action 是在Reciever中实现的,简单的实现代码如下:
//测试代码:
SimpleTestCommand command = new SimpleTestCommand(new Copy().CopyFile);
command.Execute();
SimpleTestCommand command1 = new SimpleTestCommand(new Paste().PasteFile);
command1.Execute();
CoyFile,PasteFile不需要定义在两个Command子类中。
public interface ICommand
{
void Execute();
}
public class SimpleTestCommand : ICommand
{
public Action action;
public SimpleTestCommand(Action action)
{
this.action = action;
}
public void Execute()
{
action?.Invoke();
}
}
public class Copy
{
public void CopyFile()
{
Debug.Log("Copy File");
}
}
public class Paste
{
public void PasteFile()
{
Debug.Log("Paste File");
}
}
下在是wikipedia上的应用例子:
using System;
namespace CommandPattern
{
public interface ICommand
{
void Execute();
}
/* The Invoker class */
public class Switch
{
ICommand _closedCommand;
ICommand _openedCommand;
public Switch(ICommand closedCommand, ICommand openedCommand)
{
this._closedCommand = closedCommand;
this._openedCommand = openedCommand;
}
// Close the circuit / power on
public void Close()
{
this._closedCommand.Execute();
}
// Open the circuit / power off
public void Open()
{
this._openedCommand.Execute();
}
}
/* An interface that defines actions that the receiver can perform */
public interface ISwitchable
{
void PowerOn();
void PowerOff();
}
/* The Receiver class */
public class Light : ISwitchable
{
public void PowerOn()
{
Console.WriteLine("The light is on");
}
public void PowerOff()
{
Console.WriteLine("The light is off");
}
}
/* The Command for turning off the device - ConcreteCommand #1 */
public class CloseSwitchCommand : ICommand
{
private ISwitchable _switchable;
public CloseSwitchCommand(ISwitchable switchable)
{
_switchable = switchable;
}
public void Execute()
{
_switchable.PowerOff();
}
}
/* The Command for turning on the device - ConcreteCommand #2 */
public class OpenSwitchCommand : ICommand
{
private ISwitchable _switchable;
public OpenSwitchCommand(ISwitchable switchable)
{
_switchable = switchable;
}
public void Execute()
{
_switchable.PowerOn();
}
}
/* The test class or client */
internal class Program
{
public static void Main(string[] arguments)
{
string argument = arguments.Length > 0 ? arguments[0].ToUpper() : null;
ISwitchable lamp = new Light();
// Pass reference to the lamp instance to each command
ICommand switchClose = new CloseSwitchCommand(lamp);
ICommand switchOpen = new OpenSwitchCommand(lamp);
// Pass reference to instances of the Command objects to the switch
Switch @switch = new Switch(switchClose, switchOpen);
if (argument == "ON")
{
// Switch (the Invoker) will invoke Execute() on the command object.
@switch.Open();
}
else if (argument == "OFF")
{
// Switch (the Invoker) will invoke the Execute() on the command object.
@switch.Close();
}
else
{
Console.WriteLine("Argument \"ON\" or \"OFF\" is required.");
}
}
}
}
--
Switch包含了两个Command的引用,但并没有指定具体的Command:
ICommand _closedCommand;
ICommand _openedCommand;
wikipedia上有许多关于Command的应用场景,比如UI中的按钮,菜单项, Browser的向导Wizard,你可以在很多的场景中进行应用,他并不固定于某一个方面,核心是“将一个请求封闭成一个对象”,这样做的好处是可以提高代码的复用性,请求的调用者与具体的请求之间解耦,提高灵活性,方便去扩展,并且请求与请求之间也进行了分离, 容易去添加更多的Command.
:记录得有些乱,其实应该用几句话或是更小的语言进行精练的总结他的概念,意图,应用场景,注意事项,优点,缺点
https://gameinstitute.qq.com/community/detail/133055