1.定义
将"请求"封装成对象,以便使用不同的请求,队列或者日志来参数化其他对象.命令模式也支持可撤销的操作.
注:命令模式把接收者组合到命令中,来实现客户端调用命令执行后的动作.因为最后都是接收者要执行动作.
2.代码实现
这边以遥控器为例子,通过点击不同的按钮来发送不同的命令,相应的各种电器收到命令后执行不同的动作.所以我们要做的就是给遥控器按钮添加命令.
定义一个Command命令接口,这是所有命令的抽象
public interface Command { public void execute(); }
假设有三个命令接收者,也就是说我的遥控器要控制以下三个对象,电灯,车库,音响
public class Light { private String name; public Light(String name) { super(); this.name = name; } public void on() { System.out.println("Light is on"); } public void off() { System.out.println("Light is off"); } }
public class GarageDoor { public void up() { System.out.println("GarageDoor up"); } public void down() { System.out.println("GarageDoor down"); } public void stop() { System.out.println("GarageDoor stop"); } public void lightOn() { System.out.println("GarageDoor lightOn"); } public void lightOff() { System.out.println("GarageDoor lightOff"); } }
public class Stereo { private String name; public Stereo(String name) { super(); this.name = name; } public void on() { System.out.println("Stereo on"); } public void off() { System.out.println("Stereo off"); } public void setCd() { System.out.println("Stereo setCd"); } public void setDvd() { System.out.println("Stereo setDvd"); } public void setRadio() { System.out.println("Stereo setRadio"); } public void setVolume() { System.out.println("Stereo setVolume"); } }
因为遥控器只有开和关的按钮,所以我们针对这三个接收者做出了三组命令
打开电灯,关闭电灯
package command; public class LightOnCommand implements Command{ Light light; public LightOnCommand(Light light) { this.light = light; } @Override public void execute() { light.on(); } }
package command; public class LightOffCommand implements Command{ Light light; public LightOffCommand(Light light) { this.light = light; } @Override public void execute() { light.off(); } }
可以看到,里面定义了接收者,然后实现了Command接口并且调用接收者的方法来实现execute方法.命令模式中,命令基本上都包含这接收者一起传递给调用者.
在定义
车库开,车库关
package command; public class GarageDoorCloseCommand implements Command{ GarageDoor garageDoor; public GarageDoorCloseCommand(GarageDoor garageDoor) { super(); this.garageDoor = garageDoor; } public void setGarageDoor(GarageDoor garageDoor) { this.garageDoor = garageDoor; } @Override public void execute() { garageDoor.down(); } }
package command; public class GarageDoorOpenCommand implements Command{ GarageDoor garageDoor; public GarageDoorOpenCommand(GarageDoor garageDoor) { super(); this.garageDoor = garageDoor; } public void setGarageDoor(GarageDoor garageDoor) { this.garageDoor = garageDoor; } @Override public void execute() { garageDoor.up(); } }
定义
音响开,音响关
package command; public class StereoOffWithCDCommand implements Command { private Stereo stereo; public StereoOffWithCDCommand(Stereo stereo) { super(); this.stereo = stereo; } @Override public void execute() { stereo.off(); } }
package command; public class StereoOnWIthCDCommand implements Command { private Stereo stereo; public StereoOnWIthCDCommand(Stereo stereo) { super(); this.stereo = stereo; } @Override public void execute() { stereo.on(); stereo.setCd(); stereo.setVolume(); } }
这些命令基本上都实现了Command接口
接下来定义遥控器
package command; public class RemoteControl { Command[] onCommands; Command[] offCommands; public RemoteControl() { onCommands = new Command[7]; offCommands = new Command[7]; Command noCommand = new NoCommand(); for (int i = 0; i < onCommands.length; i++) { onCommands[i] = noCommand; offCommands[i] = noCommand; } } public void setCommand(int slot, Command onCommand, Command offCommand) { onCommands[slot] = onCommand; offCommands[slot] = offCommand; } public void onButtonWasPushed(int slot) { onCommands[slot].execute(); } public void offButtonWasPushed(int slot) { offCommands[slot].execute(); } // }
因为遥控器只有开和关,所以我们分别定义Command数组,构造函数中可以自定义命令数组的大小,初始化的时候赋空值.给按钮赋命令的时候也是根据两个数组下标来赋值.
在这里可以看到,RemoteControl只关心命令Command,而并不关心具体是哪一个命令,在按下按钮的时候只需要调用Command的方法就可以执行相应的动作.
接下来是测试类
package command; public class RemoteLoader { public static void main(String[] args) { RemoteControl remoteControl = new RemoteControl(); //定义命令接收者 Light livingRoomLight = new Light("Living Room"); Stereo stereo = new Stereo("Living Room"); GarageDoor garageDoor = new GarageDoor(); //定义命令 LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight); LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight); GarageDoorCloseCommand garageDoorCloseCommand = new GarageDoorCloseCommand(garageDoor); GarageDoorOpenCommand garageDoorOpenCommand = new GarageDoorOpenCommand(garageDoor); StereoOffWithCDCommand stereoOffWithCDCommand = new StereoOffWithCDCommand(stereo); StereoOnWIthCDCommand stereoOnWIthCDCommand = new StereoOnWIthCDCommand(stereo); //设置命令到remoteControl remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff); remoteControl.setCommand(1, garageDoorOpenCommand, garageDoorCloseCommand); remoteControl.setCommand(2, stereoOnWIthCDCommand, stereoOffWithCDCommand); //调用 remoteControl.offButtonWasPushed(0); remoteControl.onButtonWasPushed(1); } }
结果为
Light is off
GarageDoor up
3.总结
命令模式最主要的就是把接收者放在了命令中,通过调用接收者的方法来实现命令的execute功能.
这里还有几个拓展功能:
3.1 撤销
之前定义的时候说有撤销,这边是写下具体的思路,代码都是简单的重复代码,这里就不写一遍了.
public interface Command { public void execute(); public void undo();//撤销 }
只需要在Command接口中添加撤销方法,并且在子类中实现undo方法,以遥控器为例子,只需要调用接收者相反的方法即可.
然后在RemoteLoader这个类中定义一个临时变量来储存上一个变量,撤销的时候直接调用这个临时变量的undo方法即可.如果是需要多次撤销.可以用栈来储存命令,然后挨个调用undo方法即可.
3.2宏命令
我们可以直接定义一个宏命令对象
public class MacroCommand implements Command{ private Command[] command; public MacroCommand(Command[] command) { super(); this.command = command; } @Override public void execute() { for (int i = 0; i < command.length; i++) { command[i].execute(); } } }
然后通过构造函数来传入想组合的命令即可.
比如:
Command[] command = {livingRoomLightOn, livingRoomLightOff}; MacroCommand mc = new MacroCommand(command);
直接把想组合的命令定义成数组,传入即可组成一个宏命令!
队列也是命令模式,队列直接把命令一个一个取出来,直接调用即可,因为命令中已经包含了接收者.