设计模式之命令模式

阅读建议
嗨,伙计!刷到这篇文章咱们就是有缘人,在阅读这篇文章前我有一些建议:
- 本篇文章大概4000多字,阅读时间长可能需要4-5分钟,请结合示例耐心读完,绝对有收获。
- 设计模式属于程序的设计思想、方法类的内容,阅读一遍,在理解上不一定会很透彻,建议收藏起来,有空多看看,书读百遍,其义自现。
- 创作不易,免费的点赞、关注,请走上一走,也算是对博主一些鼓励,可以让我更有动力输出更多的干货内容。
- UML类图是一个好东西,要多看,如果会画,就更好了,一图胜千言。
什么是命令模式
命令模式(Command Pattern)是一种行为型设计模式,它将请求和处理分开,使得请求发送者和接收者解耦,从而降低系统的耦合度。在命令模式中,请求被封装为一个独立的对象,并且将其参数化,以便在不同的请求中传递不同的参数。
命令模式有哪些核心角色
在命令模式中,请求被封装为一个对象,从而可以将请求的调用者与实现者分离开来。这降低了系统的耦合度,使得不同的调用者可以发送不同的请求给同一个接收者,而不需要修改接收者的代码。同时,命令模式还提供了对事务进行建模的方法,可以实现对事务的撤销、重做等操作。其核心角色主要有以下几个核心组成部分:

- 命令接口(Command):定义了执行操作的接口,通常包含一个执行方法(execute)。
- 具体命令(ConcreteCommand):实现了命令接口,持有对一个接收者对象的引用,并将请求转发给接收者执行具体的操作。
- 接收者(Receiver):负责具体执行命令所指定的操作。
- 调用者(Invoker):持有一个命令对象,负责调用命令对象执行请求。
- 客户端(Client):创建具体的命令对象,并设置命令的接收者。
通过命令模式,客户端与调用者之间的耦合可以被解耦,客户端只需创建具体的命令对象并将其传递给调用者,而不需要了解具体的接收者和操作细节。这样可以实现请求的发送者和接收者之间的解耦,并且支持对请求进行排队、记录日志、撤销和重做等操作。
命令模式如何实现
需求描述
理解了命令模式的定义、核心角色以及各核心角色的功能作用,如何实现就变得很简单,下面以生活中一个事情为例来说明一下如何实现命令模式。在生活中,下班回家后,坐在沙发上用遥控器打开电视是一个很常见的事情。如果写一段程序来实现用遥控器打开电视这个过程,使用命令模式就是一个很好的选择。遥控器本身就是一个命令的接收者,而打开电视这个动作实现上就是一个指令。
实现方法

1、声明一个实体电视的遥控器类,也就是具体的指令接收者;
/*** 遥控器*/
public class RemoteControl {public void onGreenButton(){System.out.println("打开电视");}public void onRedButton(){System.out.println("关闭电视");}public void onButton2_1(){System.out.println("河南卫视");}public void onButton2_2(){System.out.println("北京卫视");}public void onButton2_3(){System.out.println("山东卫视");}public void onDefaultButton(){System.out.println("中央电视台");}
}
2、声明一个抽象指令接口,在其中定义一个抽象指令执行内容方法,供具体的指令类去实现;
/*** 抽象命令接口*/
public interface Command {void execute();
}
3、声明具体按钮指令,实现于抽象接口中,重写抽象方法的具体实现,如打开电视、关闭电视;
/*** 打开指令*/
public class OpenCommand implements Command{private RemoteControl remoteControl;public OpenCommand(RemoteControl remoteControl) {this.remoteControl = remoteControl;}@Overridepublic void execute() {this.remoteControl.onGreenButton();}
}
/*** 关闭指令*/
public class CloseCommand implements Command {private RemoteControl remoteControl;public CloseCommand(RemoteControl remoteControl) {this.remoteControl = remoteControl;}@Overridepublic void execute() {this.remoteControl.onRedButton();}
}
4、声明一个指令调用者,持有一个具体的指令对象,即遥控器上具体的按钮指令;
/*** 命令执行器*/
public class CommandInvoker {private Command command;public CommandInvoker(Command command) {this.command = command;}public void executeCommand(){this.command.execute();}
}
5、编写客户端,将具体的遥控器对象、具体的指令整合在一起,执行具体的业务操作;
public class Client {public static void main(String[] args) {RemoteControl remoteControl = new RemoteControl();Command command=new OpenCommand(remoteControl);CommandInvoker commandInvoker = new CommandInvoker(command);commandInvoker.executeCommand();command=new CloseCommand(remoteControl);commandInvoker=new CommandInvoker(command);command.executeCommand();}
}

如何扩展
相信现在大家都了解这样一个事实:现在的电视机真是相当的难用,广告多的不行,各种套娃式的收费,但是我观察到一个也比较恶心的现象,现在遥控器上面的数字按钮没有了,看上去很简洁,实际上使用体验并不好。那么如果把这些数字按钮再加上去,我想看哪个电视台直接就按哪个,岂不妙哉?在命令模式的基础上来扩展类似的需求非常简单:

1、增加数字按钮指令;
/*** 数字指令*/
public class NumberCommand implements Command{private int number;private RemoteControl remoteControl;public NumberCommand(RemoteControl remoteControl) {this.remoteControl = remoteControl;}public void setNumber(int number) {this.number = number;}@Overridepublic void execute() {switch (number){case 21:this.remoteControl.onButton2_1();break;case 22:this.remoteControl.onButton2_2();break;case 23:this.remoteControl.onButton2_3();break;default:this.remoteControl.onDefaultButton();break;}}
}
2、升级一下原来的遥控器的相关业务即可,原先的其他指令按钮,如打指令按钮、关闭指令按钮等,不用变化;
public class Client {public static void main(String[] args) {RemoteControl remoteControl = new RemoteControl();Command command=new NumberCommand(remoteControl);CommandInvoker commandInvoker=new CommandInvoker(command);commandInvoker.executeCommand();((NumberCommand) command).setNumber(21);commandInvoker.executeCommand();((NumberCommand) command).setNumber(22);commandInvoker.executeCommand();}
}
命令模式适用哪些场景
具有以下特征的业务场景,都是比较适合使用命令行模式的,如:
- 请求发送者和接收者需要解耦:命令模式允许发送者和接收者之间没有直接的关系,二者通过命令进行交互。发送者只需要知道发送请求对象,不需要知道如何完成请求;接收者只需要知道如何完成请求,不需要知道请求的发送过程。这种解耦方式可以使系统更加灵活和可维护。
- 需要抽象出待执行的行为:命令模式可以将等待执行的行为抽象出来,将命令封装成对象,以便在程序中灵活地操作。这种抽象方式可以方便地对命令进行记录、撤销、重做等操作。
命令模式的优点和缺点
优点
- 解耦:命令模式解耦了请求发送者和接收者之间的耦合关系,使得发送者和接收者只需要通过命令对象进行交互,而不需要直接联系。
- 抽象:命令模式将请求或操作封装成命令对象,从而抽象出待执行的行为,提高了系统的可维护性和可扩展性。
- 事务支持:命令模式支持事务操作,可以将多个命令组合成一个事务,从而保证操作的原子性和一致性。
- 撤销支持:命令模式可以方便地支持撤销操作,通过实现命令的撤销功能,可以轻松地回滚操作,提高系统的可靠性和可维护性。
- 日志记录:命令模式可以实现日志记录功能,将命令的操作记录下来,以便后续的审计和调试。
- 宏命令:命令模式可以支持宏命令,将多个命令组合成一个宏命令,一次性执行多个操作。
缺点
- 具体命令类可能过多:在命令模式中,每个不同的请求或操作都需要一个具体的命令类来封装。因此,如果有很多不同的请求或操作,就需要定义很多具体的命令类,这会增加系统的复杂度和维护成本。
- 实现复杂度较高:命令模式需要定义很多类和接口,同时需要实现撤销、事务、日志记录等功能,因此实现起来比较复杂。
- 设计难度较大:命令模式需要设计出合适的命令类和接收者类,以及它们之间的交互关系和行为定义等,这需要具备较高的设计能力和经验。
总结
命令模式是一种行为型设计模式,它允许将请求或操作封装成对象,从而解耦了请求发送者和接收者之间的耦合关系,具有解耦、抽象、事务支持、撤销支持、日志记录和宏命令等优点,但也存在具体命令类过多、实现复杂度高和设计难度大等缺点。在使用命令模式时,需要根据具体的应用场景和需求来权衡其优劣。
总之,命令模式是一种非常实用的设计模式,它可以提高系统的灵活性和可维护性,使得代码更加清晰、易于理解和扩展,强烈建议在合适的场景中使用它。

相关文章:
设计模式之命令模式
阅读建议 嗨,伙计!刷到这篇文章咱们就是有缘人,在阅读这篇文章前我有一些建议: 本篇文章大概4000多字,阅读时间长可能需要4-5分钟,请结合示例耐心读完,绝对有收获。设计模式属于程序的设计思…...
Linux学习笔记--高级
Shell概述 1,shell概述 是一个c语言编写的脚本语言,是linux和用户的桥梁,用户输入命令交给shell处理。shell,将相应的操作传递给内核(kernel),内核把处理的结果输出给用户 1.1Shell解释器有哪…...
在Java中操作Redis
Redis中如何的去存放一个Java对象? 直接存放Json类型即可,因为我们Json类型最终就是一个String类型。 Redis的Java客户端 Redis的常用命令是我们操作Redis的基础,那么我们在Java程序当中如何来操作Redis呢? 要想基于Java语言…...
【服务器】fiber协程模块
fiber协程模块 以下是从sylar服务器中学的,对其的复习; sylar的fiber协程模块是基于ucontext_t实现非对称协程 函数只有两个行为:调用与返回。一旦函数返回后,它在栈上所拥有的状态将被销毁。协程相比函数多了两个动作…...
SparkML
SparkML SparkML_lr_train :读取py处理后的train表用于训练,将训练模型保存好。 SparkML_lr_predict :读取训练好的模型,读取py处理后的test表用于预测。将预测结果写入normal_data中,根据id修改stream_is_normal的值。…...
实时定位与路径优化:跑腿App系统开发中的地理信息技术
本文将介绍如何使用地理信息技术实现实时定位和路径优化功能,以提高跑腿服务的效率。 实时定位 用户位置获取 # 示例:获取用户的实时位置 def get_user_location(user_id):# 使用GPS或网络定位技术获取用户的地理坐标# 返回经度和纬度信息return lon…...
Tomcat的HTTP Connector
https://tomcat.apache.org/tomcat-10.1-doc/config/http.html 一个Connector代表一个接收请求、返回响应的端点(endpoint)。 HTTP Connector 元素代表一个支持HTTP/1.1的Connector组件。一个这样的组件在服务端一个指定的TCP端口上监听连接。一个Serv…...
将Pytorch搭建的ViT模型转为onnx模型
本文尝试将pytorch搭建的ViT模型转为onnx模型。 首先将博主上一篇文章中搭建的模型ViT Vision Transformer超详细解析,网络构建,可视化,数据预处理,全流程实例教程-CSDN博客转存为.pth torch.save(model, my_vit_model.pth) 然…...
图神经网络(GNN)性能优化方案汇总,附37个配套算法模型和代码
图神经网络的表达能力对其性能和应用范围有着重要的影响,是GNN研究的核心问题和发展方向。增强表达能力是扩展GNN应用范围、提高性能的关键所在。 目前GNN的表达能力受特征表示和拓扑结构这两个因素的影响,其中GNN在学习和保持图拓扑方面的缺陷是限制表…...
国科大移动互联网考试资料(2023+2020+2018真题+答案)
老师王文杰。真题附加2022部分...
ModStart系统安全规范建议
1 不要使用弱密码 很多人为了系统管理方便(或者是懒),经常会设置类似 123456、admin 这样的管理密码,这样的密码很容易被暴力软件扫描出来。 2 不要使用默认配置 默认的软件系统设置、默认的系统端口、默认的网站设置在发生漏洞…...
【漏洞复现】Django_debug page_XSS漏洞(CVE-2017-12794)
感谢互联网提供分享知识与智慧,在法治的社会里,请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞分析3、漏洞验证 说明内容漏洞编号CVE-2017-12794漏洞名称Django_debug page_XSS漏洞漏洞评级影响范…...
Redis性能调优:深度剖析与示例解析
标题:Redis性能调优:深度剖析与示例解析 引言 Redis是一款强大的开源内存数据库,广泛应用于高性能系统。然而,为了充分发挥Redis的性能,需要进行合理的性能调优。本博客将深入介绍Redis性能调优的策略和示例…...
oracle查询前几条数据的方法
在Oralce中实现select top N:由于Oracle不支持select top 语句,所以在oracle中经常是用order by 跟rownum的组合来实现select top n的查询。 方法1: SELECT * FROM (SELECT * FROM EMP ORDER BY SAL DESC) WHERE ROWNUM < 5 --抽取处记录…...
c#弹性和瞬态故障处理库Polly
1. 重试(Retry) Policy .Handle<Exception>() //指定需要重试的异常类型 .Retry(2,(ex,count,context)> { //指定发生异常重试的次数Console.WriteLine($ "重试次数{count},异常{ex.Message}" ); }) …...
20231107-前端学习炫酷菜单效果和折叠侧边栏
炫酷菜单效果 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>炫酷菜单效果</title><…...
基于CLIP的图像分类、语义分割和目标检测
OpenAI CLIP模型是一个创造性的突破; 它以与文本相同的方式处理图像。 令人惊讶的是,如果进行大规模训练,效果非常好。 在线工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 3D…...
python爬虫(数据获取——selenium)
环境测试 from selenium import webdriverchromedriver_path r"C:\Program Files\Google\Chrome\Application\chromedriver.exe" driver webdriver.Chrome()url "https://www.xinpianchang.com/discover/article?fromnavigator" driver.get(url)drive…...
[wp]NewStarCTF 2023 WEEK5|WEB
前言:比赛是结束了,但我的学习还未结束,看看自己能复习几道题吧,第四周实在太难 Final 考点: ThinkPHP 5.0.23 RCE一句话木马上传SUID提权(find) 解题: 首先页面就给了ThinkPHP V5, 那无非考…...
未将对象引用设置到对象实例
环境 vs 2017 qt 5.13.0 qt-vs-addin 2.10 qt 项目打开的vs 2010 的项目 配置完成之后可以编译执行,但是新建qt 类提示 未将对象引用设置到对象实例 问题 插件的版本太高了使用低版本的,到qt 官网下载Index of /official_releases/vsaddin 下载q…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
FFmpeg avformat_open_input函数分析
函数内部的总体流程如下: avformat_open_input 精简后的代码如下: int avformat_open_input(AVFormatContext **ps, const char *filename,ff_const59 AVInputFormat *fmt, AVDictionary **options) {AVFormatContext *s *ps;int i, ret 0;AVDictio…...
基于小程序老人监护管理系统源码数据库文档
摘 要 近年来,随着我国人口老龄化问题日益严重,独居和居住养老机构的的老年人数量越来越多。而随着老年人数量的逐步增长,随之而来的是日益突出的老年人问题,尤其是老年人的健康问题,尤其是老年人产生健康问题后&…...

