命令模式(Command Pattern)详解


一、命令模式简介

命令模式(Command Pattern) 是一种 行为型设计模式(对象行为型模式),它将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,并支持请求的排队、记录日志、撤销等操作。

别名动作(Action)模式或事务(Transaction)模式

简单来说:

“把一次方法调用包装成一个对象,这样你就可以像处理普通对象一样来处理这些请求。”

将请求发送者和接收者完全解耦
发送者与接收者之间没有直接引用关系
发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求

在这里插入图片描述
相同的开关可以通过不同的电线来控制不同的电器
开关 <- -> 请求发送者
电灯<- -> 请求的最终接收者和处理者
开关和电灯之间并不存在直接耦合关系,它们通过电线连接在一起,使用不同的电线可以连接不同的请求接收者
在这里插入图片描述

命令模式包含以下4个角色
Command(抽象命令类)
ConcreteCommand(具体命令类)
Invoker(调用者)
Receiver(接收者)

在这里插入图片描述
命令模式的本质是对请求进行封装。

一个请求对应于一个命令,将发出命令的责任和执行命令的责任分开。

命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求如何被接收、操作是否被执行、何时被执行,以及是怎么被执行的。


二、解决的问题类型

命令模式主要用于解决以下问题:

  • 解耦请求发送者和接收者:发送者不需要知道具体执行逻辑,只需要知道如何发送“命令”。
  • 支持撤销/重做功能:通过保存命令历史,可以轻松实现撤销或重做操作。
  • 支持事务回滚、日志记录、队列任务等功能:命令可以被记录、排队、延迟执行。(在不同的时间指定请求、将请求排队和执行请求)
  • 系统需要将一组操作组合在一起形成宏命令

三、使用场景

场景示例
支持撤销操作文本编辑器中的“撤销”、“重做”按钮
解耦调用方与执行方遥控器控制多个家电设备
实现宏命令执行一组命令的组合操作
任务队列将多个命令加入队列异步执行
日志与恢复记录命令执行日志,系统崩溃后可恢复

四、核心角色

角色描述
Command(命令接口)定义执行命令的方法,如 execute()
ConcreteCommand(具体命令)实现 Command 接口,绑定具体的接收者(Receiver)并调用其方法
Invoker(调用者)持有命令对象,负责调用命令的 execute() 方法
Receiver(接收者)实际执行命令的对象,包含业务逻辑
Client(客户端)创建命令对象并设置接收者,将命令交给调用者执行

五、代码案例(Java)

我们以“智能遥控器控制家电”为例来演示命令模式的使用。

1. 定义命令接口

// 命令接口
interface Command {void execute();void undo(); // 可选:用于撤销操作
}

2. 创建接收者类(实际执行动作的对象)

// 灯的接收者
class Light {public void on() {System.out.println("The light is ON");}public void off() {System.out.println("The light is OFF");}
}// 风扇的接收者
class Fan {public void start() {System.out.println("The fan is STARTING");}public void stop() {System.out.println("The fan is STOPPING");}
}

3. 创建具体命令类

// 开灯命令
class LightOnCommand implements Command {private Light light;public LightOnCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.on();}@Overridepublic void undo() {light.off();}
}// 关风扇命令
class FanOffCommand implements Command {private Fan fan;public FanOffCommand(Fan fan) {this.fan = fan;}@Overridepublic void execute() {fan.stop();}@Overridepublic void undo() {fan.start();}
}

4. 创建调用者(比如遥控器)

// 遥控器
class RemoteControl {private Command command;public void setCommand(Command command) {this.command = command;}public void pressButton() {command.execute();}public void pressUndo() {command.undo();}
}

5. 客户端测试代码

public class Client {public static void main(String[] args) {// 创建接收者Light light = new Light();Fan fan = new Fan();// 创建具体命令Command lightOn = new LightOnCommand(light);Command fanOff = new FanOffCommand(fan);// 创建调用者RemoteControl remote = new RemoteControl();// 设置命令并执行remote.setCommand(lightOn);remote.pressButton();   // 输出:The light is ONremote.pressUndo();     // 输出:The light is OFFSystem.out.println("----------");remote.setCommand(fanOff);remote.pressButton();   // 输出:The fan is STOPPINGremote.pressUndo();     // 输出:The fan is STARTING}
}
典型代码(C++)

典型的抽象命令类代码

abstract class Command
{public abstract void Execute();
}

典型的调用者(请求发送者)类代码

class Invoker
{private Command command;//构造注入public Invoker(Command command){this.command=command;}	public Command Command{get { return command; }//设值注入set { command = value; } 
}	//业务方法,用于调用命令类的方法public void Call(){command.Execute();}
}

典型的具体命令类代码

class ConcreteCommand : Command
{private Receiver receiver; //维持一个对请求接收者对象的引用public override void Execute(){receiver.Action();//调用请求接收者的业务处理方法Action()}
}

典型的请求接收者类代码

class Receiver
{public void Action(){//具体操作}
}
其他案例(C++)
  1. 为了用户使用方便,某系统提供了一系列功能键,用户可以自定义功能键的功能,例如功能键FunctionButton可以用于退出系统(由SystemExitClass类来实现),也可以用于显示帮助文档(由DisplayHelpClass类来实现)。
    用户可以通过修改配置文件来改变功能键的用途,现使用命令模式来设计该系统,使得功能键类与功能类之间解耦,可为同一个功能键设置不同的功能。

在这里插入图片描述

  1. 电视机遥控器
    电视机是请求的接收者,遥控器是请求的发送者,遥控器上有一些按钮,不同的按钮对应电视机的不同操作。抽象命令角色由一个命令接口来扮演,有三个具体的命令类实现了抽象命令接口,这三个具体命令类分别代表三种操作:打开电视机、关闭电视机和切换频道。显然,电视机遥控器就是一个典型的命令模式应用实例。
    在这里插入图片描述

六、优缺点分析

优点描述
解耦调用者与接收者调用者无需了解具体执行细节,只需调用命令即可
支持撤销/重做、宏命令、日志记录等高级功能通过命令对象可以轻松实现这些功能,可以比较容易地设计一个命令队列或宏命令(组合命令)为请求的撤销(Undo)和恢复(Redo)操作提供了一种设计和实现方案
扩展性好新增命令只需添加新的 ConcreteCommand 类,符合开闭原则
缺点描述
增加类的数量每个命令都需要一个类,可能造成类爆炸
理解成本高对于新手来说,结构略复杂,需要一定学习成本
性能开销如果频繁创建命令对象,可能带来额外内存消耗

七、与其他模式对比(补充)

模式名称目标
策略模式封装算法,让算法可替换,强调“行为切换”
观察者模式当对象状态改变时通知所有监听者
命令模式将请求封装成对象,便于传递、存储、撤销等操作

八、最终小结

命令模式是一种非常灵活且强大的行为型设计模式,它适用于:

  • 需要解耦请求发送者和接收者的场景;
  • 需要支持撤销、重做、日志记录等高级功能;
  • 需要构建任务队列、宏命令、批处理机制等系统功能。

📌 一句话总结:

命令模式就像“遥控器”,你按下按钮,背后谁在干活你不知道,但你知道事情能办成。


推荐使用方式:

  • 在需要撤销/重做的系统中优先考虑命令模式;
  • 结合“命令历史”实现多级撤销;
  • 使用“宏命令”批量执行多个命令;
  • 用 JSON 序列化保存命令状态,实现持久化。

九、扩展

实现命令队列

动机

  1. 当一个请求发送者发送一个请求时,有不止一个请求接收者产生响应,这些请求接收者将逐个执行业务方法,完成对请求的处理。
  2. 增加一个CommandQueue类,由该类负责存储多个命令对象,而不同的命令对象可以对应不同的请求接收者。
  3. 批处理。

实现

using System.Collections.Generic;
namespace CommandSample
{class CommandQueue{//定义一个List来存储命令队列private List<Command> commands = new List<Command>();public void AddCommand(Command command){commands.Add(command);}public void RemoveCommand(Command command){commands.Remove(command);}//循环调用每一个命令对象的Execute()方法public void Execute() {foreach (object command in commands) {((Command)command).Execute();}}}
}

记录请求日志

动机
将请求的历史记录保存下来,通常以日志文件(Log File)的形式永久存储在计算机中

  1. 为系统提供一种恢复机制
  2. 可以用于实现批处理
  3. 防止因为断电或者系统重启等原因造成请求丢失,而且可以避免重新发送全部请求时造成某些命令的重复执行

实现
将发送请求的命令对象通过序列化写到日志文件中
命令类必须使用属性[Serializable]标记为可序列化
在这里插入图片描述

实现撤销操作

实例
可以通过对命令类进行修改使得系统支持撤销(Undo)操作和恢复(Redo)操作。
设计一个简易计算器,该计算器可以实现简单的数学运算,还可以对运算实施撤销操作。

在这里插入图片描述

宏命令

动机

  1. 宏命令(Macro Command)又称为组合命令(Composite Command),它是组合模式和命令模式联用的产物
  2. 宏命令是一个具体命令类,它拥有一个集合,在该集合中包含了对其他命令对象的引用
  3. 当调用宏命令的Execute()方法时,将递归调用它所包含的每个成员命令的Execute()方法。一个宏命令的成员可以是简单命令,还可以继续是宏命令
  4. 执行一个宏命令将触发多个具体命令的执行,从而实现对命令的批处理

在这里插入图片描述

部分内容由AI大模型生成,注意识别!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/914690.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/914690.shtml
英文地址,请注明出处:http://en.pswp.cn/news/914690.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTML5智能排班日历:动态排班一目了然

这个日历将具备以下功能: 显示一个标准的月度日历视图。可以自由切换上一个月和下一个月。在日历的每一天自动显示当天值班的人员。您可以很方便地在文件中修改值班人员列表和排班的起始日期。包括:动态生成日历网格处理月份切换根据排班规则计算并显示每天的值班人员<!DO…

深度剖析C++生态系统:一门老牌语言如何在开源浪潮中焕发新生?

&#x1f4dd;个人主页&#x1f339;&#xff1a;慌ZHANG-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 一、前言&#xff1a;C的“长寿秘诀”是什么&#xff1f; C 诞生已超过 40 年。它经历了桌面应用、互联网爆发、移动时代&#xff0c;再…

60个功能OfficeBox 万彩办公大师:PDF 格式转换 OCR识别免费无广告

各位办公小能手们&#xff01;今天给大家介绍个超厉害的免费办公工具套装——OfficeBox万彩办公大师&#xff0c;是广州万彩科技整出来的。软件下载地址安装包 它里面有60多个没广告的绿色组件&#xff0c;简直像个百宝箱&#xff01;涵盖了PDF处理、格式转换、OCR识别、屏幕录…

拥抱主权AI:OpenCSG驱动智能体运营,共筑新加坡智能高地

2025年7月11日&#xff0c;由Linux基金会AI & Data、TikTok及LF Edge联合主办的 【LF AI & Data Day Singapore 2025】 在新加坡TikTok总部盛大启幕。本次大会以“Agent for SWE”为核心议题&#xff0c;汇聚全球顶尖AI开发者、企业领袖及开源社区先锋。作为国家主权AI…

单片机学习笔记.根据芯片数据手册写驱动程序(这里使用的是普中开发版,以DS1302为例)

硬件原理图部分&#xff1a; VCC2:是主电源 VCC1&#xff1a;是备用电源&#xff0c;此处没有使用VCC1 查芯片数据手册的网站&#xff1a; ALLDATASHEETCN.COM - 电子元件和半导体及其他半导体的数据表搜索网站。https://www.alldatasheetcn.com/ 1.由原理图可知对应引脚&…

Capture One24下载与保姆级安装教程!

软件下载 软件名称&#xff1a;Capture One24 软件语言&#xff1a;简体中文 软件大小&#xff1a;1.06G 系统要求&#xff1a;Windows7或更高&#xff0c;32/64位操作系统 硬件要求&#xff1a;CPU2.5GHz&#xff0c;RAM4G或更高 下载通道丨下载&#xff1a;https://too…

微信小程序(数据库)

const dbwx.cloud.database()//连接数据库db.collection("test").doc("b69f67c0626fac9000e123fc1ff07a42&#xff08;为要查询数据的id&#xff09;").get({success:res>{console.log(res)}})或getData(){db.collection("test").doc("&…

Apache CXF 漏洞曝光:存在拒绝服务与数据泄露双重风险

Apache软件基金会近日披露了一个影响多个Apache CXF版本的安全漏洞&#xff08;CVE-2025-48795&#xff09;。Apache CXF是开发者广泛使用的开源Web服务框架&#xff0c;用于构建基于SOAP和REST的应用程序。漏洞双重威胁该漏洞具有双重危害性&#xff1a;一方面可能通过内存耗尽…

Android 应用自动更新:从理论到实战的硬核指南

目录 1. 自动更新的核心逻辑:为什么它对用户体验至关重要? 自动更新的本质 为什么它如此重要? 2. 版本检测:如何优雅地发现“新大陆”? 设计版本检测的逻辑 实现版本检测的 API 请求 用户体验优化 3. 下载新版本:稳妥地获取安装包 下载的两种方式 注意事项 用户…

每日面试题05:ArrayList和LinkedList的底层原理

ArrayList与LinkedList深度解析&#xff1a;从底层原理到实战选择在Java的List接口实现中&#xff0c;ArrayList和LinkedList是最常用的两种选择。面试中“它们的区别”几乎是必问题&#xff0c;但仅仅停留在“数组vs链表”的层面显然不够。本文将从​​底层数据结构、内存布局…

python的慈善捐赠平台管理信息系统

前端开发框架:vue.js 数据库 mysql 版本不限 后端语言框架支持&#xff1a; 1 java(SSM/springboot)-idea/eclipse 2.NodejsVue.js -vscode 3.python(flask/django)–pycharm/vscode 4.php(thinkphp/laravel)-hbuilderx 数据库工具&#xff1a;Navicat/SQLyog等都可以 摘要 本文…

三十二、【核心功能改造】数据驱动:重构仪表盘与关键指标可视化

三十二、【核心功能改造】数据驱动:重构仪表盘与关键指标可视化 前言准备工作第一部分:后端实现 - 统计 API1. 创建 `DashboardStatsView`2. 注册统计 API 路由3. 后端初步测试第二部分:前端实现 - 重构仪表盘页面1. 创建 `api/dashboard.ts` API 服务2. 重构 `HomeView.vue…

神经网络与深度学习Python入门

一、神经网络基础 1. 神经元模型 在神经网络中&#xff0c;最基本的单元是神经元&#xff08;Neuron&#xff09;&#xff0c;也称为节点或单元。它模拟了生物神经系统中的神经元行为。一个典型的神经元模型包含多个输入&#xff08;x1,x2,…,xnx_1, x_2, \ldots, x_nx1​,x2​…

Android System WebView:Android生态的核心组件

在Android生态系统中&#xff0c;Android System WebView&#xff08;简称WebView&#xff09;扮演着至关重要的角色。它是Chrome浏览器的内核&#xff0c;为Android设备提供了强大的网页浏览和Web内容展示功能。无论是日常浏览网页、使用基于Web的应用程序&#xff0c;还是进行…

Element Plus和Ant Design Vue深度对比分析与选型指南

在 Vue3生态中&#xff0c;Element Plus和Ant Design Vue&#xff08;以下简称 AntD Vue&#xff09;是两款最主流的 UI 组件库。它们分别脱胎于 Element UI&#xff08;Vue 2 版本&#xff09;和 Ant Design&#xff08;React 生态&#xff09;&#xff0c;经过多年迭代已成为…

AJAX 开发中的注意点

关键词&#xff1a;AJAX、异步请求、前端开发、跨域、错误处理、安全、性能优化 ✅ 引言 在现代 Web 应用中&#xff0c;AJAX 是实现前后端数据交互的重要手段。然而&#xff0c;在实际开发过程中&#xff0c;如果不注意一些常见问题&#xff0c;可能会导致应用出现安全性漏洞…

类之间的纵向关系——继承

继承定义&#xff1a;被继承的类叫做基类&#xff08;父类&#xff09;&#xff0c;继承的类叫派生类&#xff08;子类&#xff09;&#xff0c;在派生类类名后面加&#xff1a; 继承方式 基类class CFather{}; class CSon:public CFather{};父类(基类)与子类(派生类)之间的关系…

bytetrack漏检补齐

bytetrack漏检补齐1.人流慢速运动&#xff0c;跟踪效果比较好&#xff0c;偶尔有漏检&#xff0c;跟踪可以自动补齐。2.快速运动&#xff0c;频繁遮挡&#xff0c;效果可能不好*如果漏检&#xff0c;倒着跟踪&#xff0c;把丢失的检测框拷贝出来&#xff0c;保留进行跟踪。有时…

安装Keycloak并启动服务(macOS)

前提&#xff1a;电脑已经安装Java 17 1、下载Keycloak 2、下载完后解压缩&#xff0c;使用文本编辑器修改配置文件&#xff08;keycloak/conf/keycloak.conf&#xff09; # Basic settings for running in production. Change accordingly before deploying the server. # …

汽车动力转向器落锤冲击试验台

落锤冲击试验台主要用于扣件减振量的测试&#xff0c;采用电动锚链提锤结构&#xff0c;控制精度高&#xff0c;定位准确。采用伺服电机减速机驱动&#xff0c;避免提锤加速和到位减速时的冲击&#xff0c;具有多重安全保护功能&#xff0c;防止二次冲击装置。主机框架采用上下…