news 2026/4/23 13:13:21

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

作者头像

张小明

前端开发工程师

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/4/23 13:01:30

华为HCIA笔记——第十九天

SNMP、SDN及NFV 本章内容主要讲述了网络管理协议SNMP、SDN、NFV技术的概述和基本原理 SNMP、SDN及NFV 一、SNMP协议 1.1 SNMP协议概述 1.2 SNMP基本原理 SNMP管理模型 SNMPv1 SNMPv2c SNMPv3 二、SDN概述 2.1 SDN起缘 2.2 OpenFlow基本概念 Flow Table简介 转发方式比较 SDN网…

作者头像 李华
网站建设 2026/4/16 10:44:14

2025.12.18代码分析

1.1题目1.2代码#include<stdio.h> long long ways(int n){ long long d[n1]; d[0]1; d[1]1; for(int i2;i<n;i){d[i]d[i-1]d[i-2]; } return d[n]; }int main() {int t,i;scanf("%d",&t);int a[t],b[t];for(i0;i<t;i){scanf("%d %d",&…

作者头像 李华
网站建设 2026/4/18 14:31:18

Jmeter 命令启动-动态参数化

Jmeter命令行参数 1、在Linux中&#xff0c;使用非GUI的方式执行Jmeter。若需更改参数&#xff0c;必须先编辑jmx文件&#xff0c;找到对应变量进行修改&#xff0c;比较麻烦。 因此&#xff0c;可以参数化一些常用的变量&#xff0c;直接在Jmeter命令行进行设置 2、参数 -J…

作者头像 李华
网站建设 2026/4/21 7:53:52

从基础到多模态:Llama-Index RAG 七大企业级落地场景实战

七大场景 企业级RAG检索实战&#xff08;附源码&#xff09; RAG 落地生产环境&#xff0c;如何解决 “检索准确性”&#xff08;事实一致性/语境完整性/领域术语召回&#xff09;和 “多模态解析”&#xff08;PDF 图表、图片甚至视频&#xff09;两大难题&#xff1f; 本文通…

作者头像 李华
网站建设 2026/4/18 23:31:16

光伏并网系统的仿真就像搭积木,每个模块看似独立却又环环相扣。今天咱们直接上手拆解这个光伏三相并网Simulink模型,顺便聊聊那些藏在模块背后的“骚操作

光伏三相并网Simulink仿真模型&#xff08;光伏并网仿真模型&#xff09; 电路包括五个主要模块&#xff1a;PV光伏阵列Boost DC/DC 变换器三相逆变器L型滤波器交流电网&#xff1b; 控制系统包括&#xff1a;MPPT控制系统交流电网侧逆变器控制系统&#xff1b; 基本工作过程 1…

作者头像 李华
网站建设 2026/4/18 10:00:19

Hyperledger Fabric与 FISCO BCOS深度对比

一、核心架构与设计哲学对比 Hyperledger Fabric&#xff08;Linux基金会主导&#xff09; 设计理念&#xff1a;模块化、可插拔的企业级区块链 架构特点&#xff1a; ├── 通道机制&#xff08;数据隔离&#xff09; ├── 可插拔共识&#xff08;Kafka/Raft&#xff09; ├…

作者头像 李华