• 命令模式


    1.定义

      将"请求"封装成对象,以便使用不同的请求,队列或者日志来参数化其他对象.命令模式也支持可撤销的操作.

      注:命令模式把接收者组合到命令中,来实现客户端调用命令执行后的动作.因为最后都是接收者要执行动作.

    2.代码实现

     这边以遥控器为例子,通过点击不同的按钮来发送不同的命令,相应的各种电器收到命令后执行不同的动作.所以我们要做的就是给遥控器按钮添加命令.

    定义一个Command命令接口,这是所有命令的抽象

    public interface Command {
        public void execute();
    }

    假设有三个命令接收者,也就是说我的遥控器要控制以下三个对象,电灯,车库,音响

    public class Light {
        private String name;
        
        public Light(String name) {
            super();
            this.name = name;
        }
        public void on() {
            System.out.println("Light is on");
        }
        public void off() {
            System.out.println("Light is off");
        }
        
    }
    public class GarageDoor {
        public void up() {
            System.out.println("GarageDoor up");
        }
        public void down() {
            System.out.println("GarageDoor down");
        }
        public void stop() {
            System.out.println("GarageDoor stop");
        }
        public void lightOn() {
            System.out.println("GarageDoor lightOn");
        }
        public void lightOff() {
            System.out.println("GarageDoor lightOff");
        }
    }
    public class Stereo {
        private String name;
        
        
        public Stereo(String name) {
            super();
            this.name = name;
        }
        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 setDvd() {
            System.out.println("Stereo setDvd");
        }
        public void setRadio() {
            System.out.println("Stereo setRadio");
        }
        public void setVolume() {
            System.out.println("Stereo setVolume");
        }
        
    }

    因为遥控器只有开和关的按钮,所以我们针对这三个接收者做出了三组命令

    打开电灯,关闭电灯

    package command;
    
    public class LightOnCommand implements Command{
        Light light;
        
        public LightOnCommand(Light light) {
            this.light = light;
        }
        @Override
        public void execute() {
            light.on();
        }
    
    }
    package command;
    
    public class LightOffCommand implements Command{
        Light light;
        
        public LightOffCommand(Light light) {
            this.light = light;
        }
        @Override
        public void execute() {
            light.off();
        }
    }

    可以看到,里面定义了接收者,然后实现了Command接口并且调用接收者的方法来实现execute方法.命令模式中,命令基本上都包含这接收者一起传递给调用者.

    在定义

    车库开,车库关

    package command;
    
    public class GarageDoorCloseCommand implements Command{
        GarageDoor garageDoor;
    
        
        public GarageDoorCloseCommand(GarageDoor garageDoor) {
            super();
            this.garageDoor = garageDoor;
        }
    
        public void setGarageDoor(GarageDoor garageDoor) {
            this.garageDoor = garageDoor;
        }
    
        @Override
        public void execute() {
            garageDoor.down();
        }
    }
    package command;
    
    public class GarageDoorOpenCommand implements Command{
        GarageDoor garageDoor;
    
        
        public GarageDoorOpenCommand(GarageDoor garageDoor) {
            super();
            this.garageDoor = garageDoor;
        }
    
        public void setGarageDoor(GarageDoor garageDoor) {
            this.garageDoor = garageDoor;
        }
    
        @Override
        public void execute() {
            garageDoor.up();
        }
        
    }

    定义

    音响开,音响关

    package command;
    
    public class StereoOffWithCDCommand implements Command {
        private Stereo stereo;
        
        public StereoOffWithCDCommand(Stereo stereo) {
            super();
            this.stereo = stereo;
        }
    
        @Override
        public void execute() {
            stereo.off();
        }
    
    }
    package command;
    
    public class StereoOnWIthCDCommand implements Command {
        private Stereo stereo;
        
        public StereoOnWIthCDCommand(Stereo stereo) {
            super();
            this.stereo = stereo;
        }
    
        @Override
        public void execute() {
            stereo.on();
            stereo.setCd();
            stereo.setVolume();
        }
    
    }

    这些命令基本上都实现了Command接口

    接下来定义遥控器

    package command;
    
    public class RemoteControl {
        Command[] onCommands;
        Command[] offCommands;
        
        public RemoteControl() {
            onCommands = new Command[7];
            offCommands = new Command[7];
            
            Command noCommand = new NoCommand();
            for (int i = 0; i < onCommands.length; i++) {
                onCommands[i] = noCommand;
                offCommands[i] = noCommand;
            }
        }
        
        public void setCommand(int slot, Command onCommand, Command offCommand) {
            onCommands[slot] = onCommand;
            offCommands[slot] = offCommand;
        }
        
        public void onButtonWasPushed(int slot) {
            onCommands[slot].execute();
        }
        
        public void offButtonWasPushed(int slot) {
            offCommands[slot].execute();
        }
        
    //    
        
        
    }

    因为遥控器只有开和关,所以我们分别定义Command数组,构造函数中可以自定义命令数组的大小,初始化的时候赋空值.给按钮赋命令的时候也是根据两个数组下标来赋值.

    在这里可以看到,RemoteControl只关心命令Command,而并不关心具体是哪一个命令,在按下按钮的时候只需要调用Command的方法就可以执行相应的动作.

    接下来是测试类

    package command;
    
    public class RemoteLoader {
        public static void main(String[] args) {
            RemoteControl remoteControl = new RemoteControl();
            
            //定义命令接收者
            Light livingRoomLight = new Light("Living Room");
            Stereo stereo = new Stereo("Living Room");
            GarageDoor garageDoor = new GarageDoor();
            
            //定义命令
            LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
            LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);
            GarageDoorCloseCommand garageDoorCloseCommand = new GarageDoorCloseCommand(garageDoor);
            GarageDoorOpenCommand garageDoorOpenCommand = new GarageDoorOpenCommand(garageDoor);
            StereoOffWithCDCommand stereoOffWithCDCommand = new StereoOffWithCDCommand(stereo);
            StereoOnWIthCDCommand stereoOnWIthCDCommand = new StereoOnWIthCDCommand(stereo);
            //设置命令到remoteControl
            remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
            remoteControl.setCommand(1, garageDoorOpenCommand, garageDoorCloseCommand);
            remoteControl.setCommand(2, stereoOnWIthCDCommand, stereoOffWithCDCommand);
            
            //调用
            remoteControl.offButtonWasPushed(0);
            remoteControl.onButtonWasPushed(1);
            
            
        }
    }

    结果为

    Light is off
    GarageDoor up

    3.总结

    命令模式最主要的就是把接收者放在了命令中,通过调用接收者的方法来实现命令的execute功能.

    这里还有几个拓展功能:

    3.1 撤销

    之前定义的时候说有撤销,这边是写下具体的思路,代码都是简单的重复代码,这里就不写一遍了.

    public interface Command {
        public void execute();
        
        public void undo();//撤销
    }

    只需要在Command接口中添加撤销方法,并且在子类中实现undo方法,以遥控器为例子,只需要调用接收者相反的方法即可.

    然后在RemoteLoader这个类中定义一个临时变量来储存上一个变量,撤销的时候直接调用这个临时变量的undo方法即可.如果是需要多次撤销.可以用栈来储存命令,然后挨个调用undo方法即可.

    3.2宏命令

    我们可以直接定义一个宏命令对象

    public class MacroCommand implements Command{
        private Command[] command;
        
        
        public MacroCommand(Command[] command) {
            super();
            this.command = command;
        }
    
        @Override
        public void execute() {
            for (int i = 0; i < command.length; i++) {
                command[i].execute();
            }
        }
    
    }

    然后通过构造函数来传入想组合的命令即可.

    比如:

    Command[] command = {livingRoomLightOn, livingRoomLightOff};
    MacroCommand mc = new MacroCommand(command);

    直接把想组合的命令定义成数组,传入即可组成一个宏命令!

    队列也是命令模式,队列直接把命令一个一个取出来,直接调用即可,因为命令中已经包含了接收者.

  • 相关阅读:
    每日英语:Why Sit Up Straight?
    每日英语:Doctor's Orders: 20 Minutes Of Meditation Twice A Day
    每日英语:SamsungApple Patent Fight: Is It Worth It?
    每日英语:Better Than Buffett, This Investor Made Me Rich for Life
    每日英语:Coming Soon to China: AtHome Toxic Food Test Kits
    去了家新公司,技术总监不让用 IntelliJ IDEA!!想离职了。。
    IDEA 进行远程 Debug,这个太强了。。
    雷军做程序员时写的博客,太牛了。。
    Spring Boot 如何获取 Controller 方法名和注解信息?
    sql 2005/2008中直接创建web service,允许客户机通过HTTP访问
  • 原文地址:https://www.cnblogs.com/lishuaiqi/p/11161110.html
Copyright © 2020-2023  润新知