命令模式

命令模式的封装性非常好,把请求方(Invoker)和执行方(Receiver)分开,扩展性也非常好。

定义

将一个请求封装成一个对象,从而使得不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

命令模式通用类图

实现

Receiver

抽象接收者,定义每个接收者都必须完成的业务。

1
2
3
public abstract class Receiver{
public abstract void doSomething();
}

具体的Receiver

1
2
3
4
5
6
7
public class Concrete1Reveiver extends Receiver{
public void doSomething(){}
}

public class Concrete2Reveiver extends Receiver{
public void doSomething(){}
}

Command

不同的命令实现都需要继承该类,实现自己的execute逻辑。

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

具体的命令实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ConcreteCommand1 extends Command{
private Receiver receiver;
public ConcreteCommand1(Receiver receiver){
this.receiver = receiver;
}
public void execute(){
this.receiver.doSomething();
}
}

public class ConcreteCommand2 extends Command{
private Receiver receiver;
public ConcreteCommand2(Receiver receiver){
this.receiver = receiver;
}
public void execute(){
this.receiver.doSomething();
}
}

Invoker

1
2
3
4
5
6
7
8
9
public class Invoker{
private Command command;
public void setCommand(Command command){
this.command = command;
}
public void action(){
this.command.execute();
}
}

Use

1
2
3
Invoker invoker = new Invoker();
invoker.setCommand(new ConcreteCommand1(new Concrete1Reveiver()));
invoker.action();

模式应用

优点

  • 解耦
    调用者角色与接收者角色之间没有任何依赖关系,调用者实现功能时只需要调用Command抽象类的execute方法即可,不需要了解是哪个接收者执行。
  • 可扩展性好
    Command子类可以非常容易的扩展,调用者Invoker和高层的模块Client不产生严重的代码耦合。
  • 与其他模式混合使用
    与责任链模式结合,实现命令族解析任务
    与模版方法结合,减少Command子类膨胀的问题。

缺点

Command的子类会随着命令的增多逐步膨胀。

命令的撤销

反命令

通过相反的命令来实现命令的撤销。

结合备忘录模式

结合备忘录模式还原最后状态,折中发昂发适合接收者为状态的变更情况,而不适合事件处理。

代码实例

假设有个录音机,录音机面板有三个按钮, play, rewind, stop。

需要根据三个按钮作出相应的反馈。

Receiver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class AudioPlayer {

public void paly(){
System.out.println("Play");
}

public void rewind(){
System.out.println("Rewind");
}

public void stop(){
System.out.println("Stop");
}
}

Command

不同的Command调用不同的Receiver命令。

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
public abstract class Command {

protected static AudioPlayer audioPlayer = new AudioPlayer();

public abstract void execute();

}

public class PlayCommand extends Command {
@Override
public void execute() {
super.audioPlayer.paly();
}
}

public class RewindCommand extends Command {
@Override
public void execute() {
audioPlayer.rewind();
}
}

public class StopCommand extends Command {
@Override
public void execute() {
audioPlayer.stop();
}
}

Invoker

用于转发Command

1
2
3
4
5
6
7
8
9
10
11
public class KeyPad {
private Command command;

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

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

Use

1
2
3
4
5
6
7
KeyPad keyPad = new KeyPad();

keyPad.setCommand(new RewindCommand());
keyPad.action();

keyPad.setCommand(new StopCommand());
keyPad.action();

如果后期需要增加一个按钮,stopAndRewind(),直接增加一个Command子类即可,其他地方不需要动。

1
2
3
4
5
6
7
8
public class StopAndRewindCommand extends  Command {

@Override
public void execute() {
audioPlayer.stop();
audioPlayer.rewind();
}
}