命令模式(Command Pattern)详解

文章目录

    • 1. 什么是命令模式?
    • 2. 为什么需要命令模式?
    • 3. 命令模式的核心概念
    • 4. 命令模式的结构
    • 5. 命令模式的基本实现
      • 5.1 简单的灯光控制示例
      • 5.2 家电控制示例
    • 6. 带有撤销功能的命令模式
      • 6.1 修改命令接口
      • 6.2 实现可撤销的灯光命令
      • 6.3 实现可撤销的风扇命令
      • 6.4 修改调用者,支持撤销功能
      • 6.5 客户端代码演示
    • 7. 宏命令实现
      • 7.1 实现宏命令
      • 7.2 客户端代码演示
    • 8. 命令队列实现
      • 8.1 命令队列类
      • 8.2 客户端代码演示
    • 9. 命令日志与恢复
      • 9.1 可序列化的命令接口
      • 9.2 命令日志管理器
    • 10. 命令模式在Java中的实际应用
      • 10.1 Swing中的Action
      • 10.2 Runnable接口
      • 10.3 任务调度系统
      • 10.4 Java 8 Lambda表达式
    • 11. 命令模式与其他设计模式的结合
      • 11.1 命令模式与组合模式
      • 11.2 命令模式与备忘录模式
      • 11.3 命令模式与策略模式
    • 12. 命令模式的优缺点
      • 12.1 优点
      • 12.2 缺点
    • 13. 命令模式的适用场景
    • 14. 命令模式的常见问题与解决方案
      • 14.1 如何处理大量的命令类?
      • 14.2 如何处理命令执行失败的情况?
      • 14.3 如何实现更高效的撤销/重做功能?
    • 15. 总结
      • 15.1 核心要点
      • 15.2 设计建议

1. 什么是命令模式?

命令模式是一种行为型设计模式,它将请求(命令)封装为一个对象,从而使你可以使用不同的请求参数化客户端,队列或记录请求日志,以及支持可撤销的操作。

简单来说,命令模式就是将"请求"转化为一个对象,这个对象可以被存储、传递、调用,而且可以在不同的时间点被请求调用,即使发送请求的对象已经不存在。

2. 为什么需要命令模式?

在以下情况下,命令模式特别有用:

  1. 需要参数化操作:当你需要根据运行时确定的请求参数来执行操作时
  2. 需要将操作放入队列:当操作需要排队执行,或者在不同时间执行时
  3. 需要支持撤销/重做功能:当系统需要支持操作的撤销和重做功能时
  4. 需要支持事务:当操作需要作为一个事务执行,要么全部完成,要么全部不做
  5. 需要将发送者与接收者解耦:当请求发送者不需要知道请求如何被处理以及由谁处理时

3. 命令模式的核心概念

命令模式涉及以下几个核心角色:

  1. 命令(Command)

    • 声明执行操作的接口
    • 通常只有一个执行方法(如execute()
  2. 具体命令(Concrete Command)

    • 实现命令接口
    • 通常持有接收者的引用
    • 调用接收者的相关操作来完成命令的执行
  3. 接收者(Receiver)

    • 知道如何实施与命令相关的操作
    • 任何类都可以作为接收者
  4. 调用者(Invoker)

    • 要求命令对象执行请求
    • 不知道命令是如何执行的,也不知道具体的接收者是谁
  5. 客户端(Client)

    • 创建具体的命令对象并设置它的接收者
    • 将命令对象交给调用者

4. 命令模式的结构

命令模式的UML类图如下:

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

5. 命令模式的基本实现

5.1 简单的灯光控制示例

下面是一个简单的灯光控制示例,展示了命令模式的基本实现。

首先,定义命令接口:

// 命令接口
public interface Command {void execute();
}

然后,定义接收者类(灯):

// 接收者类
public class Light {private String location;public Light(String location) {this.location = location;}public void turnOn() {System.out.println(location + " 灯已打开");}public void turnOff() {System.out.println(location + " 灯已关闭");}
}

接着,定义具体命令类:

// 打开灯的命令
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();}
}

然后,定义调用者类(远程控制器):

// 调用者类
public class RemoteControl {private Command command;public void setCommand(Command command) {this.command = command;}public void pressButton() {command.execute();}
}

最后,客户端代码:

public class CommandPatternDemo {public static void main(String[] args) {// 创建接收者Light livingRoomLight = new Light("客厅");Light kitchenLight = new Light("厨房");// 创建具体命令Command livingRoomLightOn = new LightOnCommand(livingRoomLight);Command livingRoomLightOff = new LightOffCommand(livingRoomLight);Command kitchenLightOn = new LightOnCommand(kitchenLight);Command kitchenLightOff = new LightOffCommand(kitchenLight);// 创建调用者RemoteControl remote = new RemoteControl();// 使用远程控制器打开客厅灯remote.setCommand(livingRoomLightOn);remote.pressButton();// 使用远程控制器关闭客厅灯remote.setCommand(livingRoomLightOff);remote.pressButton();// 使用远程控制器打开厨房灯remote.setCommand(kitchenLightOn);remote.pressButton();// 使用远程控制器关闭厨房灯remote.setCommand(kitchenLightOff);remote.pressButton();}
}

输出结果:

客厅 灯已打开
客厅 灯已关闭
厨房 灯已打开
厨房 灯已关闭

5.2 家电控制示例

我们可以扩展上面的例子,添加更多种类的家电控制:

// 音响系统接收者
public class StereoSystem {private String location;public StereoSystem(String location) {this.location = location;}public void on() {System.out.println(location + " 音响已打开");}public void off() {System.out.println(location + " 音响已关闭");}public void setCD() {System.out.println(location + " 音响已设置为CD播放模式");}public void setVolume(int volume) {System.out.println(location + " 音响音量已设置为 " + volume);}
}// 电风扇接收者
public class Fan {private String location;public Fan(String location) {this.location = location;}public void on() {System.out.println(location + " 电风扇已打开");}public void off() {System.out.println(location + " 电风扇已关闭");}public void setHigh() {System.out.println(location + " 电风扇已设置为高速");}public void setMedium() {System.out.println(location + " 电风扇已设置为中速");}public void setLow() {System.out.println(location + " 电风扇已设置为低速");}
}

然后,为这些家电创建对应的命令:

// 打开音响的命令
public class StereoOnWithCDCommand implements Command {private StereoSystem stereo;public StereoOnWithCDCommand(StereoSystem stereo) {this.stereo = stereo;}@Overridepublic void execute() {stereo.on();stereo.setCD();stereo.setVolume(11);}
}// 关闭音响的命令
public class StereoOffCommand implements Command {private StereoSystem stereo;public StereoOffCommand(StereoSystem stereo) {this.stereo = stereo;}@Overridepublic void execute() {stereo.off();}
}// 打开风扇并设置高速的命令
public class FanHighCommand implements Command {private Fan fan;public FanHighCommand(Fan fan) {this.fan = fan;}@Overridepublic void execute() {fan.on();fan.setHigh();}
}// 关闭风扇的命令
public class FanOffCommand implements Command {private Fan fan;public FanOffCommand(Fan fan) {this.fan = fan;}@Overridepublic void execute() {fan.off();}
}

修改调用者类,支持多个按钮:

// 多按钮遥控器
public class MultiButtonRemote {private Command[] onCommands;private Command[] offCommands;public MultiButtonRemote(int slotCount) {onCommands = new Command[slotCount];offCommands = new Command[slotCount];// 初始化所有命令为空命令对象,避免空指针异常Command noCommand = new NoCommand(); // NoCommand是一个空实现for (int i = 0; i < slotCount; i++) {onCommands[i] = noCommand;offCommands[i] = noCommand;}}public void setCommand(int slot, Command onCommand, Command offCommand) {onCommands[slot] = onCommand;offCommands[slot] = offCommand;}public void pressOnButton(int slot) {onCommands[slot].execute();}public void pressOffButton(int slot) {offCommands[slot].execute();}
}// 空命令 - 用于初始化,避免空指针异常
public class NoCommand implements Command {@Overridepublic void execute() {// 什么也不做}
}

客户端代码:

public class HomeAutomationDemo {public static void main(String[] args) {// 创建接收者Light livingRoomLight = new Light("客厅");Light kitchenLight = new Light("厨房");StereoSystem stereo = new StereoSystem("客厅");Fan ceilingFan = new Fan("卧室");// 创建具体命令Command livingRoomLightOn = new LightOnCommand(livingRoomLight);Command livingRoomLightOff = new LightOffCommand(livingRoomLight);Command kitchenLightOn = new LightOnCommand(kitchenLight);Command kitchenLightOff = new LightOffCommand(kitchenLight);Command stereoOnWithCD = new StereoOnWithCDCommand(stereo);Command stereoOff = new StereoOffCommand(stereo);Command fanHigh = new FanHighCommand(ceilingFan);Command fanOff = new FanOffCommand(ceilingFan);// 创建多按钮遥控器MultiButtonRemote remote = new MultiButtonRemote(4);// 设置每个插槽对应的命令remote.setCommand(0, livingRoomLightOn, livingRoomLightOff);remote.setCommand(1, kitchenLightOn, kitchenLightOff);remote.setCommand(2, stereoOnWithCD, stereoOff);remote.setCommand(3, fanHigh, fanOff);// 测试按钮System.out.println("------ 按下第1个按钮的开按钮 --

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

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

相关文章

《Vue3学习手记8》

vue3中的一些API shallowRef ( ) 和shallowReactive ( ) shallowRef (浅层响应式) 1.作用:创建一个响应式数据&#xff0c;但只对顶层属性进行响应式处理。 2.用法: const originalref(...) const original2shallowRef(original) 3.特点:只跟踪引用值的变化&#xff0c;不关心…

双列集合——map集合和三种遍历方式

双列集合的特点 键和值一一对应&#xff0c;每个键只能对应自己的值 一个键和值整体称为键值对或键值对对象&#xff0c;java中叫做entry对象。 map常见的api map接口中定义了双列集合所有的共性方法&#xff0c;下面三个实现类就没有什么额外新的方法要学习了。 map接口…

Linux安装部署Postgresql数据库

联网安装方案 Linux能在线安装依赖组件的前提下&#xff0c;可以快速安装部署PG数据库&#xff0c;安装过程使用root管理员帐号&#xff1a; 首先&#xff0c;使用如下命令自动下载Postgresql组件&#xff1a; # 在openEuler、Fedora或CentOS 8上&#xff0c;你可能会使用&a…

供应链算法整理(二)--- 智能补货

供应链业务的目标价值是&#xff1a;优化货品的供给、销售提供支撑&#xff0c;以降低成本&#xff0c;提高时效、收益&#xff0c;最终提升用户体验。基于目标价值&#xff0c;整体的算法模块分为&#xff1a;智能选品、智能预测、品仓铺货、智能补货、智能调拨、仓网路由、快…

vscode 个性化

vscode 个性化 设置 吸顶效果 使用前使用后 设置方法 VS Code 的粘性滚动预览 - 类似于 Excel 的冻结首行 插件 代码片段分享 - CodeSnap 使用方式 CtrlShiftP输入CodeSnap 唤起插件选择代码 行内报错提示 - Error Lens 使用前使用后 VSCode Error Lens插件介绍&…

Rockermq的部署与使用(0-1)

​RocketMQ​ 是阿里巴巴开源的一款 ​分布式消息中间件&#xff0c;具有高吞吐、低延迟、高可用等特点&#xff0c;广泛应用于多个领域&#xff0c;包括异步通信解耦、企业解决方案、金融支付、电信、电子商务、快递物流、广告营销、社交、即时通信、移动应用、手游、视频、物…

软件测试报告机构如何保障软件质量并维护其安全性?

软件测试报告机构在软件开发流程里起着十分关键的作用&#xff0c;它可以保障软件的质量&#xff0c;它还能够维护软件的安全性。下面&#xff0c;我们就来深入了解一下这类机构。 机构作用 软件测试报告机构是软件质量的“把关者”&#xff0c;能对软件进行全面评估&#xf…

4个纯CSS自定义的简单而优雅的滚动条样式

今天发现 uni-app 项目的滚动条不显示&#xff0c;查了下原来是设置了 ::-webkit-scrollbar {display: none; } 那么怎么用 css 设置滚动条样式呢&#xff1f; 定义滚动条整体样式‌ ::-webkit-scrollbar 定义滚动条滑块样式 ::-webkit-scrollbar-thumb 定义滚动条轨道样式‌…

ES6入门---第二单元 模块五:模块化

js不支持模块化 注意&#xff1a; 需要放到服务器环境 1、如何定义模块&#xff1f; export 东西 例&#xff1a;1.js文件中 console.log(1模块加载了);//显示是否加载了 export const a 12; export const b 5; export let c 101; const a12; const b5; const c101;ex…

14.Excel:排序和筛选

一 位置 两个位置。 二 排序&#xff1a;如何使用 1.常规使用 补充&#xff1a;不弹出排序提醒排序。 选中要排序列中的任意一个单元格&#xff0c;然后排序。 2.根据要求进行排序 1.根据姓名笔画进行降序排序 要勾选上数据包含标题&#xff0c;默认是勾选了。 2.根据运营部、…

嵌入式系统基础知识

目录 一、冯诺依曼结构与哈佛结构 &#xff08;一&#xff09;冯诺依曼结构 &#xff08;二&#xff09;哈佛架构 二、ARM存储模式 &#xff08;一&#xff09;大端模式 &#xff08;二&#xff09;小端模式 &#xff08;三&#xff09;混合模式 三、CISC 与 RISC &am…

CSS 预处理器 Sass

目录 Sass 一、Sass 是什么&#xff1f; 二、核心功能详解 1. 变量&#xff08;Variables&#xff09; 2. 嵌套&#xff08;Nesting&#xff09; 3. 混合宏&#xff08;Mixins&#xff09; 4. 继承&#xff08;Inheritance&#xff09; 5. 运算&#xff08;Operations&…

信息收集新利器:SSearch Chrome 插件来了

SSearch 下载地址 SSearch &#x1f623;用途 每次谷歌语法搜索时还得自己写&#xff0c;我想省事一点&#xff0c;弄了一个插件&#xff0c;先加了几个常用的语法&#xff0c;点击后会跳转到对应搜索页面&#xff0c;也可以直接在搜索框微调 后续也会加些其他语法 &#…

Docker搭建SFTP

在这个教程中&#xff0c;我们将通过一个简单的例子来展示如何使用 Docker 和 atmoz/sftp 镜像设置一个基本的 SFTP 服务。这个服务将允许用户通过 SFTP 安全地访问和管理文件。我们将配置一个名为 ops 的用户&#xff0c;其密码为 123456&#xff0c;并限定用户只能访问特定的…

正态分布习题集 · 答案与解析篇

正态分布习题集 答案与解析篇 与题目篇编号一致,如有其他解题思路,欢迎在评论区交流。 1. 基础定义与性质 1.1 密度函数 X ∼ N ( μ , σ 2 ) X \sim N(\mu,\sigma^2) X∼N(μ,σ2) 的 PDF: [ f(x) = \frac{1}{\sigma\sqrt{2\pi}} \exp\left(-\frac{(x-\mu)2}{2\sigma2}\…

Java学习手册:SQL 优化技巧

一、SQL 查询优化 选择合适的索引列 &#xff1a;索引可以显著提高查询速度&#xff0c;但需要选择合适的列来创建索引。通常&#xff0c;对于频繁作为查询条件的列、连接操作的列以及排序或分组操作的列&#xff0c;应该考虑创建索引。例如&#xff0c;在一个订单表中&#xf…

(02)Redis 的订阅发布Pub/Sub

我们为了自己实现一个MQ功能&#xff0c;就要深入底层挖掘现有开源产品的实现过程。 Redis 发布订阅底层结构解析 Redis 不存储消息&#xff0c;仅作为“实时中转”&#xff1b;只有订阅者在线时才能收到消息&#xff1b;消息是广播给所有订阅此频道的客户端。 1. 核心数据结…

使用Docker一键安装SigLens:简单快捷的日志分析解决方案

在当今复杂的IT环境中,高效的日志管理和分析变得越来越重要。SigLens作为一款强大的开源日志分析工具,为开发者和运维人员提供了直观、高效的日志处理体验。本文将介绍如何使用Docker快速安装SigLens,让您在几分钟内就能开始进行日志分析。 为什么选择Docker安装SigLens? Do…

C#与西门子PLC通信:S7NetPlus和HslCommunication使用指南

西门子S7协议是用来和PLC进行通讯的一个协议&#xff0c;默认端口是102&#xff0c;数据会保存在一个个DB块中&#xff0c;比较经典的用法是一个DB块专门用来读取&#xff0c;一个用来写入。 DB&#xff08;数据块&#xff09; {块号}.DBX/DBD/DBW{字节地址}.{位偏移} 1、数据…

【中间件】brpc_基础_remote_task_queue

文章目录 remote task queue1 简介2 核心功能2.1 任务提交与分发2.2 无锁或低锁设计2.3 与 bthread 深度集成2.4 流量控制与背压 3 关键实现机制3.1 数据结构3.2 任务提交接口3.3 任务窃取&#xff08;Work Stealing&#xff09;3.4 同步与唤醒 4 性能优化5 典型应用场景6 代码…