美文网首页设计模式程序员设计模式
设计模式:第六篇--命令模式

设计模式:第六篇--命令模式

作者: Jorgezhong | 来源:发表于2018-09-30 10:17 被阅读43次

    命令模式模型

    命令模式:将“请求”封装成对象,以便使用不同的请求,队列或日志来参数化其他对象,支持撤销命令的操作。实现“动作的请求者”从“动作的执行者”中解耦。

    命令模式

    理解:很简单的一个思想,抽象了命令,同时提供了操作这个命令对象的角色Invoker。命令请求于命令执行,两个东西是耦合到一起的。抽象了命令,和提供了Invoker之后。Client可以向Invoker提出具体的命令请求。具体的命令是耦合具体的Receiver(具体的命令执行者)。由Invoker去封装命令并且发出“执行命令的请求”。这个过程,其实是将命令请求从 client-->Receiver 转移到 Client-->Invoker-->Receiver。抽象和封装有时候就是这样,多一个角色去协调,那么便可以拓展和管理被协调的两方面了。

    /**
     * Project <demo-project>
     * Created by jorgezhong on 2018/9/26 10:22.
     *
     * 命令模式的Command:抽象命令对象
     */
    public interface Command {
    
        void execute();
    
    }
    
    /**
     * Project <demo-project>
     * Created by jorgezhong on 2018/9/26 10:24.
     *
     * 命令模式的Command:具体命令对象
     */
    public class ConcreteCommand implements Command{
    
        //接收命令对象
        Receiver receiver;
    
        public ConcreteCommand(Receiver light) {
            this.receiver = light;
        }
    
        @Override
        public void execute() {
            this.receiver.action();
        }
    }
    
    /**
     * Project <demo-project>
     * Created by jorgezhong on 2018/9/26 10:26.
     *
     * 命令模式的Invoker:调用者对象,调用者传递命令
     */
    public class Invoker {
    
        //插槽持有命令
        Command command;
    
        /**
         * 设置命令
         * @param command
         */
        public void setCommand(Command command) {
            this.command = command;
        }
    
        /**
         * 将命令封装进按下按钮动作中
         */
        public void buttonWasPressed() {
            command.execute();
        }
    
    }
    
    
    /**
     * Project <demo-project>
     * Created by jorgezhong on 2018/9/26 10:23.
     *
     * 命令模式的Receiver:接收者(具体动作执行者)
     */
    public class Receiver {
    
        /**
         * 具体的命令要执行的动作
         */
        void action(){
        }
    
    }
    
    /**
     * Project <demo-project>
     * Created by jorgezhong on 2018/9/26 10:47.
     *
     * client:命令模式的客户,客户通过Invoker设置命令
     */
    public class Client {
    
        public static void main(String[] args) {
    
            Invoker invoker = new Invoker();
            
            Receiver receiver = new Receiver();
            Command command = new ConcreteCommand(receiver);
    
            invoker.setCommand(command);
            invoker.buttonWasPressed();
    
        }
    
    }
    
    

    案例:家居遥控器

    需求:装修了,换一套智能家居。需要遥控器来控制家里的一些,比如灯,音响之类的一些家具。
    思考:原来我需要到开关上去控制开关,现在转移到遥控器上了,命令的请求从命令的执行者,转移到协调者(Invoker)去了。这不正符合命令模式吗。用命令模式设计一下。

    • 第一步:首先是实际的执行者(Receiver)
    public class Light {
    
        private String description;
    
        public Light(String description) {
            this.description = description;
        }
    
        public void on() {
            System.out.println(description + " on");
        }
    
        public void off() {
            System.out.println(description + " off");
        }
        
    }
    
    public class Stereo {
    
        private String description;
    
        public Stereo(String description) {
            this.description = description;
        }
    
        public void on() {
            System.out.println(description + "Stereo on");
        }
    
        public void off() {
            System.out.println(description + "Stereo off");
        }
    
        public void setCD(){
            System.out.println(description + "Stereo set CD");
        }
    
        public void setVolume(int volume){
            System.out.println(description + "Stereo set volume is " + volume);
        }
    
    }
    
    • 第二步:抽象命令接口,提供向Receiver请求执行具体命令的具体命令对象。
    public interface Command {
        void execute();
    }
    
    public class LightOffCommand implements Command {
    
        private Light light;
    
        public LightOffCommand(Light light) {
            this.light = light;
        }
    
        @Override
        public void execute() {
            light.off();
        }
    }
    
    public class LightOnCommand implements Command {
    
        private Light light;
    
        public LightOnCommand(Light light) {
            this.light = light;
        }
    
        @Override
        public void execute() {
            light.on();
        }
    }
    
    public class StereoOnWithCDComand implements Command {
    
        private Stereo stereo;
    
        public StereoOnWithCDComand(Stereo stereo) {
            this.stereo = stereo;
        }
    
        @Override
        public void execute() {
            stereo.on();
            stereo.setCD();
            stereo.setVolume(11);
        }
    }
    
    public class StereoOffWithCDComand implements Command {
    
        private Stereo stereo;
    
        public StereoOffWithCDComand(Stereo stereo) {
            this.stereo = stereo;
        }
    
        @Override
        public void execute() {
            stereo.off();
        }
    }
    
    public class NoCommand implements Command {
        /**
         * 空对象,什么都不做
         */
        @Override
        public void execute() {
    
        }
    }
    
    • 第三步:提供遥控器向Receiver请求命令之间的协调者(Invoker)
    public class RemoteControl {
    
        /**
         * 记录命令
         */
        private Command[] onCommands;
        private Command[] offCommands;
    
    
        public RemoteControl() {
            
            //设置7个命令插槽
            onCommands = new Command[7];
            offCommands = new Command[7];
    
            NoCommand noCommand = new NoCommand();
    
            for (int i = 0; i < 7; i++) {
                onCommands[i] = noCommand;
                offCommands[i] = noCommand;
            }
    
        }
    
        /**
         * 提供设置插槽命令的方法
         * @param slot
         * @param onCommand
         * @param offCommand
         */
        public void setCommand(int slot, Command onCommand, Command offCommand) {
            this.onCommands[slot] = onCommand;
            this.offCommands[slot] = offCommand;
        }
    
        /**
         * 封装出来的具体命令动作
         * @param slot
         */
        public void onButtonWasPushed(int slot) {
            onCommands[slot].execute();
        }
    
        /**
         * 封装出来的具体命令动作
         * @param slot
         */
        public void offButtonWasPushed(int slot) {
            offCommands[slot].execute();
        }
    
        @Override
        public String toString() {
    
            StringBuffer stringBuffer = new StringBuffer();
            for (int i = 0; i < onCommands.length; i++) {
                stringBuffer.append("[")
                        .append("slot ")
                        .append(i)
                        .append("]")
                        .append(onCommands[i].getClass().getName())
                        .append("   ")
                        .append(offCommands[i].getClass().getName())
                        .append("\n");
            }
    
            return stringBuffer.toString();
    
    
        }
    }
    
    
    • 第四步:实现遥控器(client)
    public class RemoteLoader {
    
        public static void main(String[] args) {
            //1、获取Invoker,协调者
            RemoteControl remoteControl = new RemoteControl();
    
            //2、设置Reveiver,实际命令执行者
            Light livingRoomLight = new Light("Living Room");
            Light kitchenLight = new Light("Kitchen");
            Stereo livingRoomStereo = new Stereo("Living Room");
    
            //设置命令,并绑定实际命令执行者
            LightOffCommand livingRoomLightOff= new LightOffCommand(livingRoomLight);
            LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
    
            LightOffCommand kitchenLightOff = new LightOffCommand(kitchenLight);
            LightOnCommand kitchenLightOn = new LightOnCommand(kitchenLight);
    
            StereoOnWithCDComand stereoOnWithCDComand = new StereoOnWithCDComand(livingRoomStereo);
            StereoOffWithCDComand stereoOffWithCDComand = new StereoOffWithCDComand(livingRoomStereo);
    
            //3、封装命令到Invoker协调者
            remoteControl.setCommand(0,livingRoomLightOn,livingRoomLightOff);
            remoteControl.setCommand(1,kitchenLightOn,kitchenLightOff);
            remoteControl.setCommand(2,stereoOnWithCDComand,stereoOffWithCDComand);
    
            System.out.println(remoteControl);
    
            //4、Invoker,协调者发出执行命令的请求
            remoteControl.onButtonWasPushed(0);
            remoteControl.offButtonWasPushed(0);
    
            remoteControl.onButtonWasPushed(1);
            remoteControl.offButtonWasPushed(1);
    
            remoteControl.onButtonWasPushed(2);
            remoteControl.offButtonWasPushed(2);
    
        }
    }
    

    总结:命令模式的核心在于抽象命令,并提供命令请求的协调者。怎加一个角色解耦另外两个角色。实现拓展。

    撤销命令

    需求:命令支持撤销
    思考:本质上撤销就是执行历史命令,或者执行乡放的命令。
    做法:命令接口提供与execute()方法相反的undo()方法,具体命令提供实现即可。

    public interface Command {
    
        void execute();
    
        void undo();
    }
    
    
    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 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 RemoteControlWithUndo {
    
        /**
         * 记录命令
         */
        private Command[] onCommands;
        private Command[] offCommands;
        
        //记录要撤销的命令
        private Command undoCommand;
    
        public RemoteControlWithUndo() {
    
            //设置7个命令插槽
            onCommands = new Command[7];
            offCommands = new Command[7];
    
            NoCommand noCommand = new NoCommand();
    
            for (int i = 0; i < 7; i++) {
                onCommands[i] = noCommand;
                offCommands[i] = noCommand;
            }
    
            undoCommand = noCommand;
    
        }
    
        /**
         * 提供设置插槽命令的方法
         *
         * @param slot
         * @param onCommand
         * @param offCommand
         */
        public void setCommand(int slot, Command onCommand, Command offCommand) {
            this.onCommands[slot] = onCommand;
            this.offCommands[slot] = offCommand;
        }
    
        /**
         * 封装出来的具体命令动作
         *
         * @param slot
         */
        public void onButtonWasPushed(int slot) {
            onCommands[slot].execute();
            undoCommand = onCommands[slot];
        }
    
        /**
         * 封装出来的具体命令动作
         *
         * @param slot
         */
        public void offButtonWasPushed(int slot) {
            offCommands[slot].execute();
            undoCommand = offCommands[slot];
        }
    
        /**
         * 撤销方法
         */
        public void undoButtonWasPushed() {
            undoCommand.undo();
        }
    
        @Override
        public String toString() {
    
            StringBuffer stringBuffer = new StringBuffer();
            for (int i = 0; i < onCommands.length; i++) {
                stringBuffer.append("[")
                        .append("slot ")
                        .append(i)
                        .append("]")
                        .append(onCommands[i].getClass().getName())
                        .append("   ")
                        .append(offCommands[i].getClass().getName())
                        .append("\n");
            }
    
            return stringBuffer.toString();
    
    
        }
    }
    
    

    使用并测试

    public class RemoteLoader {
    
        public static void main(String[] args) {
            //1、获取Invoker,协调者
            //RemoteControl remoteControl = new RemoteControl();
            //使用带撤销命令的协调者
            RemoteControlWithUndo remoteControl = new RemoteControlWithUndo();
    
            //2、设置Reveiver,实际命令执行者
            Light livingRoomLight = new Light("Living Room");
            Light kitchenLight = new Light("Kitchen");
            Stereo livingRoomStereo = new Stereo("Living Room");
    
            //设置命令,并绑定实际命令执行者
            LightOffCommand livingRoomLightOff= new LightOffCommand(livingRoomLight);
            LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
    
            LightOffCommand kitchenLightOff = new LightOffCommand(kitchenLight);
            LightOnCommand kitchenLightOn = new LightOnCommand(kitchenLight);
    
            StereoOnWithCDComand stereoOnWithCDComand = new StereoOnWithCDComand(livingRoomStereo);
            StereoOffWithCDComand stereoOffWithCDComand = new StereoOffWithCDComand(livingRoomStereo);
    
            //3、封装命令到Invoker协调者
            remoteControl.setCommand(0,livingRoomLightOn,livingRoomLightOff);
            remoteControl.setCommand(1,kitchenLightOn,kitchenLightOff);
            remoteControl.setCommand(2,stereoOnWithCDComand,stereoOffWithCDComand);
    
            System.out.println(remoteControl);
    
            //4、Invoker,协调者发出执行命令的请求
            remoteControl.onButtonWasPushed(0);
            remoteControl.offButtonWasPushed(0);
    
            remoteControl.onButtonWasPushed(1);
            remoteControl.offButtonWasPushed(1);
    
            //撤销命令
            System.out.println("撤销命令:");
            remoteControl.undoButtonWasPushed();
    
            remoteControl.onButtonWasPushed(2);
            remoteControl.offButtonWasPushed(2);
    
        }
    
    }
    
    

    备注:如果要撤销一组命令,可以将记录命令的对象改为记录命令对象的堆栈,每次按下撤销只要调用栈顶对象的undo方法即可

    宏命令

    需求:现在遥控器要可以一键打开或关闭厨房、客厅的灯。
    思考:拓展一个宏命令对象,组长其他命令即可。

    public class MacroCommand implements Command {
    
        Command[] commands;
    
        public MacroCommand(Command[] commands) {
            this.commands = commands;
        }
    
        @Override
        public void execute() {
            for (Command command : commands) {
                command.execute();
            }
        }
    
        @Override
        public void undo() {
            for (Command command : commands) {
                command.undo();
            }
        }
    }
    
    
    public class RemoteLoader {
    
        public static void main(String[] args) {
            //1、获取Invoker,协调者
            //RemoteControl remoteControl = new RemoteControl();
            //使用带撤销命令的协调者
            RemoteControlWithUndo remoteControl = new RemoteControlWithUndo();
    
            //2、设置Reveiver,实际命令执行者
            Light livingRoomLight = new Light("Living Room");
            Light kitchenLight = new Light("Kitchen");
            Stereo livingRoomStereo = new Stereo("Living Room");
    
            //设置命令,并绑定实际命令执行者
            LightOffCommand livingRoomLightOff= new LightOffCommand(livingRoomLight);
            LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
    
            LightOffCommand kitchenLightOff = new LightOffCommand(kitchenLight);
            LightOnCommand kitchenLightOn = new LightOnCommand(kitchenLight);
    
            StereoOnWithCDComand stereoOnWithCDComand = new StereoOnWithCDComand(livingRoomStereo);
            StereoOffWithCDComand stereoOffWithCDComand = new StereoOffWithCDComand(livingRoomStereo);
    
            //提供开关宏命令
            MacroCommand partyOn = new MacroCommand(new Command[]{livingRoomLightOn, kitchenLightOn});
            MacroCommand partyOff = new MacroCommand(new Command[]{livingRoomLightOff, kitchenLightOff});
    
            //3、封装命令到Invoker协调者
            remoteControl.setCommand(0,livingRoomLightOn,livingRoomLightOff);
            remoteControl.setCommand(1,kitchenLightOn,kitchenLightOff);
            remoteControl.setCommand(2,stereoOnWithCDComand,stereoOffWithCDComand);
            remoteControl.setCommand(3,partyOn,partyOff);
    
            System.out.println(remoteControl);
    
            //4、Invoker,协调者发出执行命令的请求
            remoteControl.onButtonWasPushed(0);
            remoteControl.offButtonWasPushed(0);
    
            remoteControl.onButtonWasPushed(1);
            remoteControl.offButtonWasPushed(1);
    
            //撤销命令
            System.out.println("撤销命令:");
            remoteControl.undoButtonWasPushed();
    
            remoteControl.onButtonWasPushed(2);
            remoteControl.offButtonWasPushed(2);
            
            //执行宏命令
            remoteControl.onButtonWasPushed(3);
            remoteControl.offButtonWasPushed(3);
        }
    
    }
    

    总结:

    命令模式封装的是请求:其实是将计算打包。可以将动作的执行顺序管理起来。解耦的是请求命令的对象以及执行命令的对象。

    应用场景:命令模式封装的是请求,在实际应用中,请求一般都是跟队列相关。那么命令模式应用场景其实还是蛮多的。比如说工作队列,日程安排,线程池这些。日志请求也是一个方面,设置还能记录撤销,根据日志队列取出命令请求,还可以撤销命令达到恢复的目的。

    相关文章

      网友评论

        本文标题:设计模式:第六篇--命令模式

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