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

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;}@Overridepublic void execute() {light.turnOn();}
}// 关灯命令
public class LightOffCommand implements Command {private Light light;public LightOffCommand(Light light) {this.light = light;}@Overridepublic 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 ONremote.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.rhkb.cn/news/23241.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TOGAF之架构标准规范-信息系统架构 | 应用架构

TOGAF是工业级的企业架构标准规范,信息系统架构阶段是由数据架构阶段以及应用架构阶段构成,本文主要描述信息系统架构阶段中的应用架构阶段。 如上所示,信息系统架构(Information Systems Architectures)在TOGAF标准规…

智能优化算法:莲花算法(Lotus flower algorithm,LFA)介绍,提供MATLAB代码

一、 莲花算法 1.1 算法原理 莲花算法(Lotus flower algorithm,LFA)是一种受自然启发的优化算法,其灵感来源于莲花的自清洁特性和授粉过程。莲花的自清洁特性,即所谓的“莲花效应”,是由其叶片表面的微纳…

CSS 媒体查询:从入门到精通,打造跨设备完美体验

在当今移动互联网时代,用户访问网站的设备早已不再局限于桌面电脑,手机、平板等各种屏幕尺寸的设备层出不穷。为了确保用户在不同设备上都能获得良好的浏览体验,响应式网页设计应运而生。而 CSS 媒体查询,正是实现响应式设计的核心…

【Python LeetCode 专题】树

LeetCode 题目104. 二叉树的最大深度(gif 图解)方法一:后序遍历(DFS)方法二:层序遍历(BFS)872. 叶子相似的树(DFS 遍历)1448. 统计二叉树中好节点的数目(DFS 遍历)437. 路径总和 III(前缀和 + DFS 回溯)1372. 二叉树中的最长交错路径(DFS)236. 二叉树的最近公共…

Spring有哪些缺点?

大家好,我是锋哥。今天分享关于【Spring有哪些缺点?】面试题。希望对大家有帮助; Spring有哪些缺点? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Spring框架是一个广泛使用的企业级Java开发框架,提供了丰富的功能和强大的灵…

MySQL数据库——索引潜规则(回表查询、索引覆盖、索引下推)

大家好,这里是编程Cookbook。本文详细介绍MySQL索引的三个潜规则——回表查询、索引覆盖、索引下推,以提升数据库的性能。 文章目录 索引回顾聚集索引(Clustered Index)非聚集索引(Secondary Index/辅助索引/二级索引&…

VScode运行后出现黑窗口

原文链接:VScode运行出黑窗口 1.安装插件:C/C Compile Run 2.快捷键【CtrlShiftp】,点击【首选项:打开用户设置】

使用大语言模型(Deepseek)构建一个基于 SQL 数据的问答系统

GitHub代码仓库 架构 从高层次来看,这些系统的步骤如下: 将问题转换为SQL查询:模型将用户输入转换为SQL查询。 执行SQL查询:执行查询。 回答问题:模型根据查询结果响应用户输入。 样本数据 下载样本数据&#xf…

【前端小点】vue3项目内根据主题读取不同文件夹下的图片资源(图片文件)

项目要求实现一键换肤的功能,不仅仅是主题颜色上的替换,还有图片素材的替换,主题颜色替换的方案大同小异,下面仅对图片素材的一件替换进行方法描述。 主要思路 使用本地仓库对当前主题进行存储,系统根据主题去加载不同…

vxe-table实现动态列

vxe-table实现动态列 1.动态列解释2.解决步骤2.1将后端返回的动态列表头,按照格式拼接在固定列表头上2.2将后端返回的列表数据按照键值对格式组装 1.动态列解释 正常列表是有固定的列;我的需求是,最初只知道表格的固定两列,查询数…

Windows 11 使用容器(Docker Podman)

文章目录 背景1、相关网站1.1、WSL1.2、Docker1.3、Podman 2、环境3、安装部署3.1、安装 WSL3.2、Docker3.2.1、Docker Desktop3.2.1.1、安装3.2.1.2、拉取镜像3.2.1.3、启动容器 3.3、Podman3.3.1、安装3.3.2、使用3.3.3、异常处理 总结 背景 Windows 系统中使用容器&#xf…

UE_C++ —— Gameplay Modules

目录 一,Module Creation INI File Setup 二,Multiple Gameplay Modules 三,Limitations 编译成 DLL 的游戏相关类的集合;正如引擎本身由一组模块构成一样,每个游戏也是由一个或多个游戏模块构成的;这些…

蓝桥杯定时器实现led闪烁

step1.配置定时器,TIM1时高级定时,TIM2是通用定时器,用TIM2就行,用内部时钟源,记住相关公式,定时器中断配置时要使能,且生成代码后也要在mian中写使能函数 step2.写代码 配置生成代码后多出的…

二:前端发送POST请求,后端获取数据

接着一:可以通过端口访问公网IP之后 二需要实现:点击飞书多维表格中的按钮,向服务器发送HTTP请求,并执行脚本程序 向服务器发送HTTP请求: 发送请求需要明确一下几个点 请求方法: 由于是向服务器端发送值…

内外网文件传输 安全、可控、便捷的跨网数据传输方案

一、背景与痛点 在内外网隔离的企业网络环境中,员工与外部协作伙伴(如钉钉用户)的文件传输面临以下挑战: 安全性风险:内外网直连可能导致病毒传播、数据泄露。 操作繁琐:传统方式需频繁切换网络环境&…

elasticsearch在windows上的配置

写在最前面: 上资源 第一步 解压: 第二步 配置两个环境变量 第三步 如果是其他资源需要将标蓝的文件中的内容加一句 xpack.security.enabled: false 不同版本的yaml文件可能配置不同,末尾加这个 xpack.security.enabled: true打开bin目…

OpenCV二值化处理

1.1. 为什么需要二值化操作 二值化操作将灰度图像转换为黑白图像,即将图像中的像素值分为两类:前景(通常为白色,值为 255)和背景(通常为黑色,值为 0)。二值化的主要目的是简化图像&…

[Android]浏览器下载的apk文件无法识别无法安装问题

在Android电话机上,用浏览器下载apk进行版本更新,出现下载文件没被识别为apk,导致无法安装问题。 原来的下载链接: https://mojsetup.obs.cn-southwest-2.myhuaweicloud.com/callphone-release-1.0.4.apk 修改后的下载链接&…

如何使用SSH连接设备?很简单!

前言 小白发现最近写的文章都与SSH息息相关,于是就有了这一篇文章,免得在后续的文章又不断重复如何SSH连接设备。 有需要的小伙伴自然就会看到这里,也不会影响到其他小伙伴的阅读体验。 至于文章里的广告嘛……就当是小伙伴们给小白的一点…

【自学嵌入式(9)ESP8266网络服务器的使用】

ESP8266网络服务器的使用 ESP8266WiFi 库① WiFiClass② WiFiClient③ WiFiServer④ WiFiUDP ESP8266WiFiMulti 库① WiFiMulti ESP8266WebServer 库① P8266WebServer 网络服务器实例在浏览器中控制ESP8266指示灯将开发板引脚状态显示在网页中 在之前的文章中,曾经…