命令模式:将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。支持可撤销的操作。
把方法调用封装起来
1. 餐厅是怎么工作的
订单:用来请求餐点的对象,可以被传递;接口只有orderUp方法,封装了的准备餐点所需的动作;订单内有个需要准备工作的对象(厨师)的引用
女招待员只接受订单,然后调用订单的orderUp()方法
快餐厨师具备准备餐点的知识,女招待和厨师间彻底的解耦,不需要进行沟通
1. 客户创建一个命令对象
2. 客户利用setCommand()将命令对象存储在调用者中
3. 稍后。。。客户要求调用者执行命令。
一旦命令被加载到调用者,该命令可以被使用并丢弃,或者可以被保留下来并使用多次
2. 家电自动化,遥控器API
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();
}
}
当有多个插槽的时候,每个插槽都具有“开”和“关”按钮。遥控器如何分辨 客厅或厨房 的电灯呢?
遥控器无法识别,创建命令并将其加载到遥控器时,我们创建的命令是两个LightCommand,一个绑定到客厅电灯对象,一个绑定到厨房的电灯对象。
命令中封装了请求的接受者,所以不要理会哪个电灯,只要exectute()被调用。
public class RemoteLoader {
/**
* @param args
*/
public static void main(String[] args) {
ComplexRemoteControl crc = new ComplexRemoteControl();
Light livingRoomLight = new Light("Living Room");
Light kitchenRoomLight = new Light("Kitchen Room");
LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
LightOnCommand kitchenRoomLightOn = new LightOnCommand(kitchenRoomLight);
LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);
LightOffCommand kitchenRoomLightOff = new LightOffCommand(kitchenRoomLight);
crc.setCommand(0, livingRoomLightOn, livingRoomLightOff);
crc.setCommand(1, kitchenRoomLightOn, kitchenRoomLightOff);
System.out.println(crc.toString());
crc.onButtonWasPushed(0);
crc.onButtonWasPushed(1);
crc.offButtonWasPushed(0);
crc.offButtonWasPushed(1);
}
}
主要设计目标:
让遥控器代码尽可能简单;
新的厂商类一旦出现,遥控器并不需要随之修改,从逻辑上将遥控器的类和厂商的类解耦;
降低遥控器的生产成本,大大减少未来维护时所需费用
[1] 厂商类被用来控制特定家电自动化装置 Light
[2] Command接口,每个动作都被是现成一个简单的命令对象,持有对一个厂商类的实例的引用
[3] ReomteControl管理一组命令对象,每个按钮都有一个命令对象
[4] RemoteLoader创建许多命令对象,然后加载到遥控器的插槽中
3. 取消操作
undo
4. 使用状态实现撤销
电风扇允许有多种转动速度
public class CeilingFanHighCommand implements Command {
CeilingFan ceilingFan;
int preSpeed;
public CeilingFanHighCommand(CeilingFan ceilingFan){
this.ceilingFan = ceilingFan;
}
public void execute() {
preSpeed = ceilingFan.getSpeed();
ceilingFan.high();
}
public void undo() {
if(preSpeed == CeilingFan.HIGH){
ceilingFan.high();
}else if(preSpeed == CeilingFan.MEDIUM){
ceilingFan.medium();
}else if(preSpeed == CeilingFan.LOW){
ceilingFan.low();
}else if(preSpeed == CeilingFan.OFF){
ceilingFan.off();
}
}
}
5。 拥有一个遥控器,需要光凭按下一个按钮,同时能打开和关闭电视
使用宏命令
public class MacroCommand implements Command {
Command[] commands;
public MacroCommand(Command[] commands){
this.commands = commands;
}
public void execute() {
for(Command c : commands){
c.execute();
}
}
public void undo() {
}
}
Command[] partyON = {lightOn, stereOn, tvOn, hottubOn};
MacroCommand partyOnMacro = new MacroCommand ( partyON );
remoteContol.setCommand(0, partyOnMacro , null);
6. 如何实现多层次的撤销操作 : 使用堆栈记录操作过程的每一个命令,不管什么时候按下撤销操作按钮,取最上层的命令,调用它的undo方法
命令对象不完成execute方法的细节? 调用者和接收者之间的解耦程度
7. 命令可以将运算块打包(一个接收者和一组动作,然后将它传来传去,就像是一般的对象一样)
日程安排(Schedual), 线程池,工作队列
从队列中取出一个命令,调用它的execute,等待这个调用完成,然后将此命令丢弃,再取出下一个命令……
工作队列类和进行计算的对象之间完全解耦
所有记录都记录在日志中,并能在系统死机之后,重新调用这些动作恢复到之前的状态。新增 store() 和 load()
8 例子
AudioPlayer系统,播音play,倒带rewind,停止stop功能,全都由键盘具体实施