命令设计模式
简介
命令模式(Command Pattern)是对命令的封装,每一个命令都是一个操作:请求方发出请求要求执行一个操作;接收方收到请求,并执行操作。命令模式解耦了请求方和接收方,请求方只需请求执行命令,不用关心命令怎样被接收、怎样被操作及是否被执行等。命令模式属于行为型设计模式。
通用模板
-
创建接收者角色:该类负责具体实施或执行一个请求。
// 接收者 public class Receiver {public void action() {System.out.println("执行具体操作");} }
-
创建抽象命令角色:定义需要执行的所有命令行为。
// 抽象命令接口 public interface ICommand {void execute(); }
-
创建具体命令角色:该类内部维护一个Receiver,在其execute()方法中调用Receiver的相关方法。
// 具体命令 public class ConcreteCommand implements ICommand {// 直接创建接收者,不暴露给客户端private Receiver receiver = new Receiver();@Overridepublic void execute() {this.receiver.action();} }
-
创建请求者角色:接收客户端的命令,并执行命令。
// 请求者 public class Invoker {private ICommand cmd;public Invoker(ICommand cmd) {this.cmd = cmd;}public void action(){this.cmd.execute();} }
模板测试
- 测试代码
public class Client {public static void main(String[] args) {ConcreteCommand cmd = new ConcreteCommand();Invoker invoker = new Invoker(cmd);invoker.action();} }
- 测试结果
执行具体操作
应用场景
在日常生活中,命令模式是很常见的。比如,经历过黑白电视机年代的小伙伴应该都有过这样的经历。那个年代在看电视的时候,想要换个频道简直不容易。我们得走到电视机前扭动换台的旋钮,一顿“咔咔咔”折腾才能完成频道的切换。如今,遥控器的发明简直就是“解放战争”,我们躺在沙发上只需要轻轻一按遥控器即可完成频道的切换。这就是命令模式,将换台请求和换台处理完全解耦了。
当系统的某项操作具备命令语义,且命令实现不稳定(变化)时,可以通过命令模式解耦请求与实现,使用抽象命令接口使请求方的代码架构稳定,封装接收方具体命令的实现细节。接收方与抽象命令接口呈现弱耦合(内部方法无须一致),具备良好的扩展性。命令模式主要适用于以下应用场景。 (1)现实语义中具备“命令”的操作(如命令菜单、Shell命令等)。
(2)请求的调用者和接收者需要解耦,使得调用者和接收者不直接交互。
(3)需要抽象出等待执行的行为,比如撤销(Undo)操作和恢复(Redo)等操作。
(4)需要支持命令宏(即命令组合操作)。
优点
(1)通过引入中间件(抽象接口),解耦了命令请求与实现。
(2)扩展性良好,可以很容易地增加新命令。
(3)支持组合命令,支持命令队列。
(4)可以在现有命令的基础上,增加额外功能。比如日志记录,结合装饰器模式会更加灵活。
缺点
(1)具体命令类可能过多。
(2)命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构、解耦请求与实现,引入了额外类型结构(引入了请求方与抽象命令接口),增加了理解上的困难。不过这也是设计模式的通病,抽象必然会额外增加类的数量;代码抽离肯定比代码聚合更难理解。
“生搬硬套”实战
场景描述
我们可以用一个遥控器控制家用电器(如电灯)作为例子。在这个场景中,遥控器是发出命令的对象,而家用电器则是执行命令的对象。
代码开发
-
创建接收者角色(这里指的是实际执行的对象灯)
// 定义接收者类,即实际执行动作的对象——Light public class Light {public void turnOn() {System.out.println("Light is on");}public void turnOff() {System.out.println("Light is off");} }
-
创建抽象命令角色:(这里指的是抽象命令接口,定义执行和取消操作)
// 定义一个命令接口 public interface ICommand {void execute();void undo(); }
-
创建具体命令角色(这里指的是开灯和关灯的具体命令实现类)
// 具体的开灯命令类 public class LightOnCommand implements ICommand{private final Light light;public LightOnCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.turnOn();}@Overridepublic void undo() {light.turnOff();} }
// 具体关灯命令类 public class LightOffCommand implements ICommand {private final Light light;public LightOffCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.turnOff();}@Overridepublic void undo() {light.turnOn();} }
-
创建请求者角色(这里指的就是遥控器)
// 定义一个调用者——遥控器类,它将保存命令并执行 public class RemoteControl {private ICommand command;public void setCommand(ICommand command) {this.command = command;}public void pressButton() {command.execute();}public void undo() {command.undo();} }
至此,我们就通过“生搬硬套”命令模式的模板设计出一套遥控器开关灯的案例,接下来我们进行测试:
-
测试代码
public class Client {public static void main(String[] args) {Light light = new Light();RemoteControl remote = new RemoteControl();// 设置打开电灯的命令remote.setCommand(new LightOnCommand(light));remote.pressButton(); // 执行命令:打开电灯remote.undo(); // 撤销命令:关闭电灯// 设置关闭电灯的命令remote.setCommand(new LightOffCommand(light));remote.pressButton(); // 执行命令:关闭电灯remote.undo(); // 撤销命令:打开电灯} }
-
测试结果
Light is on Light is off Light is off Light is on
场景描述2
假如我们开发一个播放器,播放器有播放功能、拖动进度条功能、停止播放功能、暂停功能,我们在操作播放器的时候并不是直接调用播放器的方法,而是通过一个控制条去传达指令给播放器内核,具体传达什么指令,会被封装为一个个按钮。那么每个按钮就相当于对一条命令的封装。用控制条实现了用户发送指令与播放器内核接收指令的解耦。
代码开发2
-
创建接收者角色(这里指的是实际执行的对象播放器)
// 定义接收者类,即实际执行动作的对象——播放器 public class GPlayer {public void play() {System.out.println("正常播放");}public void speed() {System.out.println("拖动进度条");}public void stop() {System.out.println("停止播放");}public void pause() {System.out.println("暂停播放");} }
-
创建抽象命令角色:(这里指的是抽象命令接口,定义执行命令操作)
// 定义一个命令接口 public interface IAction {void execute(); }
-
创建具体命令角色(这里指的是播放、暂停、停止、加速具体命令实现类)
// 具体的播放命令 public class PlayAction implements IAction {private GPlayer player;public PlayAction(GPlayer player) {this.player = player;}@Overridepublic void execute() {player.play();} }
// 具体的暂停命令 public class PauseAction implements IAction {private GPlayer player;public PauseAction(GPlayer player) {this.player = player;}@Overridepublic void execute() {player.pause();} }
// 具体的停止命令 public class StopAction implements IAction {private GPlayer player;public StopAction(GPlayer player) {this.player = player;}@Overridepublic void execute() {player.stop();} }
// 具体的拖动进度条命令 public class SpeedAction implements IAction {private GPlayer player;public SpeedAction(GPlayer player) {this.player = player;}@Overridepublic void execute() {player.speed();} }
-
创建请求者角色(这里指的就是播放器控制面板)
import java.util.ArrayList; import java.util.List;// 定义一个调用者——控制面板类,它将保存命令并执行 public class Controller {private List<IAction> actions = new ArrayList<>();public void addAction(IAction action) {actions.add(action);}public void execute(IAction action) {action.execute();}public void executes() {for (IAction action : actions) {action.execute();}actions.clear();} }
至此,我们就通过“生搬硬套”命令模式的模板设计出一套通过播放器控制面板上的按钮来控制播放器的案例,接下来我们进行测试:
- 代码测试
public class Client {public static void main(String[] args) {GPlayer player = new GPlayer();Controller controller = new Controller();controller.execute(new PlayAction(player));controller.addAction(new PauseAction(player));controller.addAction(new PlayAction(player));controller.addAction(new StopAction(player));controller.addAction(new SpeedAction(player));controller.executes();} }
- 测试结果
正常播放 暂停播放 正常播放 停止播放 拖动进度条
总结
在软件系统中,行为请求者与行为实现者通常是一种紧耦合关系,因为这样的实现简单明了。但紧耦合关系缺乏扩展性,在某些场合中,当需要对行为进行记录、撤销或重做等处理时,只能修改源码。而命令模式通过在请求与实现间引入一个抽象命令接口,解耦了请求与实现,并且中间件是抽象的,它由不同的子类实现,因此具备扩展性。所以,命令模式的本质是解耦命令请求与处理。
相关文章:
命令设计模式
简介 命令模式(Command Pattern)是对命令的封装,每一个命令都是一个操作:请求方发出请求要求执行一个操作;接收方收到请求,并执行操作。命令模式解耦了请求方和接收方,请求方只需请求执行命令&…...
探索智能新境界:最好用的AI工具盘点
你用过最好用的AI工具有哪些? 在人工智能技术飞速发展的今天,AI工具正逐渐成为我们工作和生活中不可或缺的助手。它们不仅提高了效率,还为我们提供了创新的解决方案。作为一名对AI充满热情的用户,我有幸体验了许多优秀的AI工具。…...

【Redis】持久化(下)-- AOF
文章目录 AOF概念如何使用AOFAOF工作流程命令写入演示文件同步策略 AOF的重写机制概念触发重写机制AOF重写流程 启动时数据恢复混合持久化总结 AOF 概念 AOF持久化:以独立日志的方式记录每次的写命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的.AOF的主要作用是解决…...
用Arduino单片机制作一个简单的音乐播放器
Arduino单片机上有多个数字IO针脚,可以输出数字信号,用于驱动发声器件,从而让它发出想要的声音。蜂鸣器是一种常见的发声器件,通电后可以发出声音。因此,单片机可以通过数字输出控制蜂鸣器发出指定的声音。另外&#x…...
软件工程相关
1.软件过程模型(重要) 1.1.瀑布模型 只适合需求明确的项目严格串行化,很长时间才能看到结果。严格区分阶段,每个阶段因果紧密相连,且要求每个阶段一次性解决该阶段的任务 1.2.原型模型(构造简易模型确定…...
速盾:游戏加速下载可以用cdn吗?
随着互联网的快速发展,游戏下载已经成为许多游戏玩家的常见需求。然而,由于游戏文件体积庞大,下载速度经常成为制约因素之一。为了解决这个问题,许多玩家开始寻找可以加速游戏下载速度的方法。其中一种常见的方法是使用CDN&#x…...
每日新闻掌握【2024年9月25日 星期三】
2024年9月25日 星期三 农历八月廿三 大公司/大事件 央行降低存量房贷利率,二套房贷最低首付比例下调到15% 国务院新闻办公室9月24日上午举行新闻发布会,中国人民银行、金融监管总局、中国证监会主要负责人介绍了金融支持经济高质量发展有关情况。多项重…...

8. Bug 与 Error
计算机程序中的缺陷通常被称为 bug。把它们想象成偶然爬进我们工作中的小东西,会让程序员感觉良好。当然,实际上是我们自己把它们放进去的。 如果程序是思想的结晶,我们可以将错误大致分为思想混乱造成的错误和将思想转化为代码时引入错误造成…...

论文 | Model-tuning Via Prompts Makes NLP Models Adversarially Robust
这篇论文研究了使用提示 (Prompting) 方法微调预训练语言模型,以提高其在对抗样本攻击下的鲁棒性。论文的主要贡献如下: 1.MVP 比 MLP-FT 更鲁棒: 论文比较了 MVP (Model-tuning Via Prompts) 和传统的 MLP-FT (Fine-tuning with an MLP head…...

828华为云征文|华为云Flexus云服务器X实例部署 即时通讯IM聊天交友软件——高性能服务器实现120W并发连接
营运版的即时通讯IM聊天交友系统:特点可发红包,可添加多条链接到用户网站和应用,安卓苹果APPPC端H5四合一 后端开发语言:PHP, 前端开发语言:uniapp混合开发。 集安卓苹果APPPC端H5四合一APP源码࿰…...

超好用的element的el-pagination分页组件二次封装-附源码及讲解
前言:在很多后台管理系统开发时总会有很多分页组件的使用,如果我们每次都用elementui官网的el-pagination去写的话,调整所有分页的样式就会很麻烦,而且页面内容也会很累赘繁琐。 讲解一个我经常使用的二次封装el-pagination组件&…...

【AIGC】通过OpenAi Canvas修改论文(附40条论文优化指令)
目录 1、用ChatGPT优化论文大纲和逻辑2、用ChatGPT充实论文内容3、用ChatGPT寻找案例和数据4、用ChatGPT检查语法和字词错误5、如何直接使用ChatGPT4o、o1、OpenAI Canvas6、OpenAI Canvas增强了啥?7、编程功能增强 在刚开始撰写学术论文时,很多小伙伴感…...

Kubernetes Pod详解
目录 1. Pod 介绍 1.1 Pod 结构 1.2 Pod 定义 2. Pod 配置 2.1 基本配置 2.2 镜像拉取 2.3 启动命令 2.4 环境变量 2.5 端口设置 2.6 资源配额 3. Pod 生命周期 3.1 创建和终止 3.2 初始化容器 3.3 钩子函数 3.4 容器探测 3.5 重启策略 4. Pod 调度 4.1 定向调…...

Vue2电商项目(七)、订单与支付
文章目录 一、交易业务Trade1. 获取用户地址2. 获取订单信息 二、提交订单三、支付1. 获取支付信息2. 支付页面--ElementUI(1) 引入Element UI(2) 弹框支付的业务逻辑(这个逻辑其实没那么全)(3) 支付逻辑知识点小总结 四、个人中心1. 搭建二级路由2. 展示动态数据(1). 接口(2).…...

你知道U盘怎么加密吗?
1、使用Windows BitLocker: 适用于Windows 10/11专业版及以上版本。 插入U盘,右键点击U盘图标,选择“启用BitLocker”。 设置密码,并选择加密选项,点击“开始加密”。 2、使用Mac的Disk Utility: 适用…...

【软件教程OBS下载使用】一篇文章教会你如何下载安装使用OBS-Studio
OBS Studio是全新的OBS(Open Broadcaster Software),是一款广泛应用的视频直播录制软件,跟经典版的区别就是,音频分路简单,在不出错的情况下性能优于经典版。可以说是高级版,目前仍然处于初期阶段,比起经典…...

鸿蒙next开发第一课03.ArkTs语法介绍-案例
前面已经学习了ArkTs的基本语法和DevEcoStudio的基本操作,接下来按照官方提示开发一个基本案例。 该案例是系统自带的demo,下载下来源代码后可以直接运行。 接下来我来演示如何运行demo。我在demo中加入了自己的注释。 切记:文件夹不能有中…...

HTML网页制作——设计系学生静态HTML网页设计作品
HTML网页制作——设计系学生静态HTML网页设计作品 网站主题为荷兰风格派,主要介绍荷兰风格设计的网站,由设计系学生亲自设计,独立开发网页,适用于学生自己的作品。 网站效果视频: 荷兰风格派(设计系学生网…...

智能翻译新纪元:4款英汉互译在线工具解析
大家好,我是一个喜欢找各种办公软件的人,今天咱们来聊聊那些让我们在英汉互译世界里如鱼得水的神器——福昕翻译在线、福昕翻译大师、海鲸AI论文翻译,还有DeepL翻译。这些家伙,简直就是我们跨语言交流的超级英雄! 1、…...

Cisco Meraki平台中国区注册
登陆下面网址注册cisco meraki中国区云平台账户 https://n4.meraki.cn/ 点击创建一个新账户 地区选择“china” 填写邮箱,名字,秘密,公司名称等信息,点击注册新账户 注册的邮箱会收到一封确认此邮箱的邮件,点击…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...

免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !
我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...
如何配置一个sql server使得其它用户可以通过excel odbc获取数据
要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据,你需要完成以下配置步骤: ✅ 一、在 SQL Server 端配置(服务器设置) 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到:SQL Server 网络配…...
Monorepo架构: Nx Cloud 扩展能力与缓存加速
借助 Nx Cloud 实现项目协同与加速构建 1 ) 缓存工作原理分析 在了解了本地缓存和远程缓存之后,我们来探究缓存是如何工作的。以计算文件的哈希串为例,若后续运行任务时文件哈希串未变,系统会直接使用对应的输出和制品文件。 2 …...