• 设计模式——命令模式(遥控器与灯)


    本文首发于cdream的个人博客,点击获得更好的阅读体验!

    欢迎转载,转载请注明出处。

    本文主要对命令模式进行概述讲解,并使用使用遥控器与灯来讲述命令模式中调用者与接收者的关系。

    image-20181213000538037

    一、概述

    命令模式(英语:Command pattern)是一种设计模式,它尝试以对象来代表实际行动。命令对象可以把行动(action) 及其参数封装起来,于是这些行动可以被重复使用、撤销、撤销后重做。

    这个是概念是来自维基百科,我觉得最容易理解,就是把命令封装成对象,使命令可以重复调用、撤销,降低了调用者和接受者的耦合,同时容易扩展出新的命令。

    其他描述
    1.将“请求”封装成对象,以便使不同的请求、队列或日志来参数化其他对象,命令模式也支持可撤销操作。(Head First 设计模式)
    2.命令模式把一个请求或者操作封装到一个对象中。命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。(Java与模式)

    大家做适当参考,不理解可以先看看下面的源码!

    二、结构

    如图,这是命令模式的结构:

    image-20181212211929846

    以用遥控器开灯为例(Head First 设计模式例子)

    • Invoker:命令调用者,负责调用命令对象的请求——遥控器

    • Command:声明了一个具体命令类实现的接口——命令的接口

    • ConcreteCommand:具体命令,实现了Invoker和Receiver之间的解耦,通常持有接收者对象的饮用——开灯按钮执行的命令

    • Receiver:命令接收者,真正接收命令并执行动作的对象——

    • Client:客户端,创造具体的命令,并确定接收者

    三、源代码

    Receiver——二档亮度的灯

    // 二档调节的灯,在这里作为接收者
    public class Light {
        public static final String HIGH = "贼亮";
    
        public static final String MEDIUM = "有点亮";
    
        public static final String LOW = "快灭火了";
    
        public static final String OFF = "真的灭火了~";
    
        private String luminance;
    
        public Light() {
            this.luminance = OFF;
        }
    
        public void off() {
            System.out.println("灯关闭了");
            this.luminance = OFF;
        }
    
        public void high() {
            System.out.println("贼亮");
            this.luminance = HIGH;
        }
    
        public void medium() {
            System.out.println("挺亮地!");
            this.luminance = MEDIUM;
        }
    
        public void low() {
            System.out.println("快灭火了");
            this.luminance = LOW;
        }
        
        public String getLuminance(){
            return this.luminance;
        }
    }
    
    

    Command——做个命令接口,带撤销功能

    public interface Command {
        void excute();
        void undo();
    }
    

    LightHighCommand——高光命令

    public class LightHighCommand implements Command {
        private Light light;
        private String preLuminance;
        @Override
        public void excute() {
            // 备份上一个命令,撤销使用
            preLuminance = light.getLuminance();
            light.high();
        }
    
        @Override
        public void undo() {
            if (Light.HIGH.equals(preLuminance)){
                light.high();
            }else if(Light.MEDIUM.equals(preLuminance) ){
                light.medium();
            }else if (Light.LOW.equals(preLuminance)){
                light.low();
            }else if (Light.OFF.equals(preLuminance)){
                light.off();
            }
        }
    
        public LightHighCommand(Light light) {
            this.light = light;
        }
        // 智能遥控器按键功能可以控制多个灯
        // 如果控制令一个接收者,可以传入,不用新建命令
        public void setLight(Light light) {
            this.light = light;
        }
    }
    

    LightOffCommand——关灯命令

    public class LightOffCommand implements Command {
        private Light light;
        private String preLuminance;
    
        public LightOffCommand(Light light) {
            this.light = light;
        }
    
        @Override
        public void excute() {
            preLuminance = light.getLuminance();
            light.off();
        }
    
        @Override
            public void undo() {
                if (Light.HIGH.equals(preLuminance)){
                    light.high();
                }else if(Light.MEDIUM.equals(preLuminance) ){
                    light.medium();
                }else if (Light.LOW.equals(preLuminance)){
                    light.low();
                }else if (Light.OFF.equals(preLuminance)){
                    light.off();
                }
            }
        
        public void setLight(Light light) {
            this.light = light;
        }
    }
    

    Invoker——遥控器

    public class RemoteControl {
        private Command off;
        private Command high;
        private Command medium;
        private Command low;
        private Command preCommand;
    
        public void setOff(Command off) {
            this.off = off;
    
        }
    
        public void setHigh(Command high) {
            this.high = high;
        }
    
        public void setMedium(Command medium) {
            this.medium = medium;
        }
    
        public void setLow(Command low) {
            this.low = low;
    
        }
    
        public void lightOff() {
            off.excute();
            preCommand = off;
        }
    
        public void lightHigh() {
            high.excute();
            preCommand = high;
    
        }
    
        public void lightMedium() {
            medium.excute();
            preCommand = medium;
    
        }
    
        public void lightLow() {
            low.excute();
            preCommand = low;
        }
    
        public void undo() {
            if (preCommand == null) {
                System.out.println("无法撤销");
            } else {
                preCommand.undo();
            }
        }
    }
    

    Client——客户端

    public class Client {
        public static void main(String[] args) {
            // 创建接收者
            Light light = new Light();
            // 创建命令
            Command lightHighCommand = new LightHighCommand(light);
            Command lightOffCommand = new LightOffCommand(light);
            // 创建调用者
            RemoteControl remoteControl = new RemoteControl();
            remoteControl.setHigh(lightHighCommand);
            remoteControl.setOff(lightOffCommand);
            // 调用
            remoteControl.lightHigh();
            remoteControl.lightOff();
            remoteControl.undo();
        }
    }
    
    ————————>结果
    贼亮
    灯关闭了
    贼亮
    

    以上就是一个带撤回功能的命令模式,其中:

    如果想实现多步撤回,可以考虑把调用者中的preCommand换成stack;

    如果想实现组合命令,可以重新创建一个宏命令,如下

    public class MacroCommand implements Command {
        private Command[] commands;
    
        public MacroCommand(Command[] commands) {
            this.commands = commands;
        }
    
        @Override
        public void excute() {
            for (Command command : commands) {
                command.excute();
            }
        }
    
        @Override
        public void undo() {
            for (Command command : commands) {
                command.undo();
            }
        }
    }
    

    你可以用这个命令实现任何形式的命令组合,甚至如果你觉得你的遥控器可以控制空调,控制电源,控制电饭锅,你甚至可以把这些命令组合进来~

    四、命令模式的优缺点

    优点

    • 解耦合:将命令调用者和命令执行者通过命令进行解耦,命令调用者不关心由谁来执行命令,只要命令执行就可以

    • 更动态的控制:请求被封装成对象后,可以轻易的参数化、队列化、日志化,使系统更加灵活。

    • 更容易的命令组合:有了宏命令后,可以任意的对命令进行组合

    • 更好扩展性:可以轻易的添加新的命令,并不会影响到其他的命令

    缺点

    • 命令过多时,会创建了过多的命令类,不方便进行管理

    五、总结

    本文对命令模式作了简单的介绍,命令模式只要明白调用者如何通过命令与接收者交互,就比较好理解。在实际应用中,命令模式可以用在并行处理、事务行为、线程池等地方。例如传统的线程池就有addTask()方法将命令加入到等待被执行的队列中,允许多线程执行实现java.lang.Runnable 的命令,尽管线程池本身对具体的任务毫无认知。


    1. Head First 设计模式,Eric Freeman &Elisabeth Freeman with Kathy Sierra & Bert Bates
    2. Command pattern,wiki
    3. 《JAVA与模式》之命令模式
  • 相关阅读:
    小程序登录页面
    小程序环境搭建
    js闭包
    作用域和作用域链及预解析
    高阶函数
    在.net core项目中,增加gulp打包任务
    阿里云部署docker-swarm 内网问题
    .net identity scaffold
    c#中对XML反序列化
    c# Reactive Extension中的FromEventPattern和FromEvent
  • 原文地址:https://www.cnblogs.com/cdream-zs/p/10115643.html
Copyright © 2020-2023  润新知