一.概念
命令模式 将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。即,命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。系统支持命令的撤消。
二.举例子
电视机遥控器,每一个按钮都代表着一个命令,所以很容易想到,电视机是一个命令接收器,遥控器就是一个集成了很多按钮的命令发送器。那么现在实现几个常用功能,电视机的开机,电视机的关机,电视机频道增大,电视机频道减小,撤销上一次操作,恢复撤销的操作。
/**
* 命令模式 将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
* 应用场景
* 在下面的情况下应当考虑使用命令模式:
* 1)使用命令模式作为"CallBack"在面向对象系统中的替代。"CallBack"讲的便是先将一个函数登记上,然后在以后调用此函数。
* 2)需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命期。换言之,原先的请求发出者可能已经不在了,
* 而命令对象本身仍然是活动的。这时命令的接收者可以是在本地,也可以在网络的另外一个地址。命令对象可以在串形化之后传送到另外一台机器上去。
* 3)系统需要支持命令的撤消(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo()方法,把命令所产生的效果撤销掉。命令对象还可以提供redo()方法,以供客户端在需要时,再重新实施命令效果。
* 4)如果一个系统要将系统中所有的数据更新到日志里,以便在系统崩溃时,可以根据日志里读回所有的数据更新命令,重新调用Execute()方法一条一条执行这些命令,从而恢复系统在崩溃前所做的数据更新。
*/
public class CommandTest {
class TV {
private int number; //当前频道,总共50个频道(0-49)
private boolean state; //电视机的状态
public TV(int number) {
this.number = number;//设置当前频道;
this.state = false;//当前状态
}
public void changeStateOpen() {
this.state = true;
System.out.println("开启电视机,当前频道为"+this.number);
}
public void changeStateClose() {
this.state = false;
System.out.println("关闭电视机");
}
public void increaseNumber() {
this.number = (this.number + 1) % 50;
System.out.println("当前频道为"+this.number);
}
public void decreaseNumber() {
this.number = (this.number + 49) % 50;
System.out.println("当前频道为"+this.number);
}
public int getNumber() {
return number;
}
public boolean isState() {
return state;
}
}
interface Command {
public void excute();//执行操作
public void undo();//撤销操作
public void redo();//恢复操作
}
class TurnOnTV implements Command {
private TV tv;
public TurnOnTV(TV tv) {
this.tv = tv;
}
@Override
public void excute() {
this.tv.changeStateOpen();
}
@Override
public void undo() {
this.tv.changeStateClose();
}
@Override
public void redo() {
this.tv.changeStateOpen();
}
}
class TurnOffTV implements Command {
private TV tv;
public TurnOffTV(TV tv) {
this.tv = tv;
}
@Override
public void excute() {
this.tv.changeStateClose();
}
@Override
public void undo() {
this.tv.changeStateOpen();
}
@Override
public void redo() {
this.tv.changeStateClose();
}
}
class TurnIncreaseNumber implements Command {
private TV tv;
public TurnIncreaseNumber(TV tv) {
this.tv = tv;
}
@Override
public void excute() {
if(this.tv.isState()){
this.tv.increaseNumber();
}else {
System.err.println("电视机未开");
}
}
@Override
public void undo() {
if(this.tv.isState()) {
this.tv.decreaseNumber();
}else {
System.err.println("电视机未开");
}
}
@Override
public void redo() {
if(this.tv.isState()){
this.tv.increaseNumber();
}else {
System.err.println("电视机未开");
}
}
}
class TurnDecreaseNumber implements Command {
private TV tv;
public TurnDecreaseNumber(TV tv) {
this.tv = tv;
}
@Override
public void excute() {
if(this.tv.isState()) {
this.tv.decreaseNumber();
}else {
System.err.println("电视机未开");
}
}
@Override
public void undo() {
if(this.tv.isState()){
this.tv.increaseNumber();
}else {
System.err.println("电视机未开");
}
}
@Override
public void redo() {
if(this.tv.isState()) {
this.tv.decreaseNumber();
}else {
System.err.println("电视机未开");
}
}
}
class NoCommand implements Command {
@Override
public void excute() {}
@Override
public void undo() {}
@Override
public void redo() {}
}
class RemotrControl {
private Command[] command;
private Command undoCommand;
private Command redoCommand;
public RemotrControl() {
command = new Command[4];//初始化遥控器有6个按钮
for(int i=0; i < 4; i++) {
command[i] = new NoCommand();//初始化成空命令
}
undoCommand = new NoCommand();//初始化撤销命令
redoCommand = new NoCommand();//初始化恢复命令
}
public void setCommand(int slot, Command command) {
this.command[slot] = command;
}
public void runCommand(int slot) {
this.command[slot].excute();
this.undoCommand = this.command[slot];
}
public void undoCommand() {
this.undoCommand.undo();
this.redoCommand = this.undoCommand;
}
public void redoCommand() {
this.redoCommand.redo();
}
}
public static void main(String[] args) {
//为电视机添加遥控器功能
TV tv = new CommandTest().new TV(4);
RemotrControl control = new CommandTest().new RemotrControl();
control.setCommand(0, new CommandTest().new TurnOnTV(tv));
control.setCommand(1, new CommandTest().new TurnOffTV(tv));
control.setCommand(2, new CommandTest().new TurnIncreaseNumber(tv));
control.setCommand(3, new CommandTest().new TurnDecreaseNumber(tv));
//测试按钮命令
control.runCommand(0);
control.runCommand(2);
control.runCommand(2);
control.runCommand(3);
control.runCommand(3);
control.runCommand(3);
control.runCommand(3);
control.runCommand(3);
control.runCommand(3);
control.runCommand(3);
control.runCommand(3);
control.undoCommand();
control.runCommand(1);
control.runCommand(3);
}
}
运行结果:
三.总结
应用场景
在下面的情况下应当考虑使用命令模式:
使用命令模式作为"CallBack"在面向对象系统中的替代。"CallBack"讲的便是先将一个函数登记上,然后在以后调用此函数。
需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命期。换言之,原先的请求发出者可能已经不在了,
而命令对象本身仍然是活动的。这时命令的接收者可以是在本地,也可以在网络的另外一个地址。命令对象可以在串形化之后传送到另外一台机器上去。
系统需要支持命令的撤消(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo()方法,把命令所产生的效果撤销掉。命令对象还可以提供redo()方法,以供客户端在需要时,再重新实施命令效果。
如果一个系统要将系统中所有的数据更新到日志里,以便在系统崩溃时,可以根据日志里读回所有的数据更新命令,重新调用Execute()方法一条一条执行这些命令,从而恢复系统在崩溃前所做的数据更新。
更多参考