http://blog.sina.com.cn/s/blog_5016113a01009rta.html
命令模式定义
将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作
命令模式可以将“动作的请求者”和“动作的执行者”分隔开来(解耦)
例子:设计一个家电自动化遥控器的API。遥控器有七个插头,可以连接不同的家电电器,每个插头有对应的开关按钮,用来控制电器的开关。这个遥控器还具备一个整体的撤销按钮。
解析:
当遥控器按下“开”按钮时,直接调用家用电器的“开”方法。但是有很多家用电器,每种家用电器的“开”方法名称也不一定相同,这意味着每一个插头的“开”按钮按下时,都要判断是什么家用电器,然后再调用它的“开”方法。
使用命令模式:
封装方法调用,把方法调用封装成对象。把每种家用电器的 “开”方法和执行“开”方法的电器封装成一个统一标准的对象,遥控器通过操作统一标准的对象,来操作家用电器,就可以忽略每种家用电器之间的差异。
UML图(点击看大图)
UML图解
Light为电灯类,LightOnCommand封装了Light和Light的on()方法,它符合Command的标准。遥控器通过操作Command来操作家用电器,从而忽略多种家用电器之间的差异。
为遥控器实现撤销(undo)按钮
例子:遥控天花板吊扇,风扇有风速档位(low、medium、hight),并且要实现撤销按钮,即打开风扇,撤销后就关闭风扇,让风扇回到打开前状态。
UML图
UML图解
RemoteControlWithUndo remoteControl = new RemoteControlWithUndo();
CeilingFan ceilingFan = new CeilingFan("Living Room"); //在客厅天花板上的吊扇
CeilingFanMediumCommand ceilingFanMedium = new CeilingFanMediumCommand(ceilingFan);
//把风扇调到中档,这是个“开”命令
CeilingFanOffCommand ceilingFanOff = new CeilingFanOffCommand(ceilingFan); //关闭风扇命令
remoteControl.setCommand(0, ceilingFanMedium, ceilingFanOff);
remoteControl.onButtonWasPushed(0); //按下“开”
remoteControl.offButtonWasPushed(0); //按下“关”
remoteControl.undoButtonWasPushed(); //按下 “撤销”
NoCommand对象
NoCommand是一个空对象(null object),当你不想返回一个有意义的对象时,空对象就很有用。空对象可以避免我们在程序中写上判断对象是否为空的if等语句。
Party模式
按下一个按钮,就同时能打开音响、电灯、电视、设置好DVD,并让热水器开始加温。
设计一个“宏”命令类
RemoteControl remoteControl = new RemoteControl();
Command[] partyOn = {…………}; //一组“开”命令
Command[] partyOff = { ………...}; //一组“关”命令
MacroCommand partyOnMacro = new MacroCommand(partyOn); //初始化宏命令
MacroCommand partyOffMacro = new MacroCommand(partyOff);
remoteControl.setCommand(0, partyOnMacro, partyOffMacro);
remoteControl.onButtonWasPushed(0); //执行一组“开”命令
remoteControl.offButtonWasPushed(0);
如何学现多层次的撤销操作?
使用一个堆栈记录操作过程的每一个命令,按撤销时从堆栈(后进先出)中取出最上层的命令,调用它的undo方法。
命令模式的更多用途
1.队列请求
把一组命令放到队列(先进先出)中,线程从队列中一个一个删除命令,然后调用它的excecute()方法。
2.日志请求
把所有动作都记录在日志中,在系统发生错误时,重新调用这些动作恢复到之前的状态
Command Pattern的适用场景
1.使用命令模式作为"CallBack"在面向对象系统中的替代。"CallBack"讲的便是先将一个函数登记上,然后在以后调用此函数。
2.需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命期。换言之,原先的请求发出者可能已经不在了,而命令对象本身仍然是活动的。这时命令的接收者可以是在本地,也可以在网络的另外一个地址。命令对象可以在串形化之后传送到另外一台机器上去。
3.系统需要支持命令的撤消(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo()方法,把命令所产生的效果撤销掉。命令对象还可以提供redo()方法,以供客户端在需要时,再重新实施命令效果。
4.如果一个系统要将系统中所有的数据更新到日志里,以便在系统崩溃时,可以根据日志里读回所有的数据更新命令,重新调用Execute()方法一条一条执行这些命令,从而恢复系统在崩溃前所做的数据更新。
总结
Command模式是非常简单而又优雅的一种设计模式,它的根本目的在于将“行为请求者”与“行为实现者”解耦。
命令模式定义
将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作
命令模式可以将“动作的请求者”和“动作的执行者”分隔开来(解耦)
例子:设计一个家电自动化遥控器的API。遥控器有七个插头,可以连接不同的家电电器,每个插头有对应的开关按钮,用来控制电器的开关。这个遥控器还具备一个整体的撤销按钮。
解析:
当遥控器按下“开”按钮时,直接调用家用电器的“开”方法。但是有很多家用电器,每种家用电器的“开”方法名称也不一定相同,这意味着每一个插头的“开”按钮按下时,都要判断是什么家用电器,然后再调用它的“开”方法。
使用命令模式:
封装方法调用,把方法调用封装成对象。把每种家用电器的 “开”方法和执行“开”方法的电器封装成一个统一标准的对象,遥控器通过操作统一标准的对象,来操作家用电器,就可以忽略每种家用电器之间的差异。
UML图(点击看大图)
UML图解
Light为电灯类,LightOnCommand封装了Light和Light的on()方法,它符合Command的标准。遥控器通过操作Command来操作家用电器,从而忽略多种家用电器之间的差异。
//1.Command接口形式: public interface Command { public void execute(); } //2.LightOnCommand形式: public class LightOnCommand implements Command { Light light; //封装方法执行者 public LightOnCommand(Light light) { this.light = light; } public void execute() { light.on(); //封装方法 } } //3.SimpleRemoteControl遥控器形式如: public class SimpleRemoteControl { Command slot; public SimpleRemoteControl() {} public void setCommand(Command command) { slot = command; } public void buttonWasPressed() { slot.execute(); //遥控器不知道方法执行者到底是谁,它操作统一家用标准Command对象。 } } //4.实际动作方式: SimpleRemoteControl remote = new SimpleRemoteControl(); Light light = new Light(); LightOnCommand lightOn = new LightOnCommand(light); //封装电灯“开”方法的对象。 remote.setCommand(lightOn); //把统一标准对象放入遥控器 remote.buttonWasPressed();
为遥控器实现撤销(undo)按钮
例子:遥控天花板吊扇,风扇有风速档位(low、medium、hight),并且要实现撤销按钮,即打开风扇,撤销后就关闭风扇,让风扇回到打开前状态。
UML图
UML图解
1.CeilingFan为天花板风扇类,包含开关、调档等方式 public class CeilingFan { public static final int HIGH = 3; //风速档位为3档 public static final int MEDIUM = 2; public static final int LOW = 1; public static final int OFF = 0; String location; //天花板的位置 int speed; //当前风速档位 public CeilingFan(String location) { this.location = location; speed = OFF; } public void high() { speed = HIGH; } public void medium() { speed = MEDIUM; } public void low() { speed = LOW; } public void off() { speed = OFF; } public int getSpeed() { return speed; } } 2.Command接口,家用电器执行/撤销动作标准 public interface Command { public void execute(); //执行某个动作 public void undo(); //撤销execute()要执行的动作 } 3.CeilingFanHighCommand类,包含风扇调为最高档位,和撤销到原来状态的方法 public class CeilingFanHighCommand implements Command { CeilingFan ceilingFan; int prevSpeed; //当前风速 public CeilingFanHighCommand(CeilingFan ceilingFan) { this.ceilingFan = ceilingFan; } public void execute() { prevSpeed = ceilingFan.getSpeed(); //调为最高风速前,记下当时的风速,用于撤销动作 ceilingFan.high(); } public void undo() { //撤销动作 if (prevSpeed == CeilingFan.HIGH) { //如果先前是最高风速,撤销当前的动作,把风速调到最高。 ceilingFan.high(); } else if (prevSpeed == CeilingFan.MEDIUM) { ceilingFan.medium(); } else if (prevSpeed == CeilingFan.LOW) { ceilingFan.low(); } else if (prevSpeed == CeilingFan.OFF) { ceilingFan.off(); } } } 4.RemoteControlWithUndo为遥控器类 public class RemoteControlWithUndo { Command[] onCommands; //封装所有“开”命令,遥控器有7个开关 Command[] offCommands; Command undoCommand; //当前的命令 public RemoteControlWithUndo() { onCommands = new Command[7]; //初始化7个开与关命令,命令为noCommand对象,它什么都不做 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(); } }实际动作方式:
RemoteControlWithUndo remoteControl = new RemoteControlWithUndo();
CeilingFan ceilingFan = new CeilingFan("Living Room"); //在客厅天花板上的吊扇
CeilingFanMediumCommand ceilingFanMedium = new CeilingFanMediumCommand(ceilingFan);
//把风扇调到中档,这是个“开”命令
CeilingFanOffCommand ceilingFanOff = new CeilingFanOffCommand(ceilingFan); //关闭风扇命令
remoteControl.setCommand(0, ceilingFanMedium, ceilingFanOff);
remoteControl.onButtonWasPushed(0); //按下“开”
remoteControl.offButtonWasPushed(0); //按下“关”
remoteControl.undoButtonWasPushed(); //按下 “撤销”
NoCommand对象
NoCommand是一个空对象(null object),当你不想返回一个有意义的对象时,空对象就很有用。空对象可以避免我们在程序中写上判断对象是否为空的if等语句。
Party模式
按下一个按钮,就同时能打开音响、电灯、电视、设置好DVD,并让热水器开始加温。
设计一个“宏”命令类
public class MacroCommand implements Command { Command[] commands; //使用命令数组存储一大堆命令 public MacroCommand(Command[] commands) { this.commands = commands; } public void execute() { //这个宏命令被遥控器执行时,就一次性执行数组中的每一个命令 for (int i = 0; i < commands.length; i++) { commands[i].execute(); } } public void undo() { //撤销宏命令,我认为应是:for (int i = commands.length-1; i >0; i--) for (int i = 0; i < commands.length; i++) { commands[i].undo(); } } }实际动作方式
RemoteControl remoteControl = new RemoteControl();
Command[] partyOn = {…………}; //一组“开”命令
Command[] partyOff = { ………...}; //一组“关”命令
MacroCommand partyOnMacro = new MacroCommand(partyOn); //初始化宏命令
MacroCommand partyOffMacro = new MacroCommand(partyOff);
remoteControl.setCommand(0, partyOnMacro, partyOffMacro);
remoteControl.onButtonWasPushed(0); //执行一组“开”命令
remoteControl.offButtonWasPushed(0);
如何学现多层次的撤销操作?
使用一个堆栈记录操作过程的每一个命令,按撤销时从堆栈(后进先出)中取出最上层的命令,调用它的undo方法。
命令模式的更多用途
1.队列请求
把一组命令放到队列(先进先出)中,线程从队列中一个一个删除命令,然后调用它的excecute()方法。
2.日志请求
把所有动作都记录在日志中,在系统发生错误时,重新调用这些动作恢复到之前的状态
Command Pattern的适用场景
1.使用命令模式作为"CallBack"在面向对象系统中的替代。"CallBack"讲的便是先将一个函数登记上,然后在以后调用此函数。
2.需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命期。换言之,原先的请求发出者可能已经不在了,而命令对象本身仍然是活动的。这时命令的接收者可以是在本地,也可以在网络的另外一个地址。命令对象可以在串形化之后传送到另外一台机器上去。
3.系统需要支持命令的撤消(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo()方法,把命令所产生的效果撤销掉。命令对象还可以提供redo()方法,以供客户端在需要时,再重新实施命令效果。
4.如果一个系统要将系统中所有的数据更新到日志里,以便在系统崩溃时,可以根据日志里读回所有的数据更新命令,重新调用Execute()方法一条一条执行这些命令,从而恢复系统在崩溃前所做的数据更新。
总结
Command模式是非常简单而又优雅的一种设计模式,它的根本目的在于将“行为请求者”与“行为实现者”解耦。