
设计模式之命令模式
第一个命令对象
实现命令接口
1 2 3 4 5 6 7 8 9 10 11
|
public interface Command {
void execute(); }
|
实现一个打开电灯的命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
public class Light {
public void on() { System.out.println("Light is on"); }
public void off() { System.out.println("Light is off"); } }z
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) { this.light = light; }
@Override public void execute() { light.on(); } }
|
创建一个简单遥控器
设计一个遥控器对象,它只有一个按钮和对应的插槽,可以控制一个简单装置
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
|
public class SimpleRemoteControl {
private Command slot;
public void setCommand(Command command) { slot = command; }
public void buttonWasPressed() { slot.execute(); } }
|
遥控器使用的简单测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
public class RemoteControlTest { public static void main(String[] args) { SimpleRemoteControl remote = new SimpleRemoteControl(); Light light = new Light(); LightOnCommand lightOnCommand = new LightOnCommand(light);
remote.setCommand(lightOnCommand); remote.buttonWasPressed(); } }
|
定义命令模式
命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
定义命令模式类图

实现一个复杂遥控器
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
|
public class RemoteControl {
private final Command[] onCommands;
private final Command[] offCommands;
private Command undoCommand;
public RemoteControl() { this.onCommands = new Command[7]; this.offCommands = new Command[7];
Command noCommand = new NoCommand(); for (int i = 0; i < 7; i++) { this.onCommands[i] = noCommand; this.offCommands[i] = noCommand; } this.undoCommand = noCommand; }
public void setCommand(int slot, Command onCommand, Command offCommand) { this.onCommands[slot] = onCommand; this.offCommands[slot] = offCommand; }
public void onButtonWasPushed(int slot) { this.onCommands[slot].execute(); this.undoCommand = onCommands[slot]; }
public void offButtonWasPushed(int slot) { this.offCommands[slot].execute(); this.undoCommand = offCommands[slot]; }
public void undoButtonWasPushed() { this.undoCommand.undo(); }
@Override public String toString() { StringBuffer buffer = new StringBuffer(); buffer.append("\n------- Remote Control-------\n"); for (int i = 0; i < this.onCommands.length; i++) { buffer.append("[slot ") .append(i) .append(" ]") .append(this.onCommands[i].getClass().getName()) .append(" ") .append(this.offCommands[i].getClass().getName()) .append("\n"); } return buffer.toString(); } }
|
实现其他命令
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
|
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) { this.light = light; }
@Override public void execute() { light.on(); }
@Override public void undo() { light.off(); } }
public class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) { this.light = light; }
@Override public void execute() { light.off(); }
@Override public void undo() { light.on(); } }
public class Stereo { private String name; public Stereo(String name) { this.name = name; }
public void on() { System.out.printf("%s stereo is on\n", name); }
public void off() { System.out.printf("%s stereo is off\n", name); }
public void setCD() { System.out.printf("%s stereo is et for CD input\n", name); }
public void setVolume(int volume) { System.out.printf("%s stereo volume se to 11\n", name); } }
public class StereoOnWithCDCommand implements Command {
private Stereo stereo;
public StereoOnWithCDCommand(Stereo stereo) { this.stereo = stereo; }
@Override public void execute() { stereo.on(); stereo.setCD(); stereo.setVolume(11); }
@Override public void undo() { stereo.off(); } }
public class StereoOffWithCDCommand implements Command {
private Stereo stereo;
public StereoOffWithCDCommand(Stereo stereo) { this.stereo = stereo; }
@Override public void execute() { stereo.off(); }
@Override public void undo() { stereo.on(); } }
|
测试我们的遥控器
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
| public class RemoteLoader { public static void main(String[] args) { RemoteControl remoteControl = new RemoteControl();
Light livingRoomLight = new Light("Living Room"); Light kitchenLight = new Light("Kitchen"); Stereo stereo = new Stereo("Living Room");
LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight); LightOnCommand kitchenLightLightOn = new LightOnCommand(kitchenLight); LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight); LightOffCommand kitchenLightLightOff = new LightOffCommand(kitchenLight);
StereoOnWithCDCommand stereoOnWithCD = new StereoOnWithCDCommand(stereo); StereoOffWithCDCommand stereoOffWithCD = new StereoOffWithCDCommand(stereo);
remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff); remoteControl.setCommand(1, kitchenLightLightOn, kitchenLightLightOff); remoteControl.setCommand(2, stereoOnWithCD, stereoOffWithCD); System.out.println(remoteControl);
remoteControl.onButtonWasPushed(0); remoteControl.offButtonWasPushed(0); remoteControl.undoButtonWasPushed();
remoteControl.onButtonWasPushed(1); remoteControl.offButtonWasPushed(1); remoteControl.undoButtonWasPushed();
remoteControl.onButtonWasPushed(2); remoteControl.offButtonWasPushed(2); remoteControl.undoButtonWasPushed(); } }
|
使用宏命令
每个遥控器都应该具备“Party模式”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Light light = new Light("Living Room"); TV tv = new TV("Living Room"); Stereo stereo = new Stereo("Living Room"); Hottub hottub = new Hottub();
LightOnCommand lightOn = new LightOnCommand(light); StereoOnCommand sterenOn = new StereoOnCommand(stereo); TVOnCommand tvOn = new TVOnCommand(tv); HottubOnCommand hottubOn = new HottubOnCommand(hottub);
LightOffCommand lightOff = new LightOffCommand(light); StereoOffCommand sterenOff = new StereoOffCommand(stereo); TVOffCommand tvOff = new TVOnCffmmand(tv); HottubOffCommand hottubOff = new HottubOnCffmmand(hottub);
|
- 接下来创建两个数组,其中记录开启命令,另一个记录关闭命令,并在数组内放入对应的命令
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
|
public class MacroCommand implements Command {
private final Command[] commands;
public MacroCommand(Command[] commands) { this.commands = commands; }
@Override public void execute() { for (Command command : commands) { command.execute(); } }
@Override public void undo() { for (Command command : commands) { command.undo(); } } }
|
1 2 3 4 5 6 7
| Command[] partyOn = { lightOn, stereoOn, tvOn, hottubOn }; Command[] partyOff = { lightOff, sterenOff, tvOff, hottubOff };
MacroCommand partyOnMacro = new MacroCommand(partyOn); MacroCommand partyOffMacro = new MacroCommand(partyOff);
|
1 2
| remoteControl.setCommand(4, partyOnMacro, partyOffMacro);
|
1 2 3 4 5
| System.out.println(remoteControl); System.out.println("--- Pushing Macro On ---"); remoteControl.onButtonWasPushed(4); System.out.println("--- Pushing Macro Off ---"); remoteControl.offButtonWasPushed(4);
|
总结
- 命令模式将发出请求的对象和执行请求的对象解耦
- 在被解耦的两者之间是通过命令对象进行沟通的。命令对象封装了接收者和一个或一组动作
- 调用者通过命令对象的execute()发出请求,这会使得接收者的动作被调用
- 调用者可以接受命令当做参数,甚至在运行时动态地进行
- 命令可以支持撤销,做法是实现一个undo()方法来回到execute()被执行的状态
- 宏命令是命令的一种简单的延伸,允许调用多个命令。宏方法也可以支持撤销
- 实际操作时,很常见使用“聪明”命令对象,也就是直接实现了请求,而不是将工作委托给接收者
- 命令也可以用来实习日志和事务系统