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

二十三种设计模式(十四)--命令模式

命令模式(Command)

当我们有一个功能完善的类VideoClass,能够实现视频转码, 视频缓存 等等实际功能.
此时调用者需要依赖用户输入的命令来执行VideoClass中的一个或几个方法函数.
直觉上, 我们会写一个switch-case语句来处理用户输入的命令, 并执行VideoClass中的对应方法, 简单的可以这么做, 但是当用户的命令批量输入, 且要我们记录用户输入的所有命令, 或者要将所有命令都队列化存储之后依次执行时, 我们需要将命令解耦出来, 封装成独立的类, 这就是命令模式.

假如我们有如下问题:

publicclassCommandPattern{publicstaticvoidmain(String[]args){Stringcommand=args[1];// 用户输入的指令Robotrobot=newRobot();Weaponweapon=newWeapon();// TODO:: 要实现用户批量输入的命令: 变身车辆-发射5次粒子炮-变身人类-发射3发子弹// TODO:: 延迟3分钟发射粒子炮switch(command){case"car":robot.transToCar();// TODO:: 记录日志: 变身车辆// TODO:: 记录变身耗时// 其他操作...case"human":robot.transToHuman();// TODO:: 记录日志: 变身人类// TODO:: 记录变身耗时// 其他操作...case"bullet":weapon.fireBullet();// TODO:: 记录日志: 开火发射子弹// TODO:: 记录发射子弹数量// 其他操作...case"particle":weapon.fireParticleCannon();// TODO:: 记录日志: 开火发射粒子炮// TODO:: 记录发射粒子炮次数// 其他操作...}}}// 两个真正执行指令对应的功能的类classRobot{publicvoidtransToCar(){System.out.println("[robot] 变身一辆车");}publicvoidtransToHuman(){System.out.println("[robot] 变身T800");}}classWeapon{publicvoidfireBullet(){System.out.println("[武器] 发射子弹");}publicvoidfireParticleCannon(){System.out.println("[武器] 发射粒子炮");}}

代码中标注TODO的部分, 随着需求的扩展, 会变得越来越臃肿

以下是针对以上问题, 命令模式的实现

定义命令的统一接口, 所有的命令类都依据接口实现功能

// 命令模式的核心, 定义通用的命令接口interfaceCommand{// 执行命令voidexecute();// 获取命令名称(用于日志)StringgetCommandName();}// 命令一:变身车辆(绑定Robot接收者)classTransToCarCommandimplementsCommand{privatefinalRobotrobot;// 附加:记录耗时、日志privatelongstartTime;privatelongendTime;publicTransToCarCommand(Robotrobot){this.robot=robot;}@Overridepublicvoidexecute(){startTime=System.currentTimeMillis();// 执行接收者的核心逻辑robot.transToCar();endTime=System.currentTimeMillis();// 附加逻辑:记录日志、耗时log();}@OverridepublicStringgetCommandName(){return"变身车辆";}privatevoidlog(){System.out.printf("[日志] 命令:%s,执行时间:%s,耗时:%dms%n",getCommandName(),newDate(),endTime-startTime);}}// 命令二:发射子弹(支持参数:发射次数)classFireBulletCommandimplementsCommand{privatefinalWeaponweapon;privatefinalinttimes;// 发射次数(参数封装)privatelongstartTime;privatelongendTime;publicFireBulletCommand(Weaponweapon,inttimes){this.weapon=weapon;this.times=times;}@Overridepublicvoidexecute(){startTime=System.currentTimeMillis();// 执行带参数的逻辑for(inti=0;i<times;i++){weapon.fireBullet();}endTime=System.currentTimeMillis();log();}@OverridepublicStringgetCommandName(){return"发射子弹";}privatevoidlog(){System.out.printf("[日志] 命令:%s,次数:%d,执行时间:%s,耗时:%dms%n",getCommandName(),times,newDate(),endTime-startTime);}}// 命令三 ...// 命令四 ...// 命令五 ...// 命令随着功能类的更新可以无限扩展

由于单独命令都进行了封装, 因此可以将命令队列化批量操作, 可以将命令记录日志
可以支持更灵活的操作
于是, 下面这个类实现将命令存储进队列后依次执行的功能

// 请求者:命令调用器(管理命令队列,批量执行)classCommandInvoker{// 命令队列(存储所有命令,支持批量执行)privatefinalList<Command>commandQueue=newArrayList<>();// 添加命令到队列publicvoidaddCommand(Commandcommand){commandQueue.add(command);}// 执行队列中的所有命令publicvoidexecuteAll(){System.out.println("\n===== 开始执行命令队列 =====");for(Commandcommand:commandQueue){command.execute();}System.out.println("===== 命令队列执行完成 =====\n");}// 清空队列publicvoidclearQueue(){commandQueue.clear();}}

真正执行指令功能的类不修改

importjava.util.Date;importjava.util.ArrayList;importjava.util.List;publicclassCommandPattern{publicstaticvoidmain(String[]args){// 1. 创建接收者(真正干活的对象)Robotrobot=newRobot();Weaponweapon=newWeapon();// 2. 创建请求者(命令调用器,管理队列)CommandInvokerinvoker=newCommandInvoker();// 3. 客户端组装命令(批量命令:变身车辆-延迟3分钟发射5次粒子炮-变身人类-发射3发子弹)invoker.addCommand(newTransToCarCommand(robot));// 变身车辆invoker.addCommand(newFireBulletCommand(weapon,3));// 发射3发子弹// 4. 执行所有命令(请求者负责执行,客户端无需关心细节)invoker.executeAll();}}// 两个指令接收者, 真正执行指令对应的功能classRobot{publicvoidtransToCar(){System.out.println("[robot] 变身一辆车");}publicvoidtransToHuman(){System.out.println("[robot] 变身T800");}}classWeapon{publicvoidfireBullet(){System.out.println("[武器] 发射子弹");}publicvoidfireParticleCannon(){System.out.println("[武器] 发射粒子炮");}}

执行结果:

===== 开始执行命令队列 ===== [robot] 变身一辆车 [日志] 命令:变身车辆,执行时间:Thu Dec 18 14:21:38 CST 2025,耗时:0ms [武器] 发射子弹 [武器] 发射子弹 [武器] 发射子弹 [日志] 命令:发射子弹,次数:3,执行时间:Thu Dec 18 14:21:38 CST 2025,耗时:0ms ===== 命令队列执行完成 =====

命令模式只解决一件事:

把“要做什么”封装成对象

其他能力(队列 / 日志 / 撤销)都是围绕这个对象自然搭建出来的
当看到代码里出现下面的逻辑:

if (cmd == A) doA(); if (cmd == B) doB();

👉 90% 情况:该考虑命令模式了

http://www.rkmt.cn/news/122859.html

相关文章:

  • 小程序毕设选题推荐:基于springboot微信小程序的校园食堂订餐服务系统基于springboot+微信小程序的大学生餐厅点餐系统小程序【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 知识城舞蹈哪家好:官方排名与深度解析 - 品牌测评家
  • Vue.js前端框架技术课程总结知识点
  • 【毕业设计】基于springboot+微信小程序的羽球快讯爱好者平台小程序(源码+文档+远程调试,全bao定制等)
  • 26、SVG 样式设计全解析
  • Cursor 快捷键全集:提升效率的隐藏秘笈
  • Mathcad的野路子】11kW PFC参数计算书实战拆解
  • 【一招根治】彻底退出Windows 10/11微软账户
  • 20、WinJS 应用样式与控件风格全解析
  • 基于LSTM - AdaBoost的多输入单输出回归预测
  • Nmap深度解析:信息收集
  • AIGC时代的平台工程:别再让你的应用被云厂商“焊死”
  • 基于混合模型磁链观测器实现异步电机感应电机矢量控制及仿真验证
  • 报告 | 《RAG实践手册 构建知识库和问答系统的实战指南》(免费下载PDF版本)
  • 小程序毕设选题推荐:基于微信小程序的智能学习小程序【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 当材料利用率和切割成本开始较劲:两阶段遗传算法如何玩转多约束排样
  • 测试流程的标准化与灵活性:在结构与适应之间寻找最优解
  • 2025.12.18代码分析
  • 22、WinJS 控件样式详解
  • 收藏备用!程序员入门大模型:从0到1的学习全攻略
  • 探索安川七伺服电机方案:从原理到代码实现
  • AutoCAD 2025安装包免费下载和安装教程(附破解版安装包)
  • 【Azure App Service】分享Python代码获取App Service Certificates (证书信息)
  • 文生中英双语的AI视频工具怎么选?一个英语老师的实测结论
  • MinIO再见!RustFS性能飙升5倍,我们团队全面迁移的实战全记录
  • 高精度时钟测试仪覆盖多行业的时间同步测试利器 gps时钟测试仪
  • Python构建AI Agent自主智能体系统
  • 2025.12.18
  • Springboot+Easyexcel将数据写入模板文件并导出Excel
  • 30年源头厂家!郑州新广发河南防火卷帘门,8条生产线月产8000扇直供 - 朴素的承诺