定州市网站建设_网站建设公司_展示型网站_seo优化
2025/12/18 20:12:34 网站建设 项目流程

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询