(本文中一些例子和定义均摘自《Head First 设计模式》)
命令模式(Command Pattern)
将“请求”封装成对象,以便使用不同的请求,队列或者日志来参数化其他对象。命令模式也支持可撤消的操作。
举例:开灯的命令
首先我们定义一个命令接口,所有的命令都应当实现这个接口
public
interface
Command
...
{
public void execute();
}
public void execute();
}
在Light类中,厂商定义了开和关的命令
public
class
Light
...
{
public void on()...{
System.out.println("Light on.......");
}
public void off()...{
System.out.println("Light off.......");
}
}
public void on()...{
System.out.println("Light on.......");
}
public void off()...{
System.out.println("Light off.......");
}
}
接着我们需要把命令封装成对象
public
class
LightOnCommand
implements
Command
...
{
//开灯命令
Light light;
public LightOnCommand(Light light)...{
this.light = light;
}
@Override
public void execute() ...{
light.on();
}
}
public class LightOffCommand implements Command ... {
//关灯命令
Light light;
public LightOffCommand(Light light)...{
this.light = light;
}
@Override
public void execute() ...{
light.off();
}
}
//开灯命令
Light light;
public LightOnCommand(Light light)...{
this.light = light;
}
@Override
public void execute() ...{
light.on();
}
}
public class LightOffCommand implements Command ... {
//关灯命令
Light light;
public LightOffCommand(Light light)...{
this.light = light;
}
@Override
public void execute() ...{
light.off();
}
}
设定使用命令的方式
public
class
SimpleRemoteControl
...
{
Command slot;
public SimpleRemoteControl()...{}
public void setCommand(Command command) ...{
slot = command;
}
public void buttonWasPressed() ...{
slot.execute();
}
}
Command slot;
public SimpleRemoteControl()...{}
public void setCommand(Command command) ...{
slot = command;
}
public void buttonWasPressed() ...{
slot.execute();
}
}
测试一下开灯,关灯
public
class
RemoteControlTest
...
{
public static void main(String[] args) ...{
SimpleRemoteControl remote = new SimpleRemoteControl();
Light light = new Light();
LightOnCommand lightOn = new LightOnCommand(light);
remote.setCommand(lightOn);
remote.buttonWasPressed();
LightOffCommand lightOff = new LightOffCommand(light);
remote.setCommand(lightOff);
remote.buttonWasPressed();
}
}
public static void main(String[] args) ...{
SimpleRemoteControl remote = new SimpleRemoteControl();
Light light = new Light();
LightOnCommand lightOn = new LightOnCommand(light);
remote.setCommand(lightOn);
remote.buttonWasPressed();
LightOffCommand lightOff = new LightOffCommand(light);
remote.setCommand(lightOff);
remote.buttonWasPressed();
}
}
命令模式也可以用于撤消一系列的命令
我们应当记录下一系列的命令操作
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 < 7 ; 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();
}
public String toString()...{
StringBuffer stringBuff = new StringBuffer();
stringBuff.append(" --------Remote Control------- ");
for(int i = 0; i < onCommands.length; i++) ...{
stringBuff.append("[slot" + i + "]" + onCommands[i].getClass().getName()
+ " " + offCommands[i].getClass().getName() + " ");
}
return stringBuff.toString();
}
}
//记录开和关的命令
Command[] onCommands;
Command[] offCommands;
public RemoteControl() ...{
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;
}
}
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();
}
public String toString()...{
StringBuffer stringBuff = new StringBuffer();
stringBuff.append(" --------Remote Control------- ");
for(int i = 0; i < onCommands.length; i++) ...{
stringBuff.append("[slot" + i + "]" + onCommands[i].getClass().getName()
+ " " + offCommands[i].getClass().getName() + " ");
}
return stringBuff.toString();
}
}
在这里 发现每次设置命令都应当判断此处是否已经存在命令。但是每次都判断比较烦琐
所以:
public
class
NoCommand
implements
Command
...
{
@Override
public void execute() ...{
// TODO Auto-generated method stub
}
}
@Override
public void execute() ...{
// TODO Auto-generated method stub
}
}
确保每个插槽都有一个命令对象。
然后需要给Command接口中加入撤消操作的方法
public
interface
Command
...
{
public void execute();
public void undo();
}
public void execute();
public void undo();
}
并且在所有具体的命令中实现这个方法。
public
class
LightOnCommand
implements
Command
...
{
Light light;
public LightOnCommand(Light light)...{
this.light = light;
}
@Override
public void execute() ...{
light.on();
}
//灯开以前的操作是关闭
public void undo() ...{
light.off();
}
}
Light light;
public LightOnCommand(Light light)...{
this.light = light;
}
@Override
public void execute() ...{
light.on();
}
//灯开以前的操作是关闭
public void undo() ...{
light.off();
}
}
重新设计一个控制台
public
class
RemoteControlWithUndo
...
{
//记录开和关的命令
Command[] onCommands;
Command[] offCommands;
//记录上一步的命令是什么
Command undoCommand;
public RemoteControlWithUndo() ...{
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];
}
//按下undo的时候,执行undo方法
public void undoButtonWasPushed() ...{
undoCommand.undo();
}
public String toString()...{
StringBuffer stringBuff = new StringBuffer();
stringBuff.append(" --------Remote Control------- ");
for(int i = 0; i < onCommands.length; i++) ...{
stringBuff.append("[slot" + i + "]" + onCommands[i].getClass().getName()
+ " " + offCommands[i].getClass().getName() + " ");
}
return stringBuff.toString();
}
}
//记录开和关的命令
Command[] onCommands;
Command[] offCommands;
//记录上一步的命令是什么
Command undoCommand;
public RemoteControlWithUndo() ...{
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];
}
//按下undo的时候,执行undo方法
public void undoButtonWasPushed() ...{
undoCommand.undo();
}
public String toString()...{
StringBuffer stringBuff = new StringBuffer();
stringBuff.append(" --------Remote Control------- ");
for(int i = 0; i < onCommands.length; i++) ...{
stringBuff.append("[slot" + i + "]" + onCommands[i].getClass().getName()
+ " " + offCommands[i].getClass().getName() + " ");
}
return stringBuff.toString();
}
}
测试一下undo
public
class
RemoteLoader
...
{
public static void main(String[] args) ...{
RemoteControlWithUndo remoteControl = new RemoteControlWithUndo();
Light livingRoomLight = new Light("Living Room");
Light kitchenLight = new Light("Kitchen");
LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);
LightOnCommand kitchenLightOn = new LightOnCommand(kitchenLight);
LightOffCommand kitchenLightOff = new LightOffCommand(kitchenLight);
remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
remoteControl.setCommand(1, kitchenLightOn, kitchenLightOff);
System.out.println(remoteControl);
remoteControl.onButtonWasPushed(0);
remoteControl.offButtonWasPushed(0);
remoteControl.undoButtonWasPushed();
remoteControl.onButtonWasPushed(1);
remoteControl.offButtonWasPushed(1);
remoteControl.undoButtonWasPushed();
}
}
public static void main(String[] args) ...{
RemoteControlWithUndo remoteControl = new RemoteControlWithUndo();
Light livingRoomLight = new Light("Living Room");
Light kitchenLight = new Light("Kitchen");
LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);
LightOnCommand kitchenLightOn = new LightOnCommand(kitchenLight);
LightOffCommand kitchenLightOff = new LightOffCommand(kitchenLight);
remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
remoteControl.setCommand(1, kitchenLightOn, kitchenLightOff);
System.out.println(remoteControl);
remoteControl.onButtonWasPushed(0);
remoteControl.offButtonWasPushed(0);
remoteControl.undoButtonWasPushed();
remoteControl.onButtonWasPushed(1);
remoteControl.offButtonWasPushed(1);
remoteControl.undoButtonWasPushed();
}
}
这种模式更多的被应用在队列请求和日志中
比如当系统发生异常的时候,我们可以根据日志来要求事务的回滚,回到原先正常的状态。