当前位置: 首页 > article >正文

设计模式 命令模式(Command Pattern)

命令模式简绍

命令模式(Command Pattern)是一种行为设计模式,它把请求封装成对象,从而让你可以用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。

组成部分:

  • 命令接口(Command Interface):定义执行操作的接口。
  • 具体命令类(Concrete Command Class):实现了命令接口,并拥有接收者对象的引用。
  • 接收者(Receiver):知道如何实施与执行一个请求相关的操作,任何类都可以充当接收者。
  • 调用者(Invoker):请求一个命令对象执行一个请求。

命令模式的优缺点

优点
  • 降低耦合度:
    • 请求发送者不需要知道请求的实际接收者是谁,也不需要知道请求的执行细节。
    • 接收者和请求发送者之间解耦,使得两者可以独立变化。
  • 易于实现撤销和重做功能:
    • 由于每个命令都是一个对象,因此可以保存以前的状态,并在需要时恢复。
    • 支持事务回滚,因为可以存储多个命令,并按顺序或逆序执行。
  • 易于支持事务处理:
    • 可以将多个命令组合成一个宏命令(复合命令),从而实现整体的事务处理(要么全部成功,要么全部失败)。
  • 易于支持队列和日志:
    • 命令对象可以被放入队列中,按照顺序执行。
    • 命令对象也可以被记录下来,用于审计或日志记录。
  • 增加新的命令非常容易:
    • 添加新命令只需要实现命令接口,而不需要修改已有的代码。
    • 这符合开闭原则(Open-Closed Principle),即对扩展开放,对修改关闭。
  • 增强安全性:
    • 可以通过命令对象来验证权限,从而在执行之前检查是否有权限执行该命令。
缺点
  • 可能导致系统复杂度增加:
    • 如果系统中存在大量的命令类,可能会导致类的数量增加,进而使系统变得复杂。
    • 每个命令都需要一个具体的类来实现,这可能会导致类爆炸。
  • 内存消耗增加:
    • 如果命令对象被频繁创建,特别是在需要撤销或重做功能时,可能会导致内存消耗增加。
    • 特别是在没有及时清理不再需要的命令对象时,内存占用会更高。
  • 性能问题:
    • 命令模式增加了额外的抽象层,可能会导致一些性能上的损耗,尤其是在需要频繁执行命令的情况下。
    • 可能不适合简单场景:

对于非常简单的应用场景,使用命令模式可能会显得过于复杂,没有必要引入这种模式。

UML 图

在这里插入图片描述

实现代码

命令接口 (CommandInterface)

这个接口声明了所有命令对象都应该遵循的方法签名。

public interface CommandInterface {
    void execute();

}
具体命令类 (ConcreteCommandClass)

这个类负责调用接收者的 openMobile() 方法,并添加了一些额外的操作(如“掏出口袋”)。

public class ConcreteCommandClass implements CommandInterface{
    private Receiver receiver;

    public ConcreteCommandClass(Receiver receiver){
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        System.out.println("掏出口袋");
        receiver.openMobile();
    }
}
具体命令类 (ConcreteCommandClass1)

这个类负责调用接收者的 closeMoblie() 方法,并添加了一些额外的操作(如“放回口袋”)。

public class ConcreteCommandClass1 implements CommandInterface{

    private Receiver receiver;

    public ConcreteCommandClass1(Receiver receiver){
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        receiver.closeMoblie();
        System.out.println("放回口袋");
    }
}
接收者

这个类包含了实际要执行的操作。

public class Receiver {

    public void openMobile(){
        System.out.println("打开手机");
    }

    public void closeMoblie(){
        System.out.println("关闭手机");
    }

}
主调用 (Main)

在这里创建了具体的命令对象,并调用了它们的 execute() 方法。

public class Main {
    public static void main(String[] args) {
        ConcreteCommandClass concreteCommandClass = new ConcreteCommandClass(new Receiver());
        concreteCommandClass.execute();
        ConcreteCommandClass1 concreteCommandClass1 = new ConcreteCommandClass1(new Receiver());
        concreteCommandClass1.execute();
    }
}

这个示例展示了命令模式的基本应用,通过将请求封装成对象,使得发送请求的对象和执行请求的对象之间松耦合。

命令模式应用场景

  • 用户界面设计中的按钮和菜单项
    在图形用户界面(GUI)应用程序中,命令模式可以用来处理用户交互事件。例如,当用户点击一个按钮或选择一个菜单项时,可以创建一个命令对象来表示这个请求,然后在适当的时候执行该命令。

  • 宏命令和批处理
    命令模式非常适合于构建宏命令或批处理命令。可以将多个简单的命令组合成一个复合命令,这样就可以一次执行一系列操作。例如,在文本编辑器中,可以定义一个宏来执行一系列编辑命令。

  • 支持撤销功能(Undo/Redo)
    命令模式可以很容易地支持撤销和重做功能。每个命令对象都可以记录其执行前后的状态,这样在需要撤销操作时可以恢复到之前的状态。这对于许多应用程序来说都是非常有用的特性,比如文本编辑器、绘图工具等。

  • 解耦发送者与接收者
    命令模式使得发送者和接收者之间的耦合度降低。发送者只需要知道如何发送命令,而不需要关心命令的具体实现细节。这使得系统更加灵活,可以在运行时动态改变命令的执行方式。

  • 支持事务处理
    在某些情况下,命令模式可以用来模拟事务处理。一组命令可以作为一个整体来执行,如果其中一个命令失败,则整个事务回滚。这对于一些需要保证数据一致性的场景非常有用。

  • 异步命令执行
    命令模式可以用来支持异步命令执行。命令对象可以在创建时立即被处理,也可以存储起来稍后由调度器统一执行。这对于需要异步处理的任务队列非常有用。

  • 多线程环境下的任务调度
    在多线程环境中,命令模式可以用来封装任务,并将其放入线程池中执行。这样可以方便地管理任务的执行顺序和优先级。

实际案例
  • 编辑器中的撤销功能:用户在编辑器中进行了一系列操作,每个操作都被封装成一个命令对象。这些命令对象可以存储在一个栈中,用户可以选择撤销上一步操作,实际上是从栈中取出最后一个命令对象并执行它的撤销方法。
  • 智能家居控制系统:用户可以通过语音助手控制家中的各种设备(如灯光、空调等)。语音助手接收到指令后,可以创建一个命令对象来表示这个请求,并通过网络发送给相应的设备进行处理。

http://www.kler.cn/news/325813.html

相关文章:

  • MySQL 索引最左匹配原则详解
  • Element-plus安装及其基础组件使用
  • Python轴承故障诊断 (四)基于EMD-CNN的故障分类
  • 用Python集成免费IP归属地查询API
  • 语言的重定向
  • python select interpreter vscode 配置
  • 深度学习模型可视化工具 Netron 使用教程
  • 主流的消息队列
  • ACM第三次考核题解
  • Docker命令---查看所有创建的容器
  • [Linux]僵尸进程,孤儿进程,环境变量
  • 代码随想录算法训练营Day16
  • Threejs绘制圆锥体
  • 1.8 软件业务测试
  • 试填+组合数学,CF 1648C - Tyler and Strings
  • 【Linux】Linux内核结构基础
  • 缓存池和数据库连接池的使用(Java)
  • Vue 中自定义指令的探索与实践
  • ZYNQ:点亮LED灯
  • 每天一个数据分析题(四百七十六)- 线性回归建模
  • Go语言匿名字段使用与注意事项
  • Markdown 字体颜色
  • postgreSql常用操作
  • Spring Boot 多线程事务管理:使用 CyclicBarrier 和 PlatformTransactionManager 实现全局事务控制
  • Docker-3.启动
  • Vue3+FastAPI中Token的刷新机制(含代码示例)
  • 鸿蒙HarmonyOS之选择相册文件(照片/视频)方法
  • MySQL之分库分表后带来的“副作用”你是怎么解决的?
  • Servlet 3.0新特征
  • 新能源汽车储充机器人:能源高效与智能调度