意图:将一个命令请求封装成一个对象。解决命令的请求者和命令的实现者之间的耦合关系。
经典的命令模式包括4个角色:
- Command:定义命令的统一接口
- ConcreteCommand:Command接口的实现者,用来执行具体的命令,某些情况下可以直接用来充当Receiver。
- Receiver:命令的实际执行者
- Invoker:命令的请求者,是命令模式中最重要的角色。这个角色用来对各个命令进行控制。
下面对上面四个角色的经典实现用代码来进行说明,这也是大部分文章对命令模式的运用方式。
经典代码实现:
1 /// <summary>
2 /// Command角色
3 /// </summary>
4 public interface ICommand
5 {
6 void Execute();
7 }
8
9 /// <summary>
10 /// ConcreteCommand角色A
11 /// </summary>
12 public class ConcreteCommandA : ICommand
13 {
14 private Receiver receiver = null;
15
16 public ConcreteCommandA(Receiver receiver)
17 {
18 this.receiver = receiver;
19 }
20
21 public void Execute()
22 {
23 this.receiver.DoA();
24 }
25 }
26
27 /// <summary>
28 /// ConcreteCommand角色B
29 /// </summary>
30 public class ConcreteCommandB : ICommand
31 {
32 private Receiver receiver = null;
33
34 public ConcreteCommandB(Receiver receiver)
35 {
36 this.receiver = receiver;
37 }
38
39 public void Execute()
40 {
41 this.receiver.DoB();
42 }
43 }
44
45 /// <summary>
46 /// Receiver角色
47 /// </summary>
48 public class Receiver
49 {
50 public void DoA()
51 {
52 //DoSomething
53 }
54
55 public void DoB()
56 {
57 //DoSomething
58 }
59 }
60
61 /// <summary>
62 /// Invoker角色
63 /// </summary>
64 public class Invoker
65 {
66 private ICommand command = null;
67
68 //设置命令
69 public void SetCommand(ICommand command)
70 {
71 this.command = command;
72 }
73 //执行命令
74 public void RunCommand()
75 {
76 command.Execute();
77 }
78 }
79
80 /// <summary>
81 /// 客户端调用
82 /// </summary>
83 public class Client
84 {
85 public Client()
86 {
87 Receiver receiver = new Receiver();
88 Invoker invoker = new Invoker();
89 invoker.SetCommand(new ConcreteCommandA(receiver));
90 invoker.RunCommand();
91 invoker.SetCommand(new ConcreteCommandB(receiver));
92 invoker.RunCommand();
93 }
94 }
模式总结
5.1 优点
5.1.1 解除了请求者与实现者之间的耦合,降低了系统的耦合度。
5.1.2 对请求排队或记录请求日志,支持撤销操作。
5.1.3 可以容易地设计一个组合命令。
5.1.4 新命令可以容易地加入到系统中。
5.2 缺点
5.2.1 因为针对每一个命令都需要设计一个具体命令类,使用命令模式可能会导致系统有过多的具体命令类。
5.3 适用场景
5.3.1 当需要对行为进行"记录、撤销/重做"等处理时。
5.3.2 系统需要将请求者和接收者解耦,使得调用者和接收者不直接交互。
5.3.3 系统需要在不同时间指定请求、请求排队和执行请求。
5.3.4 系统需要将一组操作组合在一起,即支持宏命令。
使用场景:
任务队列:Invoker将命令放入队列里,Receiver从队列里拿出命令来执行。也就是说,命令本身的执行逻辑是Invoker定义的。
日志记录:如我们拿到命令后可以序列化到硬盘,然后根据检查点在系统宕机恢复后重新执行命令。