当前位置: 首页 > news >正文

设计模式学习(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与模式》之命令模式
http://www.jsqmd.com/news/273666/

相关文章:

  • P4411 [BJWC2010] 取数游戏 题解
  • 2026年市场口碑好的保温装饰一体化板制造厂家电话,一体板/保温装饰一体化板,保温装饰一体化板直销厂家联系电话 - 品牌推荐师
  • 数据泄露:网络安全领域的新热点
  • 2026年市场可靠的一体板制造厂家哪家强,聚氨酯保温装饰一体板/仿石漆保温装饰一体板,一体板直销厂家有哪些 - 品牌推荐师
  • Agentic RAG核心解析(必收藏):从原理到架构,搞定复杂场景检索
  • 迷宫游戏的设计与实现
  • 血液离心机怎么选?7大头部品牌全解析与采购避坑指南 - 品牌推荐大师1
  • 印刷糊箱联动线选购指南:2026年哪些厂商值得信赖?行业内印刷糊箱联动线厂家赋能企业生产效率提升与成本优化 - 品牌推荐师
  • 《P2520 [HAOI2011] 向量》
  • Node.js 用hashring轻松做负载均衡
  • 博四预答辩结束
  • 【Python视觉】文字怎么“贴”在瓶子上?揭秘 AI 如何利用“网格变形”实现曲面包装的完美汉化
  • DarkHole
  • 【技术硬核】没有 PSD 源文件怎么办?揭秘 AI 如何将 JPG 图片“逆向分层”实现无损翻译
  • 哈尔滨市英语雅思培训辅导机构推荐,2026权威出国雅思课程中心学校口碑排行榜 - 苏木2025
  • Spring Boot 实现网络限速,一个注解搞定!
  • libero ProASIC3 A3P250 JTAG-DirectC 源码分析三 dp_program_from
  • 2026年市场评价高的保温装饰一体化板公司怎么选,石墨聚苯板保温装饰一体板,保温装饰一体化板生产商如何选 - 品牌推荐师
  • 2026年国内专业的一体板订制厂家如何选,聚氨酯保温装饰一体板/一体板/石墨聚苯板保温装饰一体板,一体板品牌电话 - 品牌推荐师
  • 2025年行业内诚信的一对一家教老师怎么选择,科学家教/师范家教/一对一家教/语文家教/家救,一对一家教老师推荐排行榜 - 品牌推荐师
  • 2026年流量计市场新动态:实力厂家高压流量计精选,插入式超声波流量计/管道式电磁流量计,流量计制造企业哪家好 - 品牌推荐师
  • 全栈小能手的烦恼:键盘敲累了还要开会
  • 《把脉行业与技术趋势》-68-行业周期律以及背后的底层逻辑
  • 2026年纯铝锭厂家选购推荐/铝板,铝锭,铝箔,高温铝箔,包装用铝箔 - 品牌策略师
  • 可控生成策略在大语言模型摘要生成中的应用
  • 2026年高口碑蒸汽发生器品牌TOP榜:全预混节能先锋、电蒸汽高效代表、燃气蒸汽发生器实力厂商全解析! - 品牌推荐大师1
  • AI自动化智能体与工作流平台直播课
  • rector-rules - 提供标准化的常量、变量、函数、类、属性和方法命名以及其他 Rector 规则
  • 哈尔滨市英语雅思培训辅导机构推荐、2026权威出国雅思课程中心学校口碑排行榜 - 苏木2025
  • 避开Context