设计模式教程:命令模式(Command Pattern)

news2025/2/22 15:48:54
1. 什么是命令模式?

命令模式(Command Pattern)是一种行为型设计模式。它将请求封装成一个对象,从而使你能够用不同的请求、队列和日志请求以及支持可撤销操作。

简单来说,命令模式通过把请求封装成对象的方式解耦了请求的发送者与接收者,使得客户端可以以不同的方式来请求服务,而无需直接了解接收者的实现。

命令模式的关键组成部分

命令模式通常由以下几个角色组成:

  1. Command(命令接口)

    • 定义了执行操作的接口。它通常只有一个方法 execute(),用于执行请求。
  2. ConcreteCommand(具体命令)

    • 具体命令类实现了命令接口,具体实现请求的方法。它会保存对接收者对象的引用,并在 execute() 方法中调用接收者的相应操作。
  3. Invoker(请求者)

    • 请求者对象(Invoker)是命令的调用者,它通过调用 execute() 方法来发出请求。请求者只关心命令接口,而不关心命令如何被执行。
  4. Receiver(接收者)

    • 接收者是执行实际操作的对象,它包含了执行操作的具体方法。每个命令对象通过接收者来执行具体操作。
  5. Client(客户端)

    • 客户端负责创建具体的命令对象并将其与接收者绑定,然后将这些命令对象传递给请求者(Invoker)。

命令模式的结构图

+------------+      +-----------------+      +-------------+
|   Client   | ---> |   ConcreteCommand | ---> |  Receiver  |
+------------+      +-----------------+      +-------------+
                        |  execute()       |
                        v
                  +-------------+
                  |   Invoker   |
                  +-------------+
                        |
                  +-------------+
                  |  Command    |
                  +-------------+

命令模式的工作流程

  1. 客户端(Client) 创建一个 ConcreteCommand(具体命令)对象,并将 Receiver(接收者)对象传递给它。
  2. 客户端(Client)ConcreteCommand 对象传递给 Invoker(请求者)。
  3. 请求者(Invoker) 调用命令对象的 execute() 方法,从而触发接收者的实际操作。

通过这种方式,命令的发送者(请求者)和接收者(具体执行的对象)解耦,发送者只关心命令的接口,而无需了解命令如何被执行。

命令模式的应用场景

  1. 请求的发送者与接收者解耦:

    • 发送请求的一方(调用者)与执行请求的一方(接收者)解耦,调用者不需要了解接收者的实现细节,只需通过命令对象调用执行方法。
  2. 支持撤销操作:

    • 通过将命令封装成对象,你可以为每个操作创建相应的命令对象。你还可以通过维护命令对象的历史记录来实现撤销操作(Undo)。
  3. 支持日志操作:

    • 命令对象可以被存储,便于在后续进行执行、撤销或重做操作,因此可以用于日志系统,记录用户的操作。
  4. 宏命令(MacroCommand):

    • 如果某个操作是多个命令的组合,可以通过命令模式将多个命令组合成一个宏命令,按顺序执行。

命令模式的实现代码(Java 示例)

下面通过一个具体的代码示例来演示命令模式的实现。假设我们要实现一个简单的遥控器控制灯的开关操作。

1. 定义命令接口
// Command 接口
public interface Command {
    void execute();  // 执行命令
}

2. 具体命令类

// 开灯命令
public class LightOnCommand implements Command {
    private Light light;

    public LightOnCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.turnOn();
    }
}

// 关灯命令
public class LightOffCommand implements Command {
    private Light light;

    public LightOffCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.turnOff();
    }
}

3. 接收者(Receiver)

// 接收者:Light(灯)
public class Light {
    public void turnOn() {
        System.out.println("The light is ON");
    }

    public void turnOff() {
        System.out.println("The light is OFF");
    }
}

4. 请求者(Invoker)

// 请求者:遥控器
public class RemoteControl {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void pressButton() {
        command.execute();  // 执行命令
    }
}

5. 客户端(Client)

public class Client {
    public static void main(String[] args) {
        // 创建接收者(灯)
        Light light = new Light();
        
        // 创建具体命令(开灯和关灯)
        Command lightOn = new LightOnCommand(light);
        Command lightOff = new LightOffCommand(light);
        
        // 创建请求者(遥控器)
        RemoteControl remote = new RemoteControl();
        
        // 设置命令并按下按钮
        remote.setCommand(lightOn);
        remote.pressButton();  // 输出: The light is ON
        
        remote.setCommand(lightOff);
        remote.pressButton();  // 输出: The light is OFF
    }
}

命令模式的优势

  1. 解耦请求者和接收者:

    • 请求者只与命令接口打交道,而无需直接依赖于接收者的实现。接收者可以被更换或修改,而不需要更改请求者的代码。
  2. 扩展性:

    • 如果需要增加新功能,只需要新增一个命令类并实现 Command 接口,而不需要修改现有代码,符合开闭原则。
  3. 支持撤销(Undo)和重做(Redo):

    • 可以在每个命令中添加撤销方法,并通过命令队列来实现撤销和重做操作。
  4. 支持宏命令:

    • 可以将多个命令组合成一个宏命令,一次执行多个命令。

命令模式的缺点

  1. 类的数量增加:

    • 如果系统中有很多命令,每个命令都需要一个命令类,这会导致系统类的数量迅速增加。
  2. 代码可能会变得冗长:

    • 每个具体命令都需要创建一个单独的类,这可能导致代码膨胀,尤其是系统功能复杂时。

命令模式的实际应用

命令模式在实际项目中有许多应用,例如:

  1. GUI 应用程序: 按钮的点击事件通常可以用命令模式来处理,每个按钮可以绑定一个命令来处理不同的操作。
  2. 工作流引擎: 在工作流引擎中,用户的操作可以视为一系列的命令,执行顺序和撤销操作可以使用命令模式来处理。
  3. 远程控制系统: 像遥控器这样的系统可以将不同的操作(如开关灯、调节音量等)封装成命令。

总结

命令模式通过将请求封装成对象,从而使请求的发送者与接收者解耦。这种模式非常适合需要支持撤销操作、日志记录、队列请求等场景。尽管它引入了大量的命令类,但它的灵活性和可扩展性使得它在很多大型系统中得到了广泛应用。

版权声明
  1. 本文内容属于原创,欢迎转载,但请务必注明出处和作者,尊重原创版权。
  2. 转载时,请附带原文链接并注明“本文作者:扣丁梦想家
  3. 禁止未经授权的商业转载。

如果您有任何问题或建议,欢迎留言讨论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2303494.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vim修改只读文件

现象 解决方案 对于有root权限的用户,在命令行输入 :wq! 即可强制保存退出

【DeepSeek】本地部署,保姆级教程

deepseek网站链接传送门:DeepSeek 在这里主要介绍DeepSeek的两种部署方法,一种是调用API,一种是本地部署。 一、API调用 1.进入网址Cherry Studio - 全能的AI助手选择立即下载 2.安装时位置建议放在其他盘,不要放c盘 3.进入软件后…

NCRE证书构成:全国计算机等级考试证书体系详解

全国计算机等级考试(NCRE)证书体系为中学生提供了一个系统学习和提升计算机能力的平台。本文将详细介绍 NCRE 证书的构成,帮助中学生了解 NCRE 证书的级别和内容,规划未来职业发展。 一、NCRE 证书体系概述 NCRE 证书共分为四个级…

如何在WPS打开的word、excel文件中,使用AI?

1、百度搜索:Office AI官方下载 或者直接打开网址:https://www.office-ai.cn/static/introductions/officeai/smartdownload.html 打开后会直接提示开始下载中,下载完成后会让其选择下载存放位置: 选择位置,然后命名文…

【设计模式】 代理模式(静态代理、动态代理{JDK动态代理、JDK动态代理与CGLIB动态代理的区别})

代理模式 代理模式是一种结构型设计模式,它提供了一种替代访问的方法,即通过代理对象来间接访问目标对象。代理模式可以在不改变原始类代码的情况下,增加额外的功能,如权限控制、日志记录等。 静态代理 静态代理是指创建的或特…

《A++ 敏捷开发》- 16 评审与结对编程

客户:我们的客户以银行为主,他们很注重质量,所以一直很注重评审。他们对需求评审、代码走查等也很赞同,也能找到缺陷,对提升质量有作用。但他们最困惑的是通过设计评审很难发现缺陷。 我:你听说过敏捷的结对…

NutUI内网离线部署

文章目录 官网拉取源代码到本地仓库修改源代码打包构建nginx反向代理部署访问内网离线地址 在网上找了一圈没有写NutUI内网离线部署的文档,花了1天时间研究下,终于解决了。 对于有在内网离线使用的小伙伴就可以参考使用了 如果还是不会联系UP主:QQ:10927…

【实战篇】【深度介绍 DeepSeek R1 本地/私有化部署大模型常见问题及解决方案】

引言 大家好!今天我们来聊聊 DeepSeek R1 的本地/私有化部署大模型。如果你正在考虑或者已经开始了这个项目,那么这篇文章就是为你准备的。我们会详细探讨常见问题及其解决方案,帮助你更好地理解和解决在部署过程中可能遇到的挑战。准备好了…

Qt学习(六) 软件启动界面 ,注册表使用 ,QT绘图, 视图和窗口绘图,Graphics View绘图框架:简易CAD

一 软件启动界面 注册表使用 知识点1:这样创建的界面是不可以拖动的,需要手动创建函数来进行拖动,以下的3个函数是从父类继承过来的函数 virtual void mousePressEvent(QMouseEvent *event);virtual void mouseReleaseEvent(QMouseEvent *eve…

java练习(36)

ps:题目来自力扣 给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 . 和 * 的正则表达式匹配。 . 匹配任意单个字符* 匹配零个或多个前面的那一个元素 所谓匹配,是要涵盖 整个 字符串 s 的,而不是部分字符串。 class Solution {publ…

【Linux网络编程】IP协议格式,解包步骤

目录 解析步骤 1.版本字段(大小:4比特位) 2.首部长度(大小:4比特位)(单位:4字节) 🍜细节解释: 3.服务类型(大小:8比特…

Unity 位图字体

下载Bitmap Font Generator BMFont - AngelCode.com 解压后不用安装直接双击使用 提前设置 1、设置Bit depth为32 Options->Export options 2、清空所选字符 因为我们将在后边导入需要的字符。 Edit->Select all chars 先选择所有字符 Edit->Clear all chars i…

Linux 网络与常用操作(适合开发/运维/网络工程师)

目录 OSI 七层协议简介 应用层 传输层 Linux 命令!!! 1. ifconfig 命令 简介 1. 查看网络地址信息 2. 指定开启、或者关闭网卡 3. 修改、设置 IP 地址 4. 修改机器的 MAC 地址信息 5. 永久修改网络设备信息 2. route 路由命令 …

linux 安装启动zookeeper全过程及遇到的坑

1、下载安装zookeeper 参考文章:https://blog.csdn.net/weixin_48887095/article/details/132397448 2、启动失败 1、启动失败JAVA_HOME is not set and java could not be found in PATH 已安装 JAVA 配置了JAVA_HOME,还是报错解决方法:参考&#xf…

MySQL数据库——表的约束

1.空属性(null/not null) 两个值:null(默认的)和not null(不为空) 数据库默认字段基本都是字段为空,但是实际开发时,尽可能保证字段不为空,因为数据为空没办法…

ArcGIS Pro进行坡度与坡向分析

在地理信息系统中,坡度分析是一项至关重要的空间分析方法,旨在精确计算地表或地形的坡度,为地形特征识别、土地资源规划、环境保护、灾害预警等领域提供科学依据。本文将详细介绍如何利用ArcGIS Pro这一强大的地理信息系统软件,进…

【MySQL常见疑难杂症】MySQL数据库底层图例

● Connectors(连接者)​:指的是不同语言中与SQL的交互,从图3-1中可以看到目前流行的语言都支持MySQL客户端连接。 ● Connection Pool(连接池)​:管理缓冲用户连接、线程处理等需要缓存的需求。…

谈谈对线程的认识

面对这样的一个多核CPU时代, 实现并发编程是刚需. 多进程实现并发编程, 效果是特别理想的. 但是, 多线程编程模型有一个明显的缺点, 就是进程太重了, 效率不高. 创建一个进程, 消耗时间比较多. 销毁一个进程, 消耗时间也比较多. 调度一个进程, 消耗时间也比较多. 这里的时…

无人机遥控器接口作用详解!

USB接口: 功能:USB接口是一种通用串行总线接口,用于连接外部设备,如手机、平板、电脑或充电设备。在无人机遥控器上,USB接口通常用于数据传输和充电。 应用:用户可以通过USB接口将遥控器与电脑连接&#…

【数据分析】1 认识数据分析

一、课程核心内容结构 1. 课程定位 商业数据分析导论课:旨在为初学者奠定扎实的基础,介绍数据分析的基本概念、方法和应用场景。后续模块:包括职业发展路径、技能要求等深入内容,帮助学习者规划未来的职业道路。目标群体&#x…