news 2026/9/9 11:04:41

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
二十三种设计模式(十四)--命令模式

命令模式(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% 情况:该考虑命令模式了

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 6:16:31

27、Windows 8 应用开发中的 SVG 样式设计

Windows 8 应用开发中的 SVG 样式设计 1. SVG 简介 SVG(Scalable Vector Graphics)即可缩放矢量图形,具有诸多优势,如可嵌入 HTML 中,能通过 CSS 属性对其进行样式设计。SVG 的 CSS 属性包括填充(fill)、描边(strokes)、标记(markers)、滤镜(filters)、遮罩(ma…

作者头像 李华
网站建设 2026/9/8 16:17:56

SAP ABAP拆分交货单数量、批次、存储地点 并过账

案例&#xff1a;将交货单数量拆分到不同批次&#xff0c;一个批次下数量为1&#xff0c;并进行过账。 发布时间&#xff1a;20251218主要逻辑分为两个部分&#xff1a; 拆分和过账&#xff0c;拆分批次时必须写入存储位置&#xff0c;否则过账可能会报错&#xff08;eg&#x…

作者头像 李华
网站建设 2026/9/8 19:19:01

基于MPC的智能车运动预测和控制算法 Motion predication; Kinemati...

基于MPC的智能车运动预测和控制算法 Motion predication; Kinematic model //. MATLAB coding //. 加入求解步骤进而得到自定义成本函数的可扩展MPC控制器; //. 模型状态空间方程线性化和离散化; //. 可与风险场/人工势场/决策/轨迹跟踪等算法集成; //. 需要用到车辆运动学模…

作者头像 李华
网站建设 2026/9/8 22:39:20

Mathcad的野路子】11kW PFC参数计算书实战拆解

大厂的PFC程序参数变量计算书 11kwPFC mathcad打开某大厂PFC控制程序的计算书&#xff0c;满屏Mathcad公式看得人头皮发麻。今天就带各位手撕这份11kW功率因数校正的参数计算说明书&#xff0c;咱们用最糙的实操逻辑来理解这些看似高冷的公式。先看输入条件&#xff1a;400V三相…

作者头像 李华
网站建设 2026/9/9 3:26:54

STM32学习笔记CAN

1.CAN基础知识1.CAN的介绍高速can&#xff0c;低速can的拓扑图(终端电阻&#xff0c;用于阻抗匹配&#xff0c;减少回波反射)2.CAN的物理层显性电平具有优先权&#xff08;逻辑0&#xff09;。3.Can总线制裁2.stm32的can控制器CAN控制器的工作模式有三种&#xff1a;初始化&…

作者头像 李华
网站建设 2026/9/9 3:07:43

搭建你的第一个“私有知识库” (RAG)

最近 AI 圈子里最火的技术莫过于 RAG (检索增强生成)。简单说&#xff0c;就是给大模型“外挂”一个你的私人资料库&#xff0c;让它能回答你公司内部文档、个人笔记里的问题。 市面上很多 RAG 工具都要收费&#xff0c;或者部署起来像修核电站一样复杂。 今天&#xff0c;我给…

作者头像 李华