设计模式——命令模式
命令模式的定义:将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持撤销的操作。注意命令模式是将请求封装成对象!
其实简单的说,命令模式就是把方法调用封装起来了,通过封装方法调用,可以把运算块包装成型,所以调用此运算的对象不需要关心事情是如何进行的,只要知道如何使用包装成型的方法来完成它就可以。
命令模式的使用场景:当需要将发出请求的对象和执行请求的对象解耦的时候,请考虑使用命令模式吧。
场景举例:设计一款多功能遥控器,该遥控器可以控制家里各个电器的状态,例如电灯的开关,电视的开关,电扇的速度,车库门的开关等等,下面使用命令模式实现这个遥控器。在这里,发出请求的对象是遥控器,执行请求的对象是各种电器。
首先需要让所有的命令对象实现同一个命令接口
public interface Command { public void execute(); public void undo(); }
接口中有两个方法,一个是执行动作的方法execute,一个是撤销动作的方法undo;
下面实现电灯打开关闭命令前先声明一个电灯类
public class Light { protected void on() { System.out.println("light is on"); } protected void off() { System.out.println("light is off"); } }
电灯类只有两个方法on—>打开,off—>关闭;然后实现打开电灯的命令
public class LightOnCommand implements Command { Light light; public LightOnCommand(Light light) { // TODO Auto-generated constructor stub this.light = light; } @Override public void execute() { // TODO Auto-generated method stub light.on(); } @Override public void undo() { // TODO Auto-generated method stub light.off(); } }
注意undo的操作正好是与execute的操作相反的;在写一个电灯关闭的命令
public class LightOffCommand implements Command { Light light; public LightOffCommand(Light light) { // TODO Auto-generated constructor stub this.light = light; } @Override public void execute() { // TODO Auto-generated method stub light.off(); } @Override public void undo() { // TODO Auto-generated method stub light.on(); } }
再增加一个车库门的开启和关闭
public class GarageDoor { public void up() { System.out.println("the garagedoor is up!"); } public void down() { System.out.println("the garagedoor is down!"); } public void lightOn() { System.out.println("the garagedoor's light is on!"); } public void lightOff() { System.out.println("the garagedoor's light is off!"); } }
public class GarageDoorOpenCommand implements Command { GarageDoor garageDoor; public GarageDoorOpenCommand(GarageDoor garageDoor) { // TODO Auto-generated constructor stub this.garageDoor = garageDoor; } @Override public void execute() { // TODO Auto-generated method stub garageDoor.up(); garageDoor.lightOn(); } @Override public void undo() { // TODO Auto-generated method stub garageDoor.down(); garageDoor.lightOff(); } }
public class GarageDoorCloseCommand implements Command { GarageDoor garageDoor; public GarageDoorCloseCommand(GarageDoor garageDoor) { // TODO Auto-generated constructor stub this.garageDoor = garageDoor; } @Override public void execute() { // TODO Auto-generated method stub garageDoor.down(); garageDoor.lightOff(); } @Override public void undo() { // TODO Auto-generated method stub garageDoor.up(); garageDoor.lightOn(); } }
然后就是遥控器的实现了
public class RemoteControl { /* * 由于对应多个电器这里采用数组记录这些命令 */ Command[] onCommands; Command[] offCommands; Command undoCommand;//用来记录撤销命令 public RemoteControl() { // TODO Auto-generated constructor stub onCommands = new Command[7]; offCommands = new Command[7]; Command noCommand = new NoCommand();//初始化是都是空命令 for (int i = 0; i < 7; i++) { onCommands[i] = noCommand; offCommands[i] = noCommand; } undoCommand=noCommand; } public void setCommand(int slot, Command onCommand, Command offCommand) { onCommands[slot] = onCommand; offCommands[slot] = offCommand; } public void onButtonWasPushed(int slot) { onCommands[slot].execute(); undoCommand=onCommands[slot]; } public void offButtonWasPushed(int slot) { offCommands[slot].execute(); undoCommand=offCommands[slot]; } public void undoButtonWasPushed(){ undoCommand.undo(); } }
空命令在这里什么也不做,只是保证上的按钮均有命令对象存在,空命令的实现
public class NoCommand implements Command { @Override public void execute() { // TODO Auto-generated method stub System.out.println("no command!"); } @Override public void undo() { // TODO Auto-generated method stub } }
开始测试一下遥控器
public class RemoteLoader { public static void main(String[] args) { // TODO Auto-generated method stub RemoteControl remoteControl=new RemoteControl(); Light light=new Light(); LightOnCommand lightOn=new LightOnCommand(light); LightOffCommand lightOff =new LightOffCommand(light); GarageDoor garageDoor=new GarageDoor(); GarageDoorOpenCommand garageDoorOpen=new GarageDoorOpenCommand(garageDoor); GarageDoorCloseCommand garageDoorClose=new GarageDoorCloseCommand(garageDoor); remoteControl.setCommand(0, lightOn, lightOff); remoteControl.setCommand(1, garageDoorOpen, garageDoorClose); remoteControl.onButtonWasPushed(0); remoteControl.onButtonWasPushed(1); remoteControl.offButtonWasPushed(0); remoteControl.offButtonWasPushed(1); } }
打印结果:
light is on the garagedoor is up! the garagedoor's light is on! light is off the garagedoor is down! the garagedoor's light is off!
遥控器实现完毕,测试一下撤销命令
public class RemoteLoader { public static void main(String[] args) { // TODO Auto-generated method stub RemoteControl remoteControl=new RemoteControl(); Light light=new Light(); LightOnCommand lightOn=new LightOnCommand(light); LightOffCommand lightOff =new LightOffCommand(light); remoteControl.setCommand(0, lightOn, lightOff); remoteControl.onButtonWasPushed(0); System.out.println("****************"); remoteControl.undoButtonWasPushed(); } }
打印结果
light is on **************** light is off
总结:
(1)命令模式将发出请求的对象和执行请求的对象解耦;
(2)在被解耦的两者之间是通过命令对象进行沟通的。命令对象封装了接受者和一个或一组动作。
(3)调用者通过调用命令对象的execute()发出请求,这会使得接受者的动作被调用。
(4)调用者可以接受命令当作参数,甚至在运行时动态的进行。
(5)命令可以支持撤销,做法是实现一个undo()方法来回到上一个execute()被执行前的状态。