Head First设计模式六-----命令模式(Command Pattern)

http://blog.sina.com.cn/s/blog_5016113a01009rta.html
命令模式定义
    将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作
命令模式可以将“动作的请求者”和“动作的执行者”分隔开来(解耦)

例子:设计一个家电自动化遥控器的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模式是非常简单而又优雅的一种设计模式,它的根本目的在于将“行为请求者”与“行为实现者”解耦。

你可能感兴趣的:(设计模式,PHP,活动,Blog,UML)