设计模式之命令模式

2023-10-28

介绍

在软件开发系统中,“方法的请求者”与“方法的实现者”之间经常存在紧密的耦合关系,这不利于软件功能的扩展与维护。例如,想对方法进行“撤销、重做、记录”等处理都很不方便,因此“如何将方法的请求者与实现者解耦?”变得很重要,命令模式就能很好地解决这个问题。

命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。

案例

角色:
 1、命令(Command):为所有命令声明了一个接口。调用命令对象的 execute()方法,就可以让接收者进行相关的操作。这个接口也具备一个 undo() 方法。
 2、具体命令(ConcreteCommand):实现命令接口,定义了动作和接收者之间的绑定关系。调用者只要调用 execute() 就可以发出请求,然后由 ConcreteCommand 调用接收者的一个或多个动作。
 3、请求者(Invoker):持有一个命令对象,有一个行动方法,在某个时间点调用命令对象的 execute() 方法,将请求付诸实行。
 4、接收者(Receiver):接收者知道如何进行必要的动作,实现这个请求。任何类都可以当接收者。
 5、客户端(Client):创建一个具体命令(ConcreteCommand)对象并确定其接收者,包括把其他角色串连在一起。
在这里插入图片描述
接收者

public class Light {

    public void on() {
        System.out.println("灯亮了...");
    }

    public void off() {
        System.out.println("灯暗了...");
    }
}

命令接口

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 RemoteInvoker {

    /**
     * 开关命令数组,模拟有很多对开关数组
     */
    private Command[] onCommands;
    private Command[] offCommands;
    /**
     * 撤销(回退)命令
     */
    private Command undoCommand;

    public RemoteInvoker(int length) {
        // 有几组开关,就设置多少数组
        onCommands = new Command[length];
        offCommands = new Command[length];
        // 把每个命令初始化成空命令,避免空指针异常
        //这里是一个空实现的NoCommand类
        Command noCommand = new NoCommand();
        undoCommand = noCommand;
        for (int i = 0; i < length; i++) {
            onCommands[i] = noCommand;
            offCommands[i] = noCommand;
        }
    }

   
    public void setCommond(int slot, Command onCommand, Command offCommand) {
        onCommands[slot] = onCommand;
        offCommands[slot] = offCommand;
    }


    public void onButton(int slot) {
        onCommands[slot].execute();
        //为撤销(回退)按钮记录动作
        undoCommand = onCommands[slot];
    }

    public void offButton(int slot) {
        offCommands[slot].execute();
        //为撤销(回退)按钮记录动作
        undoCommand = offCommands[slot];
    }

    public void undoButton() {
        undoCommand.undo();
    }
}

客户端

public class RemoteClient {

    public static void main(String[] args) {
        // 1、创建接收者
        Light light = new Light();
        // 2、创建命令对象
        LightOnCommand lightOnCommand = new LightOnCommand(light);
        LightOffCommand lightOffCommand = new LightOffCommand(light);
        // 3、创建一组开关并用命令对象装载它
        RemoteInvoker invoker = new RemoteInvoker(1);
        invoker.setCommond(0, lightOnCommand, lightOffCommand);
        // 4、测试
        invoker.onButton(0);
        invoker.offButton(0);
        invoker.undoButton();
    }
}

优缺点及适用场景

优点:

  1. 通过引入中间件(抽象接口)降低系统的耦合度。
  2. 扩展性良好,增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,且满足“开闭原则”。
  3. 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
  4. 方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。
  5. 可以在现有命令的基础上,增加额外功能。比如日志记录,结合装饰器模式会更加灵活。

缺点:

  1. 可能产生大量具体的命令类。因为每一个具体操作都需要设计一个具体命令类,这会增加系统的复杂性。
  2. 命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构、解耦请求与实现,引入了额外类型结构(引入了请求方与抽象命令接口),增加了理解上的困难。不过这也是设计模式的通病,抽象必然会额外增加类的数量,代码抽离肯定比代码聚合更加难理解。

适用场景:

  1. 请求调用者需要与请求接收者解耦时,命令模式可以使调用者和接收者不直接交互。
  2. 系统随机请求命令或经常增加、删除命令时,命令模式可以方便地实现这些功能。
  3. 当系统需要执行一组操作时,命令模式可以定义宏命令来实现该功能。
  4. 当系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作时,可以将命令对象存储起来,采用备忘录模式来实现。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

设计模式之命令模式 的相关文章

  • 微信小程序——开篇

    开篇 前言 锻造兵器 开发者账号 微信开发者工具 写在最后 前言 如今微信小程序已经成为我们日常生活中不可或缺的 介质 如我们的出行 购物 餐饮 社交 娱乐等活动的小程序已经因有尽有 相比于去安装一个app人们自然更加倾向于在微信中去直接访

随机推荐

  • 【qt】信号,使用自定义的结构作为参数传递

    错误 解决办法 方法1 注册改向量 1 注册位置 在第一次使用此类链接跨线程的signal slot之前 一般在当前类的构造函数中进行注册 2 注册方法 在当前类的顶部包含 include
  • ES6(一):let和const、模板字符串、函数默认值、剩余参数、扩展运算符、箭头函数

    ES6 一 一 let和const声明变量 1 变量不能重复声明 2 块儿级作用域 3 不存在变量提升 4 const声明变量 5 优点 1 for循环中用let比较奶思 2 for循环分为父作用域和子作用域 3 不会污染全局变量 二 模板
  • 基于 uni-app 和 uni-cloud 小程序项目开发实战

    基于 uni app 和 uni cloud 小程序项目开发实战 前言 一 技术栈 二 环境搭建 三 项目功能介绍 1 地图地点搜索及路线规划 2 uniCloud服务空间 3 AI识图 4 上拉框组件 5 登陆权限 四 遇到问题 五 发行
  • IDEA去除Mapper.xml中黄色警报和绿色背景

    黄色警报去除 IDEA中用以编写SQL的Mapper xml常出现的警报通常来自于两种检查 一种是SQL方言检测 一种是未配置数据源的检测 需要在检查中将这两种检查关闭 解决方案 英文原版 settings Editor Inspectio
  • android权限的问题,android关于权限的问题

    希望能帮到大家 希望大家继续补充 MyLocation API调用获取的Cellid等定位信息必需添加android permission ACCESS COARSE LOCATION这个声明 ACCESS FINE LOCATION AC
  • 直播弹幕系统(五)- 整合Stomp替换原生WebSocket方案探究

    直播弹幕系统 五 整合Stomp替换原生WebSocket方案探究 前言 一 STOMP 协议简单介绍 1 1 客户端编码基础 1 2 服务端编码基础 1 2 1 SimpMessagingTemplate 1 2 2 SendTo 和 M
  • Codeforces Round 744 (Div. 3)

    A Casimir s String Solitaire 一个A需要一个B一个C需要一个B 所以只要A和C的个数之和等于B即可 AC代码 include
  • 保姆级手把手图文并茂教你配置MAC系统Flutter环境

    Flutter 是什么 Flutter是Google开源的构建用户界面 UI 工具包 帮助开发者通过一套代码库高效构建多平台精美应用 支持移动 Web 桌面和嵌入式平台 Flutter 开源 免费 拥有宽松的开源协议 适合商业项目 下载Fl
  • PyTorch 06—基础总结

    梯度下降算法 多层感知器 多层感知器的优化算法是梯度下降算法 梯度下降法是一种致力于找到函数极值点的算法 前面介绍过 所谓 学习 便是改进模型参数 以便通过大量训练步骤将损失最小化 有了这个概念 将梯度下降法应用于寻找损失函数的极值点便构成
  • 02-SpringBoot集成MinIo

    添加依赖
  • 字节对齐

    2018 1 16 对于自己定义的结构体 如果拿该结构体去匹配内存中的一个块内容 有可能不能准确的匹配正确 就比如今天去匹配SSL ClientHello结构体 但是总是往后退了一个字节 而且非常准 这其实是编译器的问题 gcc默认是4字节
  • 高亮地图点及属性信息展示优化方案

    作者 小阳 1 序言 本文主要是利用了UTFGrid图层快速交互获取地物信息的特点 以SuperMap iClient for JavaScript的开发作为示范 注 要实现本文所讲述的功能 同时需要SuperMap iServer和Sup
  • 2023中国数据安全防护与治理市场研究报告

    随着数字经济的快速发展 数据成为重要的生产要素 数据安全面临较为严峻的态势 数据安全市场迎来较大机遇 赛迪顾问重点发布了中国数据安全防护与治理产品及服务全景图 并对各类产品及服务做了成熟度评价 形成了中国数据安全防护与治理产品及服务成熟度曲
  • 视频网站需要流媒体服务器吗,视频监控必须用流媒体服务器吗

    视频监控必须用流媒体服务器吗 内容精选 换一换 task input参数说明参数是否必选类型说明type是String视频数据的输入类型 obs 表示从华为云OBS中读取视频数据 url 表示从指定的URL地址中读取视频数据 edgecam
  • Halcon HTuple由按行排序转成按列排序

    目录 前言 方法 结果 前言 今天有一个需求 把一个HTuple的列表重新排序 列表保存的是由二维展开成一维的数据 先按行保存 存完一行再存下一行 如今需要变一下 改成先保存一列 存完一列后在保存下一列 如有一个二维表格如下 1 2 3 4
  • MySQL主备、主从、读写分离详解

    一 MySQL主备的基本原理 在状态1中 客户端的读写都直接访问节点A 而节点B是A的备库 只是将A的更新都同步过来 到本地执行 这样可以保持节点B和A的数据是相同的 当需要切换的时候 就切成状态2 这时候客户端读写访问的都是节点B 而节点
  • 激光雷达-相机外参联合标定通过手动选取3D-2D点利用PnP求解

    0 写在前面 差不多快半年没有更新博客了 这里先感慨一下找工作的不容易 这个激光雷达 相机联合标定的需求是因为我前段时间所做的项目中有一个需求 需要给点云添加对应的温度 没错 给点云加温度 从红外相机中获取到温度 1 激光雷达 gt 相机的
  • Node.js 源码安装

    Node js 源码安装 以下部分我们将介绍在Ubuntu Linux下安装 Node js 其他的Linux系统下安装步骤 在 Github 上获取 Node js 源码 sudo git clone https github com n
  • layui tableSelect 数据表格下拉框(多选或单选)

    记录 使用tableSelect插件实现下拉框表格 并多选或单选数据 HTML代码 tr td td tr
  • 设计模式之命令模式

    介绍 在软件开发系统中 方法的请求者 与 方法的实现者 之间经常存在紧密的耦合关系 这不利于软件功能的扩展与维护 例如 想对方法进行 撤销 重做 记录 等处理都很不方便 因此 如何将方法的请求者与实现者解耦 变得很重要 命令模式就能很好地解