命令模式(Command pattern)
将“请求”封装成对象,以便使用不同请求,队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
动机
有时必须向某对象提交请求,但并不知道关于被请求的操作或者请求的接受者的任何信息。命令模式可以将请求本身变成一个对象,并可向未指定的应用对象提出请求,这个对象被存储并像其他对象一样被传递,这个对象本身定义一个执行的操作,接收者将其作为一个实例变量,并执行它的一系列执行操作,接受者有执行该请求所需的具体信息。
适用场景
-
在不同的时刻指定,排序和执行请求。一个命令对象可以有一个与初始请求无关的生存期。如果一个请求的接收者可用一种与地址空间无关的方式表达,那么就可将负责该请求的命令对象传递给另一个不同的进程并在那实现该请求。
-
支持取消操作。命令的执行操作可在实施操作前将状态存储起来,在取消操作时这个状态用来消除该操作的效果。命令接口必须添加一个取消操作,该操作取消上一次执行的效果。
-
支持修改日志,这样当系统崩溃时,这些修改可以被重新做一遍,在命令接口中添加装载操作和存储操作,可以用来保持变动的一个一致的修改日志。从崩溃中恢复过程包括从磁盘中重新读入记录下来的命令并重新执行它们。
-
用构建在原语操作上的高层操作构造一个系统。这样一种结构在支持事务的信息系统中很常见。一个事务封装了对数据的一组变动。命令模式提供了对事务进行建模的方法。命令模式有一个公共的接口,使得你可以用同一种方式调用所以的事务。同时使用该模式也易于添加事务以扩展系统。
结构:
代码示例:万能的遥控
//车库门
public class GarageDoor {
public void up() {
System.out.println("向上");
}
public void down() {
System.out.println("向下");
}
public void stop() {
System.out.println("停止");
}
public void ligthOn() {
System.out.println("开灯");
}
public void ligthOff() {
System.out.println("关灯");
}
}
//客厅大灯
public class Light {
public void on() {
System.out.println("开灯");
}
public void off() {
System.out.println("关灯");
}
}
//命令接口
public interface Command {
//执行命令操作
void execute();
//撤销操作
void undo();
}
//关车库门的具体操作
public class GarageDoorCloseCommand implements Command {
GarageDoor garageDoor;
public GarageDoorCloseCommand(GarageDoor garageDoor) {
this.garageDoor = garageDoor;
}
@Override
public void execute() {
garageDoor.down();
garageDoor.stop();
garageDoor.ligthOn();
}
@Override
public void undo() {
garageDoor.up();
garageDoor.stop();
garageDoor.ligthOff();
}
}
//开车库门的具体操作
public class GarageDoorOpenCommand implements Command {
GarageDoor garageDoor;
public GarageDoorOpenCommand(GarageDoor garageDoor){
this.garageDoor = garageDoor;
}
@Override
public void execute() {
garageDoor.up();
garageDoor.stop();
garageDoor.ligthOff();
}
@Override
public void undo() {
garageDoor.down();
garageDoor.stop();
garageDoor.ligthOn();
}
}
//开客厅大灯的具体操作
public class LightOnCommand implements Command {
protected 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 {
Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
@Override
public void undo() {
light.on();
}
}
//遥控器初始化对象
public class NoCommand implements Command{
@Override
public void execute() {
}
@Override
public void undo() {
}
}
//遥控器
public class RemoteControl {
//开启操作
Command[] onCommand;
//关闭操作
Command[] offCommand;
//记录前一个操作,可执行回滚/撤销操作
Command undoCommand;
//遥控器初始化
public RemoteControl() {
onCommand = new Command[2];
offCommand = new Command[2];
Command noCommand = new NoCommand();
for (int i = 0; i < 2; i++) {
onCommand[i] = noCommand;
offCommand[i] = noCommand;
}
undoCommand = noCommand;
}
//slot:遥控按钮
public void setCommand(int slot, Command onCommand, Command offCommand) {
this.onCommand[slot] = onCommand;
this.offCommand[slot] = offCommand;
}
//遥控按钮负责开启操作
public void onButtonWasPushed(int slot) {
onCommand[slot].execute();
undoCommand = onCommand[slot];
}
//遥控按钮负责关闭操作
public void offButtonWasPushed(int slot){
offCommand[slot].execute();
undoCommand = offCommand[slot];
}
//回滚/撤销操作
public void undoButtonWasPushed() {
undoCommand.undo();
}
}
public class Client{
public static void main(String[] args) {
RemoteControl remoteControl = new RemoteControl();
Light light = new Light();
LightOnCommand lightOnCommand = new LightOnCommand(light);
LightOffCommand lightOffCommand = new LightOffCommand(light);
GarageDoor garageDoor = new GarageDoor();
GarageDoorCloseCommand garageDoorCloseCommand = new GarageDoorCloseCommand(garageDoor);
GarageDoorOpenCommand garageDoorOpenCommand = new GarageDoorOpenCommand(garageDoor);
//设置客厅电灯按钮
remoteControl.setCommand(0, lightOnCommand, lightOffCommand);
//设置车库门按钮
remoteControl.setCommand(1, garageDoorOpenCommand, garageDoorCloseCommand);
//开灯
remoteControl.onButtonWasPushed(0);
//关灯
remoteControl.offButtonWasPushed(0);
//回滚/撤销按钮
remoteControl.undoButtonWasPushed();
//开车库门
remoteControl.onButtonWasPushed(1);
//关车库门
remoteControl.offButtonWasPushed(1);
}
}
:客厅开灯
:客厅关灯
:客厅开灯
:车库门向下
:车库门停止
:车库开灯
:车库门向上
:车库门停止
:车库关灯
总结
命令模式将请求封装成一个命令对象,需要时执行操作以及回滚操作。接受者处理该请求的真正操作。如果处理请求的操作会改变接受者中的某些值,那么这些值应该先存储起来,接收者还得提供一些操作,以使该命令可将接收者恢复到它先前的状态。如果只支持一次操作,那么只需要存储最近的一次被执行的命令(undoCommand),而若要支持多级的取消和重做操作,就需要有一个已被执行的命令历史列表,该列表的最大长度决定了取消和重做的级数。历史列表存储已被执行的命令序列。向后遍历该列表并逆向执行命令回滚它们的结果;向前遍历并执行可重新执行它们。