设计模式详解(十九)——命令模式
命令模式简介
命令模式定义
命令模式(Command Pattern)是一种在面向对象程序设计中常用的行为型设计模式。命令模式的核心思想在于将请求封装成一个对象,从而使发出请求的责任和执行请求的责任分割开。它可以让请求发送者和请求接收者之间消除彼此之间的耦合关系。这种模式的关键在于将请求的行为参数化,使得不同的请求可以在不同时间由不同的对象来处理。同时,它支持对请求排队、记录请求日志,以及实现可撤销的操作。
命令模式包含以下角色:
- 命令角色(Command):这是一个抽象类或接口,它定义了执行命令的方法,通常包含执行(execute)方法,但不实现具体的命令行为。
- 具体命令角色(Concrete Command):实现了命令接口,持有接收者的引用,并在执行方法中调用接收者的方法来执行具体的命令行为;实现execute()方法,负责调用接收者的相应操作。
- 接收者角色(Receiver):接收者是执行命令的对象,它实现了命令所需要执行的具体操作。接收者可以有多个,每个接收者都可以执行不同的操作。
- 请求者角色(Invoker):请求者持有命令对象的引用,并通过调用命令的execute()方法来执行请求。它不需要知道命令的具体实现细节,只需知道如何调用命令对象。负责调用命令对象执行请求,相关的方法叫做行动方法(如action())。
- 客户端(Client):创建具体命令对象并设置其接收者,将命令对象传递给调用者执行请求。
命令模式优缺点:
优点:
- 解耦合:命令模式降低了调用者和接收者之间的耦合度。调用者只需知道命令的接口,而不需要知道命令的具体实现细节,从而降低系统的耦合度。
- 可扩展性:新的命令可以很容易地添加到系统中,而无需修改现有的代码结构,从而不影响现有的命令,符合开闭原则。
- 支持撤销和重做:由于命令被封装成对象,在具体命令类中保存状态,可以轻松地实现命令的撤销和重做功能。
- 日志记录:可以在命令对象中增加日志记录功能,记录命令的执行情况。这对于系统的调试和审计非常有帮助。
- 容易实现队列和宏命令:可以创建宏命令(复合命令),即把多个简单命令组合成一个更复杂的命令。
- 更好的安全性:命令模式可以帮助限制对敏感方法的访问权限,因为命令接口可以隐藏这些方法的实现细节。
缺点:
- 过多的具体命令类:每个具体命令都需要一个单独的类,可能会导致类的数量增加。如果系统中的命令种类非常多,可能会导致产生大量的具体命令类
- 系统复杂性:引入了额外的类和对象,增加了系统的复杂性。
- 执行效率:由于命令被封装成对象,可能会导致执行效率略有降低,特别是在需要大量命令对象的情况下。
- 可能增加对象数量:为了支持撤销操作,每个命令可能需要保存额外的状态信息,这会增加对象的数量。
使用场景
- 撤销和重做操作:当需要支持撤销和重做操作时,可以使用命令模式将每个操作封装成一个命令对象,从而实现撤销和重做功能。
- 队列请求:当需要将请求排队、记录请求日志、支持事务等场景时,可以使用命令模式将请求封装成命令对象,然后由调用者依次执行这些命令对象。
- 宏命令:当需要将多个简单命令组合成一个复合命令时,可以使用命令模式。
- 远程控制和自动化:在远程控制系统中,可以使用命令模式来发送远程指令给接收设备。
- 日程安排系统:在日程安排系统中,可以使用命令模式实现对日程的增加、删除、修改等操作,支持撤销和重做功能。
- GUI 应用程序:在图形用户界面中,命令模式可以用来处理用户的输入事件,例如按钮点击、菜单选项选择等
- 日志记录和历史追踪:命令模式可以用来记录操作的历史,这对于审计和调试非常有用。
以下举一个命令模式的例子:
下面通过一个简单的例子来演示。
假如我们自己开发一个音乐播放器,有播放功能、切换上一首功能、切换下一首功能、暂停功能,我们自己去操作的时候并不是直接调用音乐播放器的方法,而是通过一个控制条去传达指令给播放器内部。那么每个按钮就相当于是对一条命令的封装。
创建接收者角色(Receiver)
/*** 音乐播放器,作为接收者角色(Receiver)*/
public class MusicPlayer {public void play() {System.out.println("播放");}public void stop() {System.out.println("暂停");}public void previousSong() {System.out.println("拖动进度条");}public void nextSong() {System.out.println("停止播放");}
}
创建命令角色(Command)
/*** 命令角色(Command)*/
public interface Command {void execute();
}
创建4个具体命令角色(Concrete Command),分别是实现播放,暂停,上一首,下一首的操作
/*** 播放操作*/
public class Play implements Command{private MusicPlayer musicPlayer;public Play(MusicPlayer musicPlayer) {this.musicPlayer = musicPlayer;}@Overridepublic void execute() {musicPlayer.play();}
}
/*** 暂停操作*/
public class Stop implements Command{private MusicPlayer musicPlayer;public Stop(MusicPlayer musicPlayer) {this.musicPlayer = musicPlayer;}@Overridepublic void execute() {musicPlayer.stop();}
}
/*** 切换上一首歌操作*/
public class PreviousSong implements Command{private MusicPlayer musicPlayer;public PreviousSong(MusicPlayer musicPlayer) {this.musicPlayer = musicPlayer;}@Overridepublic void execute() {musicPlayer.previousSong();}
}
/*** 切换下一首歌操作*/
public class NextSong implements Command{private MusicPlayer musicPlayer;public NextSong(MusicPlayer musicPlayer) {this.musicPlayer = musicPlayer;}@Overridepublic void execute() {musicPlayer.nextSong();}
}
创建请求者角色(Invoker)
import java.util.ArrayList;
import java.util.List;/*** 请求者角色(Invoker)*/
public class Action {private List<Command> actions = new ArrayList<Command>();public void addAction(Command action){actions.add(action);}public void execute(Command action){action.execute();}public void executes(){for (Command action:actions) {action.execute();}actions.clear();}
}
创建客户端(Client)
/*** 客户端(Client)*/
public class Client {public static void main(String[] args) {MusicPlayer musicPlayer = new MusicPlayer();Action action = new Action();//立即执行操作action.execute(new Play(musicPlayer));action.execute(new Stop(musicPlayer));action.execute(new PreviousSong(musicPlayer));action.execute(new NextSong(musicPlayer));System.out.println("=========================");// 将方法添加到列表中,方便执行多条操作// 例如音乐播放器点下一首时,会切换下一首歌,并进行播放,这就设计一个按钮就行,然后这个按钮可以直接实现,切换下一首歌并播放的操作Action actionOther = new Action();actionOther.addAction(new NextSong(musicPlayer));actionOther.addAction(new Play(musicPlayer));actionOther.executes();}
}
输出结果如下所示:
播放
暂停
切换到上一首歌
切换到下一首歌
=========================
切换到下一首歌
播放
在上述例子中,
展示了命令模式在音乐播放器中的应用。MusicPlayer 类作为接收者,提供了播放、暂停、上一首和下一首的操作。Command 接口定义了命令的通用执行方法。四个具体命令类 (Play, Stop, PreviousSong, NextSong) 实现了这些操作,并持有 MusicPlayer 的引用。Action 类作为调用者,管理命令列表并执行命令。客户端通过 Action 对象立即执行单个命令或批量执行一系列命令,实现了命令的解耦和灵活管理。
总而言之:
命令模式是一种行为设计模式,用于将请求封装为对象,以便使用不同的请求对客户端进行参数化,支持命令队列、日志记录和撤销操作。核心概念包括命令接口(Command)、具体命令类(Concrete Command)、接收者(Receiver)和调用者(Invoker)。命令接口定义了所有命令共有的执行方法,具体命令类实现了命令接口并封装了接收者对象的引用,接收者执行实际的业务逻辑,调用者则请求命令对象执行命令。命令模式的优势在于降低调用者与接收者之间的耦合度,支持撤销操作和宏命令,易于扩展新命令。但同时也可能导致产生大量具体命令类,客户端需要管理命令对象,且可能存在性能开销。适用于GUI应用程序、事务处理、宏命令、命令行界面和需要撤销操作的场景。
以上代码下载请点击该链接:https://github.com/Yarrow052/Java-package.git
相关文章:
设计模式详解(十九)——命令模式
命令模式简介 命令模式定义 命令模式(Command Pattern)是一种在面向对象程序设计中常用的行为型设计模式。命令模式的核心思想在于将请求封装成一个对象,从而使发出请求的责任和执行请求的责任分割开。它可以让请求发送者和请求接收者之间消…...
实战:MySQL数据同步神器之Canal
1.概叙 场景一:数据增量实时同步 项目中业务数据量比较大,每类业务表都达到千万级别,虽然做了分库分表,每张表数据控制在300W以下,但是效率还是达不到要求,为了提高查询效率,打算使用ES进行数…...
5.6软件工程-运维
运维 系统转换系统维护系统评价练习题 系统转换 新老系统的转换 系统转换是指:新系统开发完毕,投入运行,取代现有系统的过程,需要考虑多方面的问题,以实现与老系统的交接,有一下三种转换计划: …...
在JavaScript中如何确保构造函数只被new调用
构造函数是一个特殊的函数,用于初始化一个新创建的对象。它是在创建对象时自动调用的。构造函数通常用于为对象的属性赋值,或者执行其他必要的设置。 使用函数名大写字母开头,这是一种命名约定,用于区分构造函数和普通函数。如何…...
【数据结构算法经典题目刨析(c语言)】反转链表(图文详解)
💓 博客主页:C-SDN花园GGbond ⏩ 文章专栏:数据结构经典题目刨析(c语言) 目录 一、题目描述 二、思路分析 三、代码实现 一、题目描述: 二、思路分析 : 通过三个指针n1,n2,n3来实现链表的反转 1.首先初始化 n1为…...
机器学习之争:Python vs R,谁更胜一筹?
一、引言 随着人工智能和大数据的迅速发展,机器学习已成为现代科技的重要组成部分。在医疗、金融、零售、制造等多个领域,机器学习技术的应用无处不在。从数据分析到预测建模,再到深度学习,机器学习正在改变我们的工作和生活方式…...
Vulnhub靶机:JANGOW_ 1.0.1
目录 前言: 一、安装虚拟机Jangow:1.0.1靶机 二、Web部分 前言: 难度:简单,本文使用VirtualBox打开,下载地址: https://download.vulnhub.com/jangow/jangow-01-1.0.1.ova 一、安装虚拟机J…...
Python脚本实现USB自动复制文件
USB驱动器作为常见的数据存储设备,经常用于数据传输和备份。 然而,我们在手动处理文件复制可能效率低下且容易出错。 因此,我们可以利用Python编写脚本来自动化这一过程,提高效率和数据安全性。 准备工作 首先,我们需…...
【C++学习第19天】最小生成树(对应无向图)
一、最小生成树 二、代码 1、Prim算法 #include <cstring> #include <iostream> #include <algorithm>using namespace std;const int N 510, INF 0x3f3f3f3f;int n, m; int g[N][N]; int dist[N]; bool st[N];int prim() {memset(dist, 0x3f, sizeof di…...
第一个 Flask 项目
第一个 Flask 项目 安装环境创建项目启动程序访问项目参数说明Flask对象的初始化参数app.run()参数 应用程序配置参数使用 Flask 的 config.from_object() 方法使用 Flask 的 config.from_pyfile() 方法使用 Flask 的 config.from_envvar() 方法步骤 1: 设置环境变量步骤 2: 编…...
利用 Angular 发挥环境的力量
一.介绍 您是否曾想过如何在不同的环境中为同一应用设置不同的颜色、标题或 API 调用?可以肯定的是,生产 API 和测试 API 是不同的,应谨慎使用。部署时,我们不会在项目的所有地方手动更改所有 API 调用。不应这样做,因…...
Vue3+TypeScript+printjs 实现标签批量打印功能
前言:临时性需求没怎么接触过前端,代码实现有问题及优化点希望大佬可以留言告知一下 开发工具:VS CODE 界面开发:Vue3TypeScriptElementPlus 打印组件:Print-JS 前端打印入口图: 标签页面: …...
微信文件如何直接打印及打印功能在哪里设置?
在数字化时代,打印需求依旧不可或缺,但传统打印店的高昂价格和不便操作常常让人头疼。幸运的是,琢贝打印作为一款集便捷、经济、高效于一体的网上打印平台,正逐渐成为众多用户的首选。特别是通过微信小程序下单,更是让…...
dataX -20240804-master分支
1、相关报错 Error: java.io.IOException: java.lang.RuntimeException: ORC split generation failed with exception: org.apache.orc.impl.SchemaEvolution$IllegalEvolutionException: ORC does not support type conversion from file type struct<nanos:int> (10)…...
【网络】传输层
传输层 一、预备知识1、端口号1、端口号范围划分2、知名端口号3、两个问题4、netstat && iostate5、pidof6、谈下面协议始终铭记两个问题 二、UDP协议(简单)1、UDP协议端格式2、UDP的特点3、面向数据报4、UDP缓冲区 三、TCP协议(重点…...
学生管理系统之更新和删除、筛选
学生管理系统之更新和删除 建立新的窗口 添加组件 进行布局 使用Widget把二个放在一块,作为一列,然后全选进行栅格布局,最后添加弹簧进行微调。 编写增加的槽函数 在主函数中调用对话框...
教您一键批量下载拼多多批发图片信息,节省时间
图片是电商的核心展示手段,高质量、吸引人的图片能显著提升商品吸引力,增强用户体验,促进购买决策。良好的视觉呈现有助于品牌形象的塑造,提高转化率和客户满意度,对电商平台的流量和销售业绩具有直接影响。 使用图快…...
基于微信小程序的微课堂笔记的设计与实现(源码+论文+部署讲解等)
博主介绍:✌全网粉丝10W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术栈介绍:我是程序员阿龙ÿ…...
去噪扩散恢复模型
去噪扩散恢复模型 Bahjat Kawar 计算机科学系 以色列海法理工学院 bahjat.kawarcs.technion.ac.il Michael Elad 计算机科学系 以色列海法理工学院 eladcs.technion.ac.il Stefano Ermon 计算机科学系 美国加利福尼亚州斯坦福大学 ermoncs.stanford.edu …...
Stable Diffusion 官方模型V1.5版本下载
模型描述 Stable Diffusion的官方模型更适合绘制偏写实的风格,如果您想绘制二次元之类的风格,可以考虑下载本站的其它模型。 安装方法 将模型下载后,将会得到一个名为****.ckpt格式的文件,将该文件剪切至你的Stable Diffusion本…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换
目录 关键点 技术实现1 技术实现2 摘要: 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式(自动驾驶、人工驾驶、远程驾驶、主动安全),并通过实时消息推送更新车…...
Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...
