news 2026/8/13 13:06:40

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

作者头像

张小明

前端开发工程师

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/8/12 13:12:22

【前端知识点总结】请求/响应拦截器的介绍

在现代前端应用中&#xff0c;与后端服务的 HTTP 通信是项目的命脉。我们频繁地发起请求、处理响应。但如果每个请求都需要手动处理通用逻辑&#xff08;如添加 token、错误处理&#xff09;&#xff0c;代码将变得冗余、难以维护。这时&#xff0c;拦截器便应运而生&#xff0…

作者头像 李华
网站建设 2026/8/12 20:20:42

零基础使用网络安全工具的方法

第❶步&#xff1a;工具认知&#xff08;第1个月&#xff09;- 别被工具吓倒&#xff0c;先当“普通软件”用核心心态&#xff1a;忘掉“黑客工具”的标签&#xff0c;把它们看作帮你完成特定任务的“瑞士军刀”。必装三件套&#xff08;虚拟机环境内操作&#xff09;&#xff…

作者头像 李华
网站建设 2026/8/12 12:53:44

校园人体工学深度解析:固定高度课桌椅如何成为学生“隐形推手”

引言在现代化校园建设中&#xff0c;标准化的管理模式往往被视为高效与秩序的象征。为了追求视觉上的整齐划一&#xff0c;许多学校在教室家具配置上采取了“一刀切”的策略&#xff1a;无论班级里的学生身高是1.2米还是1.6米&#xff0c;配备的课桌椅高度往往是固定的。这种为…

作者头像 李华
网站建设 2026/8/12 11:43:38

Vu3 打包问题

Vu3 打包问题 npm run build 时出现原因 TS 验证比较严格 解决方案 &#xff1a;在tsconfig.app。json中添加 “exclude”: [“node_modules/unplugin-element-plus/dist/vite.d.ts”]彻底解决 在package.json 中添加 “type-check”: “echo “Skipping type check””, c…

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

用 Swap 技巧彻底释放 Vector 内存

C 性能优化笔记&#xff1a;为什么 clear() 还不够&#xff1f;教你用 Swap 技巧彻底释放 Vector 内存 在阅读 DataNode.cpp 源码时&#xff0c;我发现了一个非常经典且优雅的 C 惯用写法&#xff08;Idiom&#xff09;。在 RemoveAll 函数中&#xff0c;作者并没有直接调用我…

作者头像 李华