探讨命令模式及其应用
目录
- 命令模式
- 命令模式结构
- 命令模式适用场景
- 命令模式优缺点
- 练手题目
- 题目描述
- 输入描述
- 输出描述
- 题解
命令模式
命令模式是一种行为设计模式, 它可将请求转换为一个包含与请求相关的所有信息的独立对象。 该转换让你能根据不同的请求将方法参数化、 延迟请求执行或将其放入队列中, 且能实现可撤销操作。
命令模式结构

- 发送者(Sender)——亦称 “触发者(Invoker)”——类负责对请求进行初始化,其中必须包含一个成员变量来存储对于命令对象的引用。发送者触发命令,而不向接收者直接发送请求。注意,发送者并不负责创建命令对象:它通常会通过构造函数从客户端处获得预先生成的命令。
- 命令(Command)接口通常仅声明一个执行命令的方法。
- 具体命令 (Concrete Commands)会实现各种类型的请求。具体命令自身并不完成工作,而是会将调用委派给一个业务逻辑对象。但为了简化代码,这些类可以进行合并。接收对象执行方法所需的参数可以声明为具体命令的成员变量。你可以将命令对象设为不可变,仅允许通过构造函数对这些成员变量进行初始化。
- 接收者(Receiver)类包含部分业务逻辑。几乎任何对象都可以作为接收者。绝大部分命令只处理如何将请求传递到接收者的细节,接收者自己会完成实际的工作。
- 客户端(Client)会创建并配置具体命令对象。客户端必须将包括接收者实体在内的所有请求参数传递给命令的构造函数。此后,生成的命令就可以与一个或多个发送者相关联了。
命令模式通用代码:
//抽象接收者
public abstract class Receiver{public abstract void operation();
}//具体接收者
public class Recevier1 extends Recevier{public void operation(){...}
}//通用命令接口
public interface Command{void execute();
}//具体命令类
public class ConcreteCommand1 implements Command{private Receiver receiver;public ConcreteCommand1(Receiver _receiver){this.receiver = _receiver;}public void execute(){this.receiver.operation();}
}//调用者类
public class Invoker{private Command command;public void setCommand(Command _command){this.command = _command;}public void executeCommand(){this.command.execute();}}//主程序类
public class Client{public static void main(String[] args){//调用者Invoker invoker = new Invoker();//接收者Receiver receiver1 = new Receiver1();//定义一个命令Command command = new ConcreteCommand1(receiver1);invoker.setCommand(command);invoker.executeCommand();}
}
命令模式适用场景
-
如果你需要通过操作来参数化对象,可使用命令模式。
命令模式可将特定的方法调用转化为独立对象。 这一改变也带来了许多有趣的应用: 你可以将命令作为方法的参数进行传递、 将命令保存在其他对象中, 或者在运行时切换已连接的命令等。
-
如果你想要将操作放入队列中、操作的执行或者远程执行操作,可使用命令模式。
同其他对象一样,命令也可以实现序列化(序列化的意思是转化为字符串),从而能方便地写入文件或数据库中。一段时间后,该字符串可被恢复成为最初的命令对象。因此,你可以延迟或计划命令的执行。但其功能远不止如此!使用同样的方式,你还可以将命令放入队列、记录命令或者通过网络发送命令。
-
如果你想要实现操作回滚功能,可使用命令模式。

**识别方法:**命令模式可以通过抽象或接口类型(发送者)中的行为方法来识别, 该类型调用另一个不同的抽象或接口类型 (接收者)实现中的方法,该实现则是在创建时由命令模式的实现封装。命令类通常仅限于一些特殊行为。
命令模式优缺点
命令模式优点:
- 单一职责原则。你可以解耦触发和执行操作的类。
- 开闭原则。你可以在不修改已有客户端代码的情况下在程序中创建新的命令。
- 你可以实现撤销和恢复功能。
- 你可以实现操作的延迟执行。
- 你可以将一组简单命令组合成一个复杂命令。
命令模式缺点:
- 代码可能会变得更加复杂,因为你在发送者和接收者之间增加了一个全新的层次。
练手题目
题目描述
小明去奶茶店买奶茶,他可以通过在自助点餐机上来点不同的饮品,请你使用命令模式设计一个程序,模拟这个自助点餐系统的功能。
输入描述
第一行是一个整数 n(1 ≤ n ≤ 100),表示点单的数量。
接下来的 n 行,每行包含一个字符串,表示点餐的饮品名称。
输出描述
输出执行完所有点单后的制作情况,每行输出一种饮品的制作情况。如果制作完成,输出 “XXX is ready!”,其中 XXX 表示饮品名称。

题解
解法一:
import java.util.Scanner;// 枚举类表示饮料类型
enum BeverageType {MILKTEA, COFFEE, COLA
}// 抽象饮料类
abstract class Beverage {abstract void make();
}// 具体饮料类
class MilkTea extends Beverage {@Overridepublic void make() {System.out.println("MilkTea is ready!");}
}class Coffee extends Beverage {@Overridepublic void make() {System.out.println("Coffee is ready!");}
}class Cola extends Beverage {@Overridepublic void make() {System.out.println("Cola is ready!");}
}// 抽象命令类
abstract class Command {protected Beverage beverage;public Command(Beverage _beverage) {this.beverage = _beverage;}abstract void execute();
}// 具体命令类
class MilkTeaCommand extends Command {public MilkTeaCommand() {super(new MilkTea());}@Overridepublic void execute() {beverage.make();}
}class CoffeeCommand extends Command {public CoffeeCommand() {super(new Coffee());}@Overridepublic void execute() {beverage.make();}
}class ColaCommand extends Command {public ColaCommand() {super(new Cola());}@Overridepublic void execute() {beverage.make();}
}// 调用者类
class Invoker {private Command command;public void setCommand(Command _command) {command = _command;}public void action() {command.execute();}
}// 主类
public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);try {Invoker invoker = new Invoker();int n = scanner.nextInt();scanner.nextLine();for (int i = 0; i < n; i++) {String type = scanner.nextLine().trim().toUpperCase();try {switch (BeverageType.valueOf(type)) {case MILKTEA:invoker.setCommand(new MilkTeaCommand());break;case COFFEE:invoker.setCommand(new CoffeeCommand());break;case COLA:invoker.setCommand(new ColaCommand());break;default:System.out.println("请重新输入:");continue;}invoker.action();} catch (IllegalArgumentException e) {System.out.println("无效的饮料类型");}}} catch (Exception e) {System.out.println("发生错误: " + e.getMessage());} finally {scanner.close();}}
}
解法二:
import java.util.Scanner;// 命令接口
interface Command {void execute();
}// 具体命令类 - 点餐命令
class OrderCommand implements Command {private String drinkName;private DrinkMaker receiver;public OrderCommand(String drinkName, DrinkMaker receiver) {this.drinkName = drinkName;this.receiver = receiver;}@Overridepublic void execute() {receiver.makeDrink(drinkName);}
}// 接收者类 - 制作饮品
class DrinkMaker {public void makeDrink(String drinkName) {System.out.println(drinkName + " is ready!");}
}// 调用者类 - 点餐机
class OrderMachine {private Command command;public void setCommand(Command command) {this.command = command;}public void executeOrder() {command.execute();}
}public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);// 创建接收者和命令对象DrinkMaker drinkMaker = new DrinkMaker();// 读取命令数量int n = scanner.nextInt();scanner.nextLine();while (n-- > 0) {// 读取命令String drinkName = scanner.next();// 创建命令对象Command command = new OrderCommand(drinkName, drinkMaker);// 执行命令OrderMachine orderMachine = new OrderMachine();orderMachine.setCommand(command);orderMachine.executeOrder();}scanner.close();}
}
解法三:命令模式+工厂模式
import java.util.Scanner;// 命令接口
interface Command {void execute();
}// 具体命令类 - 点餐命令
class OrderCommand implements Command {private String drinkName;private DrinkMaker receiver;public OrderCommand(String drinkName, DrinkMaker receiver) {this.drinkName = drinkName;this.receiver = receiver;}@Overridepublic void execute() {receiver.makeDrink(drinkName);}
}// 接收者类 - 制作饮品
class DrinkMaker {public void makeDrink(String drinkName) {System.out.println(drinkName + " is ready!");}
}// 调用者类 - 点餐机
class OrderMachine {private Command command;public void setCommand(Command command) {this.command = command;}public void executeOrder() {if (command != null) {command.execute();} else {System.out.println("未设置命令.");}}
}// 命令工厂类
class CommandFactory {private DrinkMaker drinkMaker;public CommandFactory(DrinkMaker drinkMaker) {this.drinkMaker = drinkMaker;}public Command createCommand(String drinkName) {return new OrderCommand(drinkName, drinkMaker);}
}// 主类
public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);// 创建接收者和工厂对象DrinkMaker drinkMaker = new DrinkMaker();CommandFactory commandFactory = new CommandFactory(drinkMaker);OrderMachine orderMachine = new OrderMachine();// 读取命令数量int n = scanner.nextInt();scanner.nextLine();while (n-- > 0) {// 读取命令String drinkName = scanner.nextLine().trim();if (drinkName.isEmpty()) {System.out.println("无效输入,请输入饮品名.");continue;}// 使用工厂创建命令对象Command command = commandFactory.createCommand(drinkName);// 设置命令并执行orderMachine.setCommand(command);orderMachine.executeOrder();}scanner.close();}
}
相关文章:
探讨命令模式及其应用
目录 命令模式命令模式结构命令模式适用场景命令模式优缺点练手题目题目描述输入描述输出描述题解 命令模式 命令模式是一种行为设计模式, 它可将请求转换为一个包含与请求相关的所有信息的独立对象。 该转换让你能根据不同的请求将方法参数化、 延迟请求执行或将其…...
1、音视频解封装流程---解复用
对于一个视频文件(mp4格式/flv格式),audio_pkt或者video_pkt是其最基本的数据单元,即视频文件是由独立的视频编码包或者音频编码包组成的。 解复用就是从视频文件中把视频包/音频包单独读取出来保存成独立文件,那么如何得知packet是视频包还是…...
centos7升级gcc到7.3.0
1、下载gcc-7.3.0源码 wget ftp.gnu.org/gnu/gcc/gcc-7.3.0/gcc-7.3.0.tar.gz 2、解压gcc-7.3.0 tar -xvf gcc-7.3.0.tar.gz3、安装依赖 cd gcc-7.3.0 ./contrib/download_prerequisites ./contrib/download_prerequisites会下载对应的依赖包,如果下载不了的话&a…...
系统运维面试题总结(网络基础类)
系统运维面试题总结(网络基础类) 网络基础类第七层:应用层第六层:表示层第五层:会话层第四层:传输层第三层:网络层第二层:数据链路层第一层:物理层 类似面试题1、TCP/IP四…...
PO模式登录测试
项目实践 登陆项目测试 get_driver import page from selenium import webdriverclass GetDriver:driver Noneclassmethoddef get_driver(cls):if cls.driver is None:cls.driver webdriver.Edge()cls.driver.maximize_window()cls.driver.get(page.url)return cls.drivercl…...
X86 +PC104+支持WinCE5.0,WinCE6.0,DOS,WinXP, QNX等操作系统,工业控制数据采集核心模块板卡定制
CPU 模块 是一款基于RDC 3306的SOM Express模块。RDC 3306这款X86架构的CPU是一款性能高、稳定性强的处理器。 它是一款灵活精巧的主板(尺寸为91.8mm68.6mm),可以灵活的运用于用户的底板,节约开发成本。模块的接插件使用插针形式…...
视频监控汇聚和融合平台的特点、功能、接入方式、应用场景
目录 一、产品概述 二、主要特点 1、多协议支持 2、高度集成与兼容性 3、高性能与可扩展性 4、智能化分析 5、安全可靠 三、功能概述 1. 视频接入与汇聚 2. 视频存储与回放 3. 实时监控与预警 4. 信息共享与联动 5. 远程管理与控制 四、接入方式 1、直接接入 2…...
实习总结 --- 其他业务
一. 回归测试:回归测试与测新是对应的,当需求准入交付测试的时候首先要进行的就是测新,也就是对新功能对测试,一般是在sim环境下测试的;当测新通过后才会进行回归测试,回归测试的目的是为了保证老功能的正确…...
2024年上半年典型网络攻击事件汇总
文章目录 前言一、Ivanti VPN 的0 Day攻击(2024年1月)二、微软公司高管账户泄露攻击(2024年1月)三、Change Healthcare网络攻击(2024年2月)四、ConnectWise ScreenConnect漏洞利用攻击(2024年2月)五、XZ Utils软件供应链攻击(2024年3月)六、AT&T数据泄露攻击(20…...
Ozon、美客多补单测评黑科技:打造无懈可击的自养号补单环境
不管哪个跨境平台的风控都会做升级,相对的补单技术也需要进行相应的做升级,风控升级后,自己养号补单需要注意以下技术问题,以确保补单的稳定性和安全性: 一、物理环境 1. 硬件参数伪装:平台已经开始通过I…...
ES报错:解决too_many_clauses: maxClauseCount is set to 1024 报错问题
解决too_many_clauses: maxClauseCount is set to 1024 报错问题 问题场景报错信息问题分析解决1. 优化查询2. 增加maxClauseCount3. 改用其他查询类型修改后的查询示例 问题场景 查询语句:查询clcNo分类号包含分类O的所有文档 {"match_phrase_prefix":…...
完全指南:在Linux上安装和精通Conda
前言 Conda是一个强大的包管理和环境管理工具,特别适用于数据科学和机器学习项目。本文将详细指导你在Linux系统上安装、配置和充分利用Conda的方法。 步骤一:下载和安装Conda 下载安装包: wget https://repo.anaconda.com/miniconda/Minic…...
# linux 系统中,使用 “ ll “ 命令报错 “ bash ll command not found “ 解决方法:
linux 系统中,使用 " ll " 命令报错 " bash ll command not found " 解决方法: 一、错误描述: 报错原因: 1、这个错误表明你尝试在 bash shell 中执行 ll 命令,但是系统找不到这个命令。ll 通常…...
吴恩达深度学习笔记:机器学习策略(2)(ML Strategy (2)) 2.3-2.4
目录 第三门课 结构化机器学习项目(Structuring Machine Learning Projects)第二周:机器学习策略(2)(ML Strategy (2))2.3 快速搭建你的第一个系统,并进行迭代(Build your first system quickly…...
【软件测试】快速定位bug,编写测试用例
作为一名测试人员如果连常见的系统问题都不知道如何分析,频繁将前端人员问题指派给后端人员,后端人员问题指派给前端人员,那么在团队里你在开发中的地位显而易见 ,口碑、升值、加薪那应该是你遥不可及的梦 但是作为测试人员来说&…...
升级springboot3
坑爹的发版流水线,管天管地,springboot2过了维护期,就催着我们升级。 导致必须上jdk17 记录一下升级需要处理的事情 先升级springboot和cloud,这里定下基调,其他的才好跟着升级 https://spring.io/projects/spring-b…...
视频编解码从H.264到H.266:浅析GB28181安防视频汇聚EasyCVR视频压缩技术
随着信息技术的飞速发展,视频编解码技术也在不断革新,以适应高清、超高清甚至8K视频时代的到来。视频编解码技术作为数字多媒体领域的核心技术之一,也在不断地演进和革新。从早期的H.261到现在的H.265、H.266,每一次技术的升级都极…...
vue项目访问 域名/index.html 空页面问题
很大可能是vue前端没做404页面,在路由不匹配时会跳转到空路由页面。 也可以把所有路由不匹配的网址全部跳转到域名首页。防止出现404或者页面错误。 如果使用docker nginx部署项目,配置文件上会有 try_files $uri $uri/ /index.html; 这段配置会尝试…...
区块链开发入门:基础概念与实施技术详解
区块链开发入门:基础概念与实施技术详解 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 引言 随着区块链技术的快速发展,它已经不再局…...
Rust破界:前端革新与Vite重构的深度透视(下)
Rust破界:前端革新与Vite重构的深度透视(下) 前端开发者:拥抱 Rust 的策略与时机技能树的扩展 结语:跨界融合的未来展望Vite 重构的深远意义 附录:进一步探索 Rust 的资源指南 前端开发者:拥抱 …...
OpenClaw任务编排:用Qwen3.5-4B-Claude实现爬虫+分析闭环
OpenClaw任务编排:用Qwen3.5-4B-Claude实现爬虫分析闭环 1. 为什么需要自动化任务编排 去年我接手了一个市场调研项目,需要每周从20多个网站抓取产品价格数据,清洗后生成趋势图表。最初用Python脚本手动Excel处理,每次要花3小时…...
Anomalib Padim模型训练完整踩坑记录:从环境配置、自制数据集准备到ONNX导出一步到位
Anomalib Padim模型实战:工业缺陷检测从零到ONNX部署全指南 工业质检领域正经历一场从传统人工检测到智能算法驱动的变革。想象一下,当生产线上的金属部件以每分钟数十个的速度通过摄像头时,如何确保每个产品表面没有细微划痕、凹陷或腐蚀&am…...
基于ZLMediaKit API的Java流媒体服务实战:从配置到核心功能封装
1. ZLMediaKit快速入门与环境搭建 第一次接触ZLMediaKit时,我被它的轻量级和高性能所吸引。作为一款开源的流媒体服务器,它支持RTSP、RTMP、HLS等多种协议,特别适合中小型视频项目的快速部署。记得当时为了测试性能,我在一台2核4G…...
Vivado中OSD核报错全攻略:从IP_flow 19-167到BD 41-1030的解决方案
Vivado中OSD核报错全攻略:从IP_flow 19-167到BD 41-1030的解决方案 在FPGA开发过程中,Xilinx Vivado工具链的OSD(On-Screen Display)核是一个常用的视频处理IP,但开发者常会遇到各种报错问题。本文将深入解析从IP_flo…...
告别Widgets?用QtQuick和QML为你的桌面应用注入现代感(附完整Demo)
从Qt Widgets到QtQuick:打造现代桌面应用的实战指南 在桌面应用开发领域,Qt框架一直以其跨平台能力和稳定性著称。然而,随着用户对界面体验要求的提升,传统的Widgets方式逐渐显露出局限性——动画生硬、响应迟钝、与现代操作系统风…...
HBuilderX + 极光推送踩坑实录:免费版为啥息屏收不到通知?手把手教你配置与避坑
HBuilderX与极光推送免费版避坑指南:破解息屏通知失效难题 早上八点,你的咖啡还没喝完,测试组的消息就炸开了锅——"昨晚推送的版本在息屏状态下根本收不到通知!"作为使用HBuilderX开发跨平台应用的团队,这个…...
STC15W4K32S4寄存器操作避坑指南:为什么你的PWM输出异常?(附完整初始化流程图)
STC15W4K32S4寄存器操作避坑指南:为什么你的PWM输出异常? 最近在调试STC15W4K32S4的PWM功能时,发现不少开发者都会遇到一些共性问题:明明按照手册配置了寄存器,PWM输出就是不稳定或者干脆没有波形。这些问题往往源于几…...
智能内容解锁工具:5分钟掌握付费墙突破技巧
智能内容解锁工具:5分钟掌握付费墙突破技巧 【免费下载链接】bypass-paywalls-chrome-clean 项目地址: https://gitcode.com/GitHub_Trending/by/bypass-paywalls-chrome-clean 在数字信息时代,优质内容常被付费墙阻隔,而bypass-payw…...
STM32F103C8T6与HC05蓝牙模块实战:手机APP控制OLED显示(附完整代码)
STM32F103C8T6与HC05蓝牙模块实战:手机APP控制OLED显示(附完整代码) 1. 项目概述与硬件准备 在物联网终端设备交互场景中,蓝牙通信因其低功耗、低成本的特点成为短距离无线传输的理想选择。本项目基于STM32F103C8T6微控制器与HC05…...
Artisan咖啡烘焙专业级工具实战指南:从数据驱动到精准控制
Artisan咖啡烘焙专业级工具实战指南:从数据驱动到精准控制 【免费下载链接】artisan artisan: visual scope for coffee roasters 项目地址: https://gitcode.com/gh_mirrors/ar/artisan Artisan作为一款开源咖啡烘焙可视化软件,为专业烘焙师提供…...
