美文网首页
命令模式

命令模式

作者: yangzai | 来源:发表于2018-01-11 09:12 被阅读3次

    命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开。关键在于引入了抽象命令接口,且发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。

    • UML:


      image.png
    • 模型:家电自动遥控器
    • 特点:我们想用一个遥控器来控制多个家电,例如,电灯,音响,空调,电视等。家电是请求的接收者,遥控器是请求的发送者,遥控器上有一些按钮,不同的按钮对应家电的不同操作。抽象命令角色由一个命令接口来扮演,有三个具体的命令类实现了抽象命令接口,这三个具体命令类分别代表三种操作:打开家电、关闭关闭和啥都不干。
    public class Light {
            //灯的位置,卧室,厨房...
        String loc = "";
        public Light(String loc) {
            this.loc = loc;
        }
        public void On() {
            System.out.println(loc + " On");
        }
        public void Off() {
            System.out.println(loc + " Off");
        }
    }
    //音响设备
    public class Stereo {
        static int volume = 0;
        public void On() {
            System.out.println("Stereo On");
        }
        public void Off() {
            System.out.println("Stereo Off");
        }
        public void SetCd() {
            System.out.println("Stereo SetCd");
        }
        public void SetVol(int vol) {
            volume = vol;
            System.out.println("Stereo volume=" + volume);
        }
        public int GetVol() {
            return volume;
        }
        public void Start() {
            System.out.println("Stereo Start");
        }
    }
    //模拟设备厂家提供的遥控器控制接口
    public interface Control {
        public void onButton(int slot);
        public void offButton(int slot);
        public void undoButton();
    }
    

    使用传统的面向对象的设计,一般是创建一个遥控器实例,将灯、音响的实例传给遥控器,然后根据遥控器的按钮设置不同的家电操作,例如按下1号键是音响开,2号键是音响加音量等

    public class TraditionControl implements Control {
        Light light;
        Stereo stereo;
        public TraditionControl(Light light, Stereo stereo) {
            this.light = light;
            this.stereo = stereo;
        }
        @Override
        public void onButton(int slot) {
            // TODO Auto-generated method stub
            switch (slot) {
            case 0:
                light.On();
                break;
            case 1:
                stereo.On();
                break;
            case 2:
                int vol = stereo.GetVol();
                if (vol < 11) {
                    stereo.SetVol(++vol);
                }
                break;
            }
        }
        @Override
        public void offButton(int slot) {
            // TODO Auto-generated method stub
            switch (slot) {
            case 0:
                light.Off();
                break;
            case 1:
                stereo.Off();
                break;
            case 2:
                int vol = stereo.GetVol();
                if (vol > 0) {
                    stereo.SetVol(--vol);
                }
                break;
            }
        }
        @Override
        public void undoButton() {  
        }
    }
    

    这样设计,如果要加一种设备,这种设计的控制器就不好添加了,首先要在控制器中注入设备,按键处理中要再加新设备的处理。所以,这样就高耦合了,不利于扩展。

    • 使用命令模式:
    //将命令抽象处理请求接口
    public interface Command {
        public void execute();
        public void undo();
    }
    //卧室关灯命令
    public class LightOffCommand implements Command {
        private Light light;
        public LightOffCommand(Light light){
            this.light=light;
        }
        @Override
        public void execute() {
            light.Off();
        }
        @Override
        public void undo() {
            light.On();
        }
    }
    //卧室开灯命令
    public class LightOnCommand implements Command {
        private Light light;
        public LightOnCommand(Light light){
            this.light=light;
        }
        @Override
        public void execute() {
            light.On();
        }
        @Override
        public void undo() {
            light.Off();
        }
    }
    //操作无响应命令
    public class NoCommand implements Command {
        @Override
        public void execute() {}
        @Override
        public void undo() {}
    }
    //音响开命令
    public class StereoOnCommand implements Command {
        private Stereo setreo;
        public StereoOnCommand(Stereo setreo){
            this.setreo=setreo;
        }
        @Override
        public void execute() {
            setreo.On();
            setreo.SetCd();
        }
        @Override
        public void undo() {
            setreo.Off();
        }
    }
    //音响关命令
    public class StereoOffCommand implements Command {
        private Stereo setreo;
        public StereoOffCommand(Stereo setreo){
            this.setreo=setreo;
        }
        @Override
        public void execute() {
            setreo.Off();
        }
        @Override
        public void undo() {
            setreo.On();
            setreo.SetCd();
        }
    }
    //音响加音量
    public class StereoAddVolCommand implements Command{
        private Stereo setreo;
        public StereoAddVolCommand(Stereo setreo){
            this.setreo=setreo;
        }
        @Override
        public void execute() {
        int vol=    setreo.GetVol();
        if(vol<11){
            setreo.SetVol(++vol);
        }   
        }
        @Override
        public void undo() {
        int vol=    setreo.GetVol();
        if(vol>0){
            setreo.SetVol(--vol);
        }   
        }
    }
    //音响减音量命令
    public class StereoSubVolCommand implements Command{
        private Stereo setreo;
        public StereoSubVolCommand(Stereo setreo){
            this.setreo=setreo;
        }
        @Override
        public void execute() {
        int vol=    setreo.GetVol();
        if(vol>0){
            setreo.SetVol(--vol);
        }   
        }
        @Override
        public void undo() {
        int vol=    setreo.GetVol();
        if(vol<11){
            setreo.SetVol(++vol);
        }   
        }
    }
    

    //当有了这样命令之后,我们只需要将命令设置给遥控器的不同的按键即可,用户在按键时会请求执行已经设定好的命令,由命令再调用具体家电执行。

    public class CommandModeControl implements Control{
           //所有开命令
        private Command[] onCommands;
          //所有关命令
        private Command[] offCommands;
           //记录上一个执行的命令
        private Stack<Command> stack=new Stack<Command>();
        public CommandModeControl(){
            onCommands=new Command[5];
             offCommands=new Command[5];
             Command noCommand=new NoCommand();
                     // 初始化为无操作命令
             for(int i=0,len=onCommands.length;i<len;i++) {
                 onCommands[i]=noCommand;
                 offCommands[i]=noCommand;
             } 
        }
           //设置具体按键命令
        public void setCommand(int slot,Command onCommand,Command offCommand){
            onCommands[slot]=onCommand;
             offCommands[slot]=offCommand;
        }
        @Override
        public void onButton(int slot) {
                    //执行命令
            onCommands[slot].execute();
            stack.push(onCommands[slot]);
        }
        @Override
        public void offButton(int slot) {
            offCommands[slot].execute();
            stack.push(offCommands[slot]);
        }
        @Override
        public void undoButton() {
            stack.pop().undo();
        }
    }
    //使用遥控器
    public class ControlTest {
        public static void main(String[] args) {
                    //创建遥控器,也就是UML中的Invoker实现类
            CommandModeControl control = new CommandModeControl();
            Light bedroomlight = new Light("BedRoom");
            Light kitchlight = new Light("Kitch");
            Stereo stereo = new Stereo();
            //设置各种命令
            LightOnCommand bedroomlighton = new LightOnCommand(bedroomlight);
            LightOffCommand bedroomlightoff = new LightOffCommand(bedroomlight);
            LightOnCommand kitchlighton = new LightOnCommand(kitchlight);
            LightOffCommand kitchlightoff = new LightOffCommand(kitchlight);
    
             Command[] oncommands={bedroomlighton,kitchlighton};
             Command[] offcommands={bedroomlightoff,kitchlightoff};
    
            
            StereoOnCommand stereoOn = new StereoOnCommand(stereo);
            StereoOffCommand stereoOff = new StereoOffCommand(stereo);
            StereoAddVolCommand stereoaddvol = new StereoAddVolCommand(stereo);
            StereoSubVolCommand stereosubvol = new StereoSubVolCommand(stereo);
                    //将命令注入到遥控器中
            control.setCommand(0, bedroomlighton, bedroomlightoff);
            control.setCommand(1, kitchlighton, kitchlightoff);
            control.setCommand(2, stereoOn, stereoOff);
            control.setCommand(3, stereoaddvol, stereosubvol);
                  //操作遥控器就能执行已经绑定好的命令控制家电了
            control.onButton(0);
            control.undoButton();
            //control.offButton(0);
            control.onButton(1);
            control.offButton(1);
            control.onButton(2);
            control.onButton(3);
                    
            control.offButton(3);
            control.undoButton();
            control.offButton(2);
            control.undoButton();
        }
    }
    

    通过上面的设计,遥控器不需要注入家电设备,而是注入各种命令。如果新加一种家电,则只需要扩展新的命令,并注入到遥控器中即可。遥控器不关心家电的实现,它是调用命令接口,解耦了遥控器与家电。但是可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类。

    相关文章

      网友评论

          本文标题:命令模式

          本文链接:https://www.haomeiwen.com/subject/pofznxtx.html