• 初识设计模式(命令模式)


    前言:继续学习设计模式,今天学习命令模式,命令模式就是为了将一组行为抽象为对象,实现二者之间的松耦合。

    命令模式(Command Pattern)

    定义:将“请求”封装为对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。

    类图:

      Command:定义命令的接口,声明执行的方法。
      ConcreteCommand:命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
      Receiver:接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
      Invoker:要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
      Client:创建具体的命令对象,并且设置命令对象的接收者。注意这个不是我们常规意义上的客户端,而是在组装命令对象和接收者,或许,把这个Client称为装配者会更好理解,因为真正使用命令的客户端是从Invoker来触发执行。
     
    模式协作:

      1. Client创建一个ConcreteCommand对象并指定他的Receiver对象

      2. 某个Invoker对象存储该ConcreteCommand对象

      3. 该Invoker通过调用Command对象的Execute操作来提交一个请求。若该命令是可撤销的,ConcreteCommand就在执行Execute操作之前存储当前状态以用于取消该命令

      4. ConcreteCommand对象对调用它的Receiver的一些操作以执行该请求

    模式分析:

      1.命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开。  

      2.每一个命令都是一个操作:请求的一方发出请求,要求执行一个操作;接收的一方收到请求,并执行操作。

      3.命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。

      4.命令模式使请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递。

      5.命令模式的关键在于引入了抽象命令接口,且发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。

    优点: 

      1.降低对象之间的耦合度。 

      2.新的命令可以很容易地加入到系统中。

      3.可以比较容易地设计一个组合命令。

      4.调用同一方法实现不同的功能

    缺点:

      1.使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。

    使用场景:  

      当需要将发出请求的对象和执行请求的对象解耦的时候,使用命令模式。

      1.系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。

      2.系统需要在不同的时间指定请求、将请求排队和执行请求。

      3.系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。

      4.系统需要将一组操作组合在一起,即支持宏命令。

    更多用途:

      队列请求:将命令排成一个队列打包,一个个调用 execute 方法,如线程池的任务队列,线程不关心任务队列中是读 IO 还是计算,只取出命令后执行,接着进行下一个。

      日志请求:某些应用需要我们将所有的动作记录在日志中,然后在系统死机等情况出现时,重新调用这些动作恢复到之前的状态。如数据库事务。

      宏命令模式:命令模式 加 组合模式,我们可以将多个命令组合到一起来实现命令的批处理。

    示例:

      这里有一个家电遥控器,上面有一些可以控制家电的开关,有开、关、和一个撤销一步的操作,比如电视机的开关、灯的开关等等。

      其中灯、电视机就是Receiver,遥控器就是Invoker。

      创建灯、电视的实体

    /**
     * 灯
     * Created by yule on 2018/7/21 21:02.
     */
    public class Light {
        public void on(){
            System.out.println("开灯了");
        }
    
        public void off(){
            System.out.println("关灯了");
        }
    }
    /**
     * 电视机
     * Created by yule on 2018/7/21 21:05.
     */
    public class Television {
        public void on(){
            System.out.println("开电视了");
        }
    
        public void off(){
            System.out.println("关电视机了");
        }
    }

      定义命令接口类

    /**
     * 命令接口类
     * Created by yule on 2018/7/21 21:00.
     */
    public interface Command {
        /**
         * 执行操作
         */
        void execute();
    
        /**
         * 撤销操作
         */
        void undo();
    }

      灯实现命令

    /**
     * 开灯命令
     * Created by yule on 2018/7/21 21:02.
     */
    public class LightOnCommand implements Command{
        private Light light;
    
        public LightOnCommand(Light light){
            this.light = light;
        }
    
        @Override
        public void execute() {
            this.light.on();
        }
    
        @Override
        public void undo() {
            //开灯命令的撤销就是关灯。
            this.light.off();
        }
    }
    /**
     * 关灯命令
     * Created by yule on 2018/7/21 21:04.
     */
    public class LightOffCommand implements Command {
        private Light light;
    
        public LightOffCommand(Light light){
            this.light = light;
        }
    
        @Override
        public void execute() {
            this.light.off();
        }
    
        @Override
        public void undo() {
            //关灯命令的撤销就是开灯。
            this.light.on();
        }
    }

      电视实现命令

    /**
     * 电视机开命令
     * Created by yule on 2018/7/21 21:05.
     */
    public class TelevisionOnCommand implements Command{
        private Television television;
    
        public TelevisionOnCommand(Television television){
            this.television = television;
        }
    
        @Override
        public void execute() {
            this.television.on();
        }
    
        @Override
        public void undo() {
            this.television.off();
        }
    }
    /**
     * 电视机关命令
     * Created by yule on 2018/7/21 21:13.
     */
    public class TelevisionOffCommand implements Command{
        private Television television;
    
        public TelevisionOffCommand(Television television){
            this.television = television;
        }
    
        @Override
        public void execute() {
            this.television.off();
        }
    
        @Override
        public void undo() {
            this.television.on();
        }
    }

      定义一个什么都不操作的命令类

    /**
     * 什么都不做的命令类,用于初始化
     * Created by yule on 2018/7/21 21:19.
     */
    public class NoCommand implements Command {
        @Override
        public void execute() {
    
        }
    
        @Override
        public void undo() {
    
        }
    }

      实现家电遥控器

    /**
     * 家电遥控器
     * Created by yule on 2018/7/21 21:15.
     */
    public class RemoteControl {
        Command[] onCommands;
        Command[] offCommands;
        Command undoCommand;//前一个命令将被记录在这里,这里暂时只支持撤销一步
    
        public RemoteControl(int count){
            onCommands = new Command[count];
            offCommands = new Command[count];
    
            //全部初始化为什么都不做的命令类
            for(int i = 0; i < count; i++){
                onCommands[i] = new NoCommand();
                offCommands[i] = new NoCommand();
            }
            undoCommand = new NoCommand();
        }
    
        public void setCommand(int slot, Command onCommand, Command offCommand){
            onCommands[slot] = onCommand;
            offCommands[slot] = offCommand;
        }
    
        /**
         * 点击开启按钮
         * @param slot
         */
        public void onButtonClick(int slot){
            onCommands[slot].execute();
            undoCommand = onCommands[slot];
        }
    
        /**
         * 点击关闭按钮
         * @param slot
         */
        public void offButtonClick(int slot){
            offCommands[slot].execute();
            undoCommand = offCommands[slot];
        }
    
        /**
         * 点击撤销按钮
         */
        public void undoButtonClick(){
            undoCommand.undo();
        }
    
    }

      测试

    /**
     * 测试类
     * Created by yule on 2018/7/21 20:55.
     */
    public class Client {
        public static void main(String[] args){
            //先创建一个控制两个家电的遥控器
            RemoteControl remoteControl = new RemoteControl(2);
    
            //创建家电
            Light light = new Light();
            Television tv = new Television();
    
            //创建家电的命令
            LightOnCommand lightOnCommand = new LightOnCommand(light);
            LightOffCommand lightOffCommand = new LightOffCommand(light);
            TelevisionOnCommand televisionOnCommand = new TelevisionOnCommand(tv);
            TelevisionOffCommand televisionOffCommand = new TelevisionOffCommand(tv);
    
            //给遥控器设置命令
            remoteControl.setCommand(0, lightOnCommand, lightOffCommand);
            remoteControl.setCommand(1, televisionOnCommand, televisionOffCommand);
    
            //操作遥控器
            remoteControl.onButtonClick(0);
            remoteControl.onButtonClick(1);
            remoteControl.offButtonClick(1);
            remoteControl.undoButtonClick();
            remoteControl.offButtonClick(0);
            remoteControl.undoButtonClick();
        }
    }

      运行结果

    如何实现多层的撤销操作:

      这里示例实现的撤销操作,只支持撤销一步,想要达到多层次的撤销操作,就不能只记录最后一个被执行的命令,可以使用堆栈记录操作过程的每一个命令。然后,不管什么时候按下了撤销按钮,都可以从堆栈中取出最上层的命令,然后调用它的 undo() 方法。

    如何按下一个按钮就实现多个按钮的操作: 

      可以使用宏命令。制造一个新的命令实现命令接口类,

    /**
     * 宏命令
     * Created by yule on 2018/7/21 21:37.
     */
    public class MacroCommand implements Command{
        private 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();
            }
        }
    }

     参考:《Head first 设计模式》

  • 相关阅读:
    属性选择器(通常用在input)
    函数调用的文档注释
    List集合操作
    数组排序三种方法
    字符串反序输出字符串
    js中完美运动框架
    查找100-200之间是否存在水仙花数
    提示用户输入一个正整数,如果错误,则重新输入,可以使用以下的代码来保证用户输入正确:
    Ubuntu 16.10下的 jdk 1.8.0_111
    方法内部类
  • 原文地址:https://www.cnblogs.com/yuxiaole/p/9348160.html
Copyright © 2020-2023  润新知