经典命令模式实现

我们前面介绍了命令模式中命令与执行分离的特性,这个特性是我们理解经典命令模式设计的重点。经典命令模式的提出非常地早,而且为了表示通用性,没有使用现代语言的特点,(比如说Java,没有接口或委托),经典命令模式包含一些概念,这些概念都是围绕命令与执行分离的特性展开的。

经典命令模式包含四个概念:Command(抽象命令)、ConcreteCommand(具体命令)、Invoker(命令的调用者、管理者)、Receiver(被Command访问和操作)

我们先看一下之前的简单命令模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public interface ICommand
{
void Execute();
}

public class OpenLightCommand : ICommand
{
public void Execute()
{
var light = GameObject.Find("Light");
light.SetActive(true);

Debug.Log("打开灯光");
}
}

void Start()
{
var command = new OpenLightCommand();
command.Execute();
}

我们可以将其中的一些东西和经典命令模式概念对应起来:

  • ICommand:命令接口,对应经典实现中的Command
  • OpenLightCommand:命令的实现,对应经典实现中的ConcreteCommand
  • Start方法:命令调用,对应经典实现中的Invoker

我们再看一下经典命令模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//Receiver 被Command访问和操作
public class Receiver
{
public void SayHello(int times = 1)
{
for(int i = 0;i < times; i++)
{
Debug.Log("Hello");
}
}
}

//Command 抽象命令
public interface ICommand
{
void Execute();
}

//ConcreteCommand 具体命令
public class ConcreteCommand : ICommand
{
public Receiver receiver;
public int times;
public void Execute()
{
receiver.SayHello(times);
}

public ConcreteCommand(Receiver receiver,int times)
{
this.receiver = receiver;
this.times = times;
}
}

//Invoker 命令的调用者、管理者
public class Invoker
{
List<ICommand> mCommands = new List<ICommand>();

public void AddCommand(Command command)
{
mCommands.Add(command);
}
public void ExecuteCommands()
{
mCommands.ForEach(c => c.Execute());
mCommands.Clear();
}
}

//客户端
void Start()
{
Invoker invoker = new Invoker();
Receiver receiver = new Receiver();
invoker.AddCommand( new ConcreteCommand( receiver,3));
invoker.AddCommand( new ConcreteCommand( receiver,5));
invoker.ExecuteCommands();
}

在决定版架构中的Command模式里,Receiver则是Model、System等对象,Invoker则是Architecture对象本身,因为负责执行Command是在Architecture内部。

  • Receiver:Model、System等对象
  • Invoker:Architecture
  • Client:IController

经典命令模式分析

我们再看一下命令模式的定义:

  • 将请求封装成对象,让你可以将客户端的不同请求参数化,并配合队列、记录、复原等方法来执行请求操作。

定义部分只有将请求封装成对象这几个字,而后边所说的参数化、队列、记录、复原都是以前边的几个字为基础的。

命令模式经典实现确实实现了“将请求封装成对象”这个功能,并且也实现了参数化和队列(Invoker)中管理这些功能。但是记录和复原是没有实现的。

经典命令模式的好处

经典命令模式中Invoker和Receiver都是完全解耦的状态,这是第一个好处。(通过Command来解耦)

当然我们用观察者模式也能实现Invoker和Receiver完全解耦状态,那么经典命令模式有什么不同呢?

经典命令模式的不同在于,除了能够实现Invoker和Receiver的完全解耦,还可以拓展Command。当然我们简化的命令模式也是可以拓展Command的,不过我们简化了以后没有了Invoker这种管理类。这就涉及了经典命令模式另一个好处:

可以在Invoker中实现Command的对列、记录、复原,甚至可以实现一个轻量级的行为树出来。

总结一下这些优点:

  • Invoker和Receiver解耦
  • 可以拓展Command
  • Invoker可以做到对列、记录、复原、行为树