设计模式之命令模式(十九)

直接调用时执行方法的一般方式。然而,有时我们无法控制方法执行的时机与上下文。这种情况下,可以将方法封装在对象的内部。通过在对象内部存储调用方法所需要的信息,就可以让客户端或者服务决定何时调用该方法。

命令模式的意图是将请求封装在对象内部。

命令模式的结构

命令模式包含以下主要角色。

  1. 抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法 execute()。

  2. 具体命令角色(Concrete Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。

  3. 实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。

  4. 调用者/请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。

其结构图如图所示。

命令模式结构图

命令模式的实现

命令模式的代码如下:

调用者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Invoker
{
private Command command;
public Invoker(Command command)
{
this.command=command;
}
public void setCommand(Command command)
{
this.command=command;
}
public void call()
{
System.out.println("调用者执行命令command...");
command.execute();
}
}

抽象命令

1
2
3
4
interface Command
{
public abstract void execute();
}

具体命令

1
2
3
4
5
6
7
8
9
10
11
12
class ConcreteCommand implements Command
{
private Receiver receiver;
ConcreteCommand()
{
receiver=new Receiver();
}
public void execute()
{
receiver.action();
}
}

接收者

1
2
3
4
5
6
7
class Receiver
{
public void action()
{
System.out.println("接收者的action()方法被调用...");
}
}

运行

1
2
3
4
5
6
7
public static void main(String[] args)
{
Command cmd=new ConcreteCommand();
Invoker ir=new Invoker(cmd);
System.out.println("客户访问调用者的call()方法...");
ir.call();
}

结果

客户访问调用者的call()方法...
调用者执行命令command...
接收者的action()方法被调用...

常见案例及应用场景

常见案例:struts 1 中的 action 核心控制器 ActionServlet 只有一个,相当于 Invoker,而模型层的类会随着不同的应用有不同的模型类,相当于具体的 Command。

应用场景:

  1. 当系统需要将请求调用者与请求接收者解耦时,命令模式使得调用者和接收者不直接交互。
  2. 当系统需要随机请求命令或经常增加或删除命令时,命令模式比较方便实现这些功能。
  3. 当系统需要执行一组操作时,命令模式可以定义宏命令来实现该功能。
  4. 当系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作时,可以将命令对象存储起来,采用备忘录模式来实现。

总结

命令模式可以将请求封装在一个对象中,允许你可以像管理对象一样去管理方法,传递并且在合适的时机去调用他们。它的另外一个用处是,允许在服务执行的上下文中执行客户类代码。服务经常在调用客户代码的前后执行。

命令模式的主要优点如下:

  1. 降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。
  2. 增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
  3. 可以实现宏命令。命令模式可以与合成模式结合,将多个命令装配成一个组合命令,即宏命令。
  4. 方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。

其缺点是:可能产生大量具体命令类。因为计对每一个具体操作都需要设计一个具体命令类,这将增加系统的复杂性。