命令模式关注动作本身,通过将动作封装成对象实现调用者和底层实现相分离。调用者只需要简单的下达命令,然后等待命令完成即可,对底层发生了什么完全不知情。关于命令模式一个很直观的例子就是点餐:当我们点餐时,我们只用关心将选好的菜品下单,然后等待送餐即可,我们不关心饭菜是怎么做的,不关心厨师是男是女。
下面通过一个万能遥控器的例子进一步认识命令模式。
步入物联网时代,很多家电都可以实现远程控制,我们想看电视,听音乐,打扫房间,只需要按一下遥控器上对应的按键,相应的家电就会自动工作。那么这样的一款遥控器要怎样实现呢?现在的场景是,遥控器上的多个按钮对应多个家电,每个家电都有“开”、“关”两个命令。当然,最重要的地方在于,遥控器还必须要能够方便扩展,以后购置新的家电时,只要加一些按钮就可以了。
首先定义命令接口,为简单起见,命令接口里面只有execute方法,因为命令就是要被执行的:
1 public interface Command {
2 void execute();
3 }
遥控器上的每一个按钮都是一个命令,对应不同的电器,所以命令应该有很多种具体类型,假设目前有灯泡、电视、音箱三种家电,电器的定义如下:
1 // 灯泡
2 public class Light {
3 public void on(){
4 System.out.println("打开电灯。。。");
5 }
6
7 public void off(){
8 System.out.println("关闭电灯。。。");
9 }
10 }
11
12 // 电视
13 public class TV {
14 public void on(){
15 System.out.println("打开电视。。。");
16 }
17
18 public void off(){
19 System.out.println("关闭电视。。。");
20 }
21 }
22
23 // 音箱
24 public class LoudspeakerBox {
25 public void on(){
26 System.out.println("打开音箱。。。");
27 }
28
29 public void off(){
30 System.out.println("关闭音箱。。。");
31 }
32 }
每种电器都有“开”、“关”两个具体命令,定义如下:
1 // 开灯命令
2 public class LightOnCommand implements Command{
3 Light light;
4
5 public LightOnCommand(Light light){
6 this.light = light;
7 }
8
9 @Override
10 public void execute() {
11 light.on();
12 }
13 }
14
15 // 关灯命令
16 public class LightOffCommand implements Command{
17 Light light;
18
19 public LightOffCommand(Light light){
20 this.light = light;
21 }
22
23 @Override
24 public void execute() {
25 light.off();
26 }
27 }
28
29 // 电视和音箱的开关命令与此类型,略去
30 ...
现在可以看看遥控器的样子了:
1 // 遥控器
2 public class RemoteController {
3 Command[] onCommands;
4 Command[] offCommands;
5
6 public RemoteController(int commandSize){
7 this.onCommands = new Command[commandSize];
8 this.offCommands = new Command[commandSize];
9 }
10
11 public void setCommand(int i, Command onCommand, Command offCommand){
12 onCommands[i] = onCommand;
13 offCommands[i] = offCommand;
14 }
15
16 // 按下开按钮
17 public void onButtonPressed(int i){
18 onCommands[i].execute();
19 }
20
21 // 按下关按钮
22 public void offButtonPressed(int i){
23 offCommands[i].execute();
24 }
25 }
遥控器针对每一种家电设置了两个开关,按下对应的家电的对应开关,会触发相应的动作。这里只对接了3类家电,实际上完全可以对接任意的家电。现在就需要写个测试类看看遥控器是否正常工作:
1 public class RemoteControllerTest {
2 public static void main(String[] args){
3 Light light = new Light();
4 TV tv = new TV();
5 LoudspeakerBox loudspeakerBox = new LoudspeakerBox();
6 Command lightOn = new LightOnCommand(light);
7 Command lightOff = new LightOffCommand(light);
8 Command TVOn = new TVOnCommand(tv);
9 Command TVOff = new TVOffCommand(tv);
10 Command LoudspeakerBoxOn = new LoudspeakerBoxOnCommand(loudspeakerBox);
11 Command LoudspeakerBoxOff = new LoudspeakerBoxOffCommand(loudspeakerBox);
12
13 RemoteController remoteController = new RemoteController(3);
14 remoteController.setCommand(0, lightOn, lightOff);
15 remoteController.setCommand(1, TVOn, TVOff);
16 remoteController.setCommand(2, LoudspeakerBoxOn, LoudspeakerBoxOff);
17
18 remoteController.onButtonPressed(0);
19 remoteController.offButtonPressed(0);
20 remoteController.onButtonPressed(1);
21 remoteController.offButtonPressed(1);
22 remoteController.onButtonPressed(2);
23 remoteController.offButtonPressed(2);
24 }
25 }
输出如下:
打开电灯。。。 关闭电灯。。。 打开电视。。。 关闭电视。。。 打开音箱。。。 关闭音箱。。。
遥控器看起来一起顺利。对以上的代码进行整理,可以得出命令模式的类图如下。

遥控器对应Client,各种家电的开关命令对应ConcreteCommand,Receiver就是家电。
以上就是命令模式的简单应用,它允许我们将动作封装成命令对象,然后就可以随心所欲地存储、传递和调用它们了。通过命令对象实现调用者和执行者解耦,两者之间通过命令对象间接地进行沟通。
