初探命令模式

代码链接

koonchen/design-patterns/command

要点

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化。

这个模式很有意思,直接给一张图先:

cover

假设,我们现在有一个工具箱,其中有一些按钮,我们不知道被请求的操作或请求的接收者的信息。

命令模式通过将请求本身变成一个对象那个来使工具箱对象可向未指定的应用对象提出请求。这里没有标明的 Command 具体子类中有目标接收者

比如下面的一个实现:

cover

这个例子就比较明显了,接收者是 document ,这里指定了接收者采取的动作,接收者内部具有执行这个请求的具体信息。

比较特殊的有打开一个新的文档:

cover

它的接收者是 Application ,需要使用的是 Application 的操作。

还有一种是多个命名的执行:

cover

它没有指定接收者,但是每一个指令中都有其具体的接收者。

指令模式的适用性:

  • 像上面的 MenuItem 那样,抽象出待执行的动作以参数化某对象。有点类似于 回调
  • 在不同的时刻指定、排序、执行请求。
  • 支持取消操作。我们可以在 Command 接口中添加一个 Unexecute 操作,取消上一个 Execute 调用的影响。
  • 支持修改日志。当系统崩溃的时候,可以将这些修改重做。
  • 用构建在原语操作上的高层操作系统构造一个系统。一个事务封装了对数据的一组变化,我们可以用同一种方式调用所有的事务,同时该模式也易于添加事务以扩展系统。

指令模式的参与者:

  • Command — 声明执行操作的接口
  • ConcreteCommand — 将一个接收者绑定于一个动作
  • Client (Application) — 创建一个具体命名对象并设定它的接收者
  • Invoker — 要求改命令执行这个请求
  • Receiver — 知道如何实施或执行一个请求相关的操作

指令模式的结构:

cover

协作方式:

  1. Client 创建爱你一个 ConcreteCommand 对象并指定它的 Receiver 对象
  2. Invoker 对象存储该 ConcreteCommand 对象。
  3. 这里的 Invoker 调用 Command 对象的 execute() 操作来提交一个请求。
  4. ConcreteCommand 通过 Receiver 的操作执行这个请求。

效果:

  1. 接收者对象,它真正执行处理该请求的各操作
  2. 接收者上执行操作的参数
  3. 如果处理请求的操作会改变接收者对象的某些值,那么这些值应该先存储起来。
  4. 避免取消操作过程中的错误积累

实现

Java 实现

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
class Invoker {
Command command = null;

public void setCommand(Command command) {
this.command = command;
}

public void doSth() {
this.command.execute();
}
}

class Receiver {
public void action() {
System.out.println("action method has been created");
}
}

interface Command {
void execute();
}

class ConcreteCommand implements Command {

Receiver receiver = null;

public void setReceiver(Receiver receiver) {
this.receiver = receiver;
}

@Override
public void execute() {
this.receiver.action();
}
}

public class CommandClient {

public static void main(String[] args) {
Command command = new ConcreteCommand();
((ConcreteCommand) command).setReceiver(new Receiver());

Invoker invoker = new Invoker();
invoker.setCommand(command);

invoker.doSth();
}
}

相关模式:

Memento 模式可被保持某个状态,命令用这一状态来消除它的效果