行为模式之命令模式

Posted by Night Field's Blog on June 3, 2020

1 概述

命令模式(Command Pattern),是将请求封装成一系列命令对象,以解耦请求发起者和请求接收者的一种行为模式。

2 命令模式

正常情况下,请求发送者和请求接收者是相互耦合的,发送者直接调用接收者相关的方法,直接交互。 用命令模式进行改造,将两者交互的媒介—-命令单独抽离出来,使得发送者和接收者相互独立,只依赖于命令对象,提高了扩展性和可维护性。同时,通过命令模式,我们还能完成一些高级操作,如撤销命令方法参数化命令入队批量命令等等。

3 案例

通过一个简单的例子加深对命令模式的理解。小时候我们都玩过遥控赛车,赛车可以根据遥控器的按钮,执行相应的动作。我们作为命令的发送者,其实并不直接跟赛车交互,而是通过遥控器的指令来操作赛车:

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
public interface RemoteControlCar {
    void moveForward();
    void turnLeft();
    void turnRight();
    void moveBackward();
}
//请求接收者
public class RemoteControlCarImpl implements RemoteControlCar {
    @Override
    public void moveForward() {
        System.out.println("The car is moving forward!");
    }
    @Override
    public void turnLeft() {
        System.out.println("The car turns left!");
    }
    @Override
    public void turnRight() {
        System.out.println("The car turns right!");
    }
    @Override
    public void moveBackward() {
        System.out.println("The car is moving backward!");
    }
}

//命令接口,支持回退操作
public interface RemoteControlCommand {
    void execute();
    void undo();
}
public class ForwardCommand implements RemoteControlCommand {
    RemoteControlCar rcCar;
    public ForwardCommand(RemoteControlCar rcCar) {
        this.rcCar = rcCar;
    }
    @Override
    public void execute() {
        rcCar.moveForward();
    }
    @Override
    public void undo() {
        rcCar.moveBackward();
    }
}
public class BackwardCommand implements RemoteControlCommand {
    RemoteControlCar rcCar;
    public BackwardCommand(RemoteControlCar rcCar) {
        this.rcCar = rcCar;
    }
    @Override
    public void execute() {
        rcCar.moveBackward();
    }
    @Override
    public void undo() {
        rcCar.moveForward();
    }
}
public class TurnLeftCommand implements RemoteControlCommand {
    RemoteControlCar rcCar;
    public TurnLeftCommand(RemoteControlCar rcCar) {
        this.rcCar = rcCar;
    }
    @Override
    public void execute() {
        rcCar.turnLeft();
    }
    @Override
    public void undo() {
        rcCar.turnRight();
    }
}
public class TurnRightCommand implements RemoteControlCommand {
    RemoteControlCar rcCar;
    public TurnRightCommand(RemoteControlCar rcCar) {
        this.rcCar = rcCar;
    }
    @Override
    public void execute() {
        rcCar.turnRight();
    }
    @Override
    public void undo() {
        rcCar.turnLeft();
    }
}

public interface CarPlayer {
    void play();
    void undo();
    void setCommand(RemoteControlCommand command);
}
// 命令发送者,将请求委托给命令对象来完成。通过设置不同的命令,完成不同的操作
public class Kid implements CarPlayer {
    RemoteControlCommand command;
    public Kid(RemoteControlCommand command) {
        this.command = command;
    }
    @Override
    public void setCommand(RemoteControlCommand command) {
        this.command = command;
    }
    @Override
    public void play() {
        command.execute();
    }
    @Override
    public void undo() {
        command.undo();
    }
}

public class Test {
    public static void main(String[] args) {
        RemoteControlCar remoteControlCar = new RemoteControlCarImpl();
        RemoteControlCommand moveCommand = new ForwardCommand(remoteControlCar);
        RemoteControlCommand turnLeftCommand = new TurnLeftCommand(remoteControlCar);
        RemoteControlCommand turnRightCommand = new TurnRightCommand(remoteControlCar);
        RemoteControlCommand stopCommand = new BackwardCommand(remoteControlCar);

        // 需要完成不同的操作,只需设置不同的命令
        CarPlayer kid = new Kid(moveCommand);
        kid.play();
        kid.undo();
        kid.setCommand(turnLeftCommand);
        kid.play();
        kid.setCommand(turnRightCommand);
        kid.play();
        kid.setCommand(stopCommand);
        kid.play();
        kid.undo();
    }
}

输出:

1
2
3
4
5
6
The car is moving forward!
The car is moving backward!
The car turns left!
The car turns right!
The car is moving backward!
The car is moving forward!

在上述例子中,我们避免了CarPlayerRemoteControlCar的直接交互,将所有的复杂度都放在RemoteControlCommand中。对命令的增删改并不影响整体系统。在这里只演示了通过命令模式可以完成撤销操作;不过很容易联想,将几个命令放到一个命令集合中一起调用,便可以达到批量操作的目的;将命令放入队列,用类似生产者–消费者的模式,可以做到延迟命令/命令入队的要求。

JDK中我们熟悉线程,就是命令模式的运用。Runnable相当于是Thread对象的命令。

4 总结

命令对象命令模式的核心,它像是一个桥梁,连接了请求调用者和接收者,并解耦了两者的关系。它使得系统扩展变得容易。

文中例子的github地址