news 2026/3/20 8:45:45

设计模式学习(16) 23-14 命令模式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
设计模式学习(16) 23-14 命令模式

文章目录

  • 0. 个人感悟
  • 1. 概念
  • 2. 适配场景
    • 2.1 适合的场景
    • 2.2 常见场景举例
  • 3. 实现方法
    • 3.1 实现思路
    • 3.2 UML类图
    • 3.3 代码示例
  • 4. 优缺点
    • 4.1 优点
    • 4.2 缺点
  • 5. 源码分析

0. 个人感悟

  • 命令模式核心是将请求或者操作封装成对象。那么就可以基于这些对象进行额外操作,比如队列记录、日志、撤销恢复等
  • 实际工作中,对于任务队列其实已经有很多成熟的框架,不过万变不离其宗,理解命令模式,对于其它知识(比如三方件、架构)的学习还是很有帮助的

1. 概念

英文定义(《设计模式:可复用面向对象软件的基础》)

Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or long requests, and support undoable operations.

中文翻译

将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

理解

  1. 请求封装:将"做什么"(操作)和"谁来做"(执行者)分离
  2. 参数化:可以像传递参数一样传递命令对象
  3. 延迟执行:命令可以在创建后的某个时间点执行
  4. 可撤销/重做:通过记录命令历史实现操作回退

2. 适配场景

2.1 适合的场景

  1. 解耦调用者和接收者:需要将请求的发起者和执行者解耦时
  2. 支持撤销/重做:需要实现操作的撤销和重做功能
  3. 任务队列/日志:需要将请求排队、记录日志或延迟执行

2.2 常见场景举例

  • 遥控器控制家电:不同按钮触发不同设备的不同操作
  • 餐厅点餐系统:订单作为命令,厨师作为接收者
  • 文本编辑器:撤销/重做功能
  • 线程池任务调度:将任务封装为命令对象
  • 游戏控制:玩家输入映射到游戏角色的不同动作

3. 实现方法

3.1 实现思路

  1. 定义命令接口:声明执行命令的抽象方法(通常包含execute()undo()
  2. 创建具体命令类:实现命令接口,关联接收者对象
  3. 定义接收者类:实际执行操作的对象
  4. 创建调用者/请求者类:持有命令对象并触发执行
  5. 客户端组装:创建命令对象并设置给调用者

3.2 UML类图

![[命令模式_UML.png]]

角色说明

  • Command(命令接口):声明执行操作的接口
  • ConcreteCommand(具体命令):绑定接收者和动作
  • Receiver(接收者):知道如何执行请求的具体操作
  • Invoker(调用者):持有命令对象并触发执行
  • Client(客户端):创建具体命令并设置接收者

3.3 代码示例

背景: 遥控器控制家电,可以遥控灯、电视灯家具
命令接口:

publicinterfaceCommand{/** * @description 执行 * @author bigHao * @date 2026/1/20 **/voidexecute();/** * @description 撤销 * @author bigHao * @date 2026/1/20 **/voidundo();}

灯的接受者和具体命令:

publicclassLightReceiver{/** * @description 开灯 * @author bigHao * @date 2026/1/20 **/publicvoidon(){System.out.println("开灯了");}/** * @description 关灯 * @author bigHao * @date 2026/1/20 **/publicvoidoff(){System.out.println("关灯了");}}publicclassLightOnCommandimplementsCommand{privateLightReceiverlightReceiver;publicLightOnCommand(LightReceiverlightReceiver){this.lightReceiver=lightReceiver;}@Overridepublicvoidexecute(){lightReceiver.on();}@Overridepublicvoidundo(){lightReceiver.off();}}publicclassLightOffCommandimplementsCommand{privateLightReceiverlightReceiver;publicLightOffCommand(LightReceiverlightReceiver){this.lightReceiver=lightReceiver;}@Overridepublicvoidexecute(){lightReceiver.off();}@Overridepublicvoidundo(){lightReceiver.on();}}

电视接受者和命令:

publicclassTVReceiver{/** * @description 开机 * @author bigHao * @date 2026/1/20 **/publicvoidon(){System.out.println("电视开了");}/** * @description 关机 * @author bigHao * @date 2026/1/20 **/publicvoidoff(){System.out.println("电视关了");}}publicclassTVOnCommandimplementsCommand{privateTVReceiverreceiver;publicTVOnCommand(TVReceiverreceiver){this.receiver=receiver;}@Overridepublicvoidexecute(){receiver.on();}@Overridepublicvoidundo(){receiver.off();}}publicclassTVOffCommandimplementsCommand{privateTVReceiverreceiver;publicTVOffCommand(TVReceiverreceiver){this.receiver=receiver;}@Overridepublicvoidexecute(){receiver.off();}@Overridepublicvoidundo(){receiver.on();}}

遥控器:

publicclassRemoteController{publicstaticfinalintINIT_COMMAND_NUM=5;privateCommand[]onCommands;privateCommand[]offCommands;privateCommandundoCommand;publicRemoteController(){onCommands=newCommand[INIT_COMMAND_NUM];offCommands=newCommand[INIT_COMMAND_NUM];for(inti=0;i<INIT_COMMAND_NUM;i++){onCommands[i]=newNoCommand();offCommands[i]=newNoCommand();}}publicvoidsetOnCommand(intno,CommandonCommand,CommandoffCommand){onCommands[no]=onCommand;offCommands[no]=offCommand;}publicvoidon(intno){onCommands[no].execute();// 记录当前操作undoCommand=onCommands[no];}publicvoidoff(intno){offCommands[no].execute();// 记录当前操作undoCommand=offCommands[no];}publicvoidundo(){undoCommand.undo();}}

测试:

publicclassClient{publicstaticfinalintLIGHT_NO=0;publicstaticfinalintTV_NO=1;staticvoidmain(){RemoteControllerremoteController=newRemoteController();LightReceiverlightReceiver=newLightReceiver();LightOnCommandlightOnCommand=newLightOnCommand(lightReceiver);LightOffCommandlightOffCommand=newLightOffCommand(lightReceiver);// 按键0是灯开关remoteController.setOnCommand(LIGHT_NO,lightOnCommand,lightOffCommand);System.out.println("=== 按下开灯键位 ===");remoteController.on(LIGHT_NO);System.out.println("=== 按下关灯键位 ===");remoteController.off(LIGHT_NO);System.out.println("=== 按下撤销键 ===");remoteController.undo();TVReceivertvReceiver=newTVReceiver();TVOnCommandtvOnCommand=newTVOnCommand(tvReceiver);TVOffCommandtvOffCommand=newTVOffCommand(tvReceiver);// 按键1是灯开关remoteController.setOnCommand(TV_NO,tvOnCommand,tvOffCommand);System.out.println("=== 按下开机键位 ===");remoteController.on(TV_NO);System.out.println("=== 按下关机键位 ===");remoteController.off(TV_NO);System.out.println("=== 按下撤销键 ===");remoteController.undo();}}

输出:

=== 按下开灯键位 === 开灯了 === 按下关灯键位 === 关灯了 === 按下撤销键 === 开灯了 === 按下开机键位 === 电视开了 === 按下关机键位 === 电视关了 === 按下撤销键 === 电视开了

4. 优缺点

4.1 优点

高内聚低耦合

  • 调用者与接收者解耦:调用者无需知道接收者的具体实现
  • 命令对象内聚性高:每个命令专注于一个具体操作

复用性与可扩展性

  • 易于扩展新命令:只需实现Command接口
  • 命令可复用:同一命令可在不同上下文中使用

维护性

  • 易于维护和修改:修改具体操作只需修改对应命令类
  • 支持宏命令:可将多个命令组合成复杂操作

稳定性与可靠性

  • 支持事务:可批量执行命令并支持回滚
  • 支持撤销/重做:通过命令历史记录实现

4.2 缺点

复杂性增加

  • 类数量增多:每个命令都需要一个具体类
  • 系统复杂度提高:增加了间接层次

性能开销

  • 内存占用:每个命令都需要创建对象
  • 执行效率:间接调用可能比直接调用稍慢

5. 源码分析

Java标准库中Runnable相关实现是简化的命令模式
java.lang.Runnable接口

// Runnable就是命令接口publicinterfaceRunnable{publicabstractvoidrun();// execute()方法}// Thread作为InvokerThreadthread=newThread(()->System.out.println("Running command"));thread.start();// 触发命令执行

角色分析:

  1. Command(命令接口):Runnable接口,它定义了run()方法,相当于命令模式中的执行方法。
  2. ConcreteCommand(具体命令):实现了Runnable接口的类,例如我们通过匿名内部类、Lambda表达式或者具体类实现的run方法中的具体逻辑。
  3. Receiver(接收者):可以是Ru实际执行操作的对象。通常Runnable的实现会调用其他对象(接收者)的方法。
  4. Invoker(调用者/请求者):调用命令的对象。在Java中,Thread类就是一个典型的调用者,它接收一个Runnable(命令)并在适当的时机调用其run方法。

参考:

  • 韩顺平 Java设计模式
  • 张维鹏 Java设计模式之行为型:命令模式
  • java_my_life《JAVA与模式》之命令模式
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/15 7:35:59

收藏!35+程序员转行大模型全攻略:从入门到求职落地,少走90%弯路

在技术迭代日新月异的当下&#xff0c;大模型领域凭借其广阔的应用场景、持续攀升的市场需求以及极具吸引力的薪资待遇&#xff0c;已然成为程序员群体转型的热门优选赛道。对于35岁以上的程序员而言&#xff0c;尽管面临着精力分配失衡、技术代际断层、职场竞争加剧等现实挑战…

作者头像 李华
网站建设 2026/3/17 19:27:30

收藏!大模型压缩核心技术全解析(含组合优化流程)

本文将系统拆解大模型压缩的三大核心技术&#xff0c;详细说明剪枝&#xff08;移除冗余连接&#xff09;、量化&#xff08;降低数值精度&#xff09;与知识蒸馏&#xff08;教师-学生模式&#xff09;的原理、分类及实操要点&#xff0c;并给出经实践验证的“知识蒸馏→剪枝→…

作者头像 李华
网站建设 2026/3/17 19:27:28

告别 PPT 熬夜魔咒!宏智树 AI 一键生成学术职场双高分演示文稿

还在为开题报告 PPT 逻辑混乱发愁&#xff1f;还在为论文答辩 PPT 数据排版抓狂&#xff1f;还在为工作汇报 PPT 颜值不够焦虑&#xff1f;作为深耕论文写作科普的教育博主&#xff0c;我实测多款工具后发现&#xff0c;宏智树 AI 科研工具的 AI PPT 功能&#xff0c;堪称学术人…

作者头像 李华
网站建设 2026/3/17 12:13:55

高效管理学术文献的关键在于利用AI优化论文引用标注方式

论文引用标注排名&#xff1a;AI工具如何帮你高效管理文献 工具名称 核心功能 效率评分 适用场景 AiBiYe 智能引用格式转换 ★★★★☆ 多格式引用需求 AiCheck 引用查重与合规检测 ★★★★ 避免学术不端 AskPaper 文献内容智能提取 ★★★★ 快速理解文献核心…

作者头像 李华
网站建设 2026/3/17 19:27:24

ce认证需要注意哪些内容?

CE 认证的核心是精准匹配指令、规范技术文件、合规测试、正确使用标志与持续合规&#xff0c;高风险产品需公告机构介入&#xff0c;非欧盟企业必须指定欧盟授权代表&#xff0c;技术文件需留存 10 年备查。以下从全流程拆解关键注意事项与风险防控要点&#xff1a;一、前期判定…

作者头像 李华
网站建设 2026/3/14 16:03:04

五寨甜糯玉米打造乡村振兴金色名片

​作为 “中国甜糯玉米之乡”&#xff0c;山西五寨凭借北纬 38 黄金种植带的先天优势与全产业链升级&#xff0c;正通过 “央视 高铁 社区” 三维宣传矩阵&#xff0c;让这颗承载晋西北风土的 “黄金玉米” 飘香全国、走向世界&#xff0c;全力铸就乡村振兴背景下的农产品金色…

作者头像 李华