命令模式(Command Pattern)

命令模式(Command Pattern)是一种行为型设计模式,它将请求(命令)封装成对象,从而使您能够参数化客户端(调用者)使用不同的请求、队列请求或者日志请求,甚至支持可撤销的操作。

核心思想是 将请求封装成对象,从而使得调用者与接收者解耦,同时支持请求的存储、撤销、重做和排队执行。

主要组成部分

  1. Command(命令接口):通常是一个接口,定义一个执行命令的 execute() 方法。

  2. ConcreteCommand(具体命令):实现命令接口,并定义执行该命令的具体操作,通常将请求的接收者(Receiver)和操作绑定在一起。

  3. Client(客户端):创建一个具体命令对象并设置其接收者(Receiver)。

  4. Invoker(调用者):请求命令的执行。通常在用户操作时,会调用 execute() 方法。

  5. Receiver(接收者):知道如何实施与执行一个请求相关的操作,实际的业务逻辑通常由此类执行。

案例实现

一个 图形编辑器,用户可以执行对图形的操作(如绘制、擦除等),并能够撤销这些操作。

案例类图

image

命令调用者类依赖于命令接口,命令接口下的具体命令类操作实际的业务逻辑

Command接口

public interface Command {void execute();void undo();
}

绘制图形命令

// ConcreteCommand - 绘制图形命令
public class DrawShapeCommand implements Command {private Shape shape;public DrawShapeCommand(Shape shape) {this.shape = shape;}@Overridepublic void execute() {shape.draw();}@Overridepublic void undo() {shape.erase();}
}// ConcreteCommand - 删除图形命令
class EraseShapeCommand implements Command {private Shape shape;public EraseShapeCommand(Shape shape) {this.shape = shape;}@Overridepublic void execute() {shape.erase();}@Overridepublic void undo() {shape.draw();}
}

接受者--抽象图形类

public abstract class Shape {protected String name;public abstract void draw();public abstract void erase();
}

接受者--具体图形类

public class Circle extends Shape {public Circle() {this.name = "Circle";}@Overridepublic void draw() {System.out.println("绘制图形 " + name);}@Overridepublic void erase() {System.out.println("擦除图形 " + name);}
}class Rectangle extends Shape {public Rectangle() {this.name = "Rectangle";}@Overridepublic void draw() {System.out.println("绘制图形 " + name);}@Overridepublic void erase() {System.out.println("擦除图形 " + name);}
}

命令调用者

public class CommandInvoker {private Stack<Command> commandHistory = new Stack<>();public void executeCommand(Command command) {command.execute();commandHistory.push(command);}public void undo() {if (!commandHistory.isEmpty()) {Command lastCommand = commandHistory.pop();lastCommand.undo();} else {System.out.println("No commands to undo.");}}
}

测试代码

public class CommandDemo {public static void main(String[] args) {// 创建图形Shape circle = new Circle();Shape rectangle = new Rectangle();// 创建命令Command drawCircleCommand = new DrawShapeCommand(circle);Command drawRectangleCommand = new DrawShapeCommand(rectangle);Command eraseCircleCommand = new EraseShapeCommand(circle);Command eraseRectangleCommand = new EraseShapeCommand(rectangle);// 创建命令调用者CommandInvoker invoker = new CommandInvoker();// 执行命令invoker.executeCommand(drawCircleCommand);  // 绘制圆形invoker.executeCommand(drawRectangleCommand);  // 绘制矩形invoker.executeCommand(eraseCircleCommand);  // 删除圆形invoker.executeCommand(eraseRectangleCommand);  // 删除矩形// 撤销操作invoker.undo();  // 撤销删除矩形invoker.undo();  // 撤销删除圆形invoker.undo();  // 撤销绘制矩形invoker.undo();  // 撤销绘制圆形invoker.undo();}
}

测试结果

绘制图形 Circle

绘制图形 Rectangle

擦除图形 Circle

擦除图形 Rectangle

绘制图形 Rectangle

绘制图形 Circle

擦除图形 Rectangle

擦除图形 Circle

No commands to undo.

优缺点和适用场景

优点:

  1. 解耦请求者和接收者:请求者(客户端)不需要知道接收者的具体实现,只需要知道命令接口。

  2. 支持撤销操作:可以将命令对象设计为支持撤销的操作,使得某些操作能够撤回。

  3. 可以将命令参数化:命令可以作为参数传递,或被存储起来,支持批量操作。

  4. 扩展性好:增加新的命令时,不需要改变现有代码,只需要新增具体命令类。

缺点:

  1. 增加类的数量:每个具体命令类都需要创建一个类,可能导致类的数量增多。

  2. 实现复杂度:如果系统中的命令非常多,可能导致命令类实现过于复杂。

命令模式在 GUI 程序、事务管理、队列任务等场景中非常常见。

适用场景

  • 需要解耦请求者和接收者。

  • 需要撤销、重做操作。

  • 需要存储请求、支持队列、日志功能。

  • 需要动态选择操作或扩展操作。

  • 需要将多个操作封装为一个命令。

  • 需要管理跨平台或多设备的操作。

总结

命令模式的核心关注点是将请求封装成对象,从而使得请求的发送者(调用者)和接收者(执行者)解耦。命令模式通过把请求封装成命令对象,使得你可以在不改变请求者的情况下改变请求的执行方式、顺序或者操作对象。

  • 行为封装:命令模式将请求、操作或事务封装为命令对象,这些对象可以被请求者调用。请求者不关心具体操作的执行方式,只需要调用命令对象的执行方法即可。

  • 请求者和执行者解耦:通过引入命令对象,调用者和被调用者的关系被解耦,调用者不需要知道如何执行操作,也不需要知道具体的操作是什么,只需要发出命令请求。

文章转载自:渊渟岳

原文链接:掌握设计模式--命令模式 - 渊渟岳 - 博客园

体验地址:JNPF快速开发平台

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

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

相关文章

STM32之beep、多文件、延迟、按键以及呼吸灯

一、Beep控制 原理图分析&#xff1a; 蜂鸣器三极管控制引脚对应 MCU PB8。当前蜂鸣器对应的电路中&#xff0c;三极管是 NPN 三极管&#xff0c;当前【基极】存在小电流&#xff0c;当前三极管导通。要求对应 PB8 引脚对外输出电压 / 电流。当前 PB8 输出高电平&#xff0c;当…

C++的struct里面可以放函数,讨论一下C++和C关于struct的使用区别

我们来看一个C代码下面的struct结构体: struct UserValue {float lx;float ly;float rx;float ry;float L2;// 【构造函数】UserValue() {setZero();}// 【成员函数】void setZero() {lx 0;ly 0;rx 0;ry 0;L2 0;} };在这篇文章中&#xff0c;我们将来详细解释一下为什么 U…

【Kubernetes知识点】资源配额与访问控制

目录 1.解释ResourceQuota的作用。 2.解释Service Account的用途。 3.详细解释Role和ClusterRole。 4.什么是K8s的NetworkPolicy&#xff1f; 5.详细描述在K8s中如何控制跨Namespace的Pod访问&#xff1f; 1.解释ResourceQuota的作用。 ResourceQuota&#xff08;资源配额…

在SAP Query中添加双击事件

在SAP系统中&#xff0c;SAP Query是一个强大的工具&#xff0c;允许用户自定义报告以满足特定的数据查询需求。它提供了灵活的报表设计功能&#xff0c;使非编程背景的用户也能创建和修改查询。在某些情况下&#xff0c;我们可能希望在查询结果上添加交互性&#xff0c;比如通…

c++:MFC中sqlite3的使用(附实际案例)

MFC中sqlite3的使用sqlite3介绍sqlite3安装常用API函数操作流程接口函数执行sql语句函数回调函数MFC中案例实践控制台实践sqlite3介绍 SQLite 是一个软件库&#xff0c;实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 是在世界上最广泛部署的 SQL …

LeetCode第1019题 - 链表中的下一个更大节点

题目 解答 class Solution {Stack<Integer> stack new Stack<>();List<Integer> values new LinkedList<>();public int[] nextLargerNodes(ListNode head) {nextLargerNodes2(head);return values.stream().mapToInt(x -> x).toArray();}publi…

STM32 硬件I2C读写MPU6050

本文代码基于 STM32 单片机&#xff0c;通过 I2C 总线驱动 MPU6050 六轴传感器&#xff08;集成加速度计与陀螺仪&#xff09;&#xff0c;实现传感器初始化、ID 读取、原始数据采集&#xff0c;并借助 OLED 显示屏实时展示加速度&#xff08;AccX、AccY、AccZ&#xff09;与角…

倍福下的EC-A10020-P2-24电机调试说明

今天调试EC-A10020-P2-24电机&#xff0c;采用力位混合控制指令进行控制&#xff0c;无前馈力矩&#xff0c;只调节Kp和Kd,跟踪红色轨迹&#xff08;正弦信号&#xff1a;幅值10&#xff0c;频率0.5Hz&#xff09;&#xff0c;结果显示Kp 180, Kd 40&#xff0c;实际上Kp进一步…

SQL注入1----(sql注入原理)

一.前言前面我们讲解了一下信息收集&#xff0c;本章节我们来讲解一下sql注入的基本原理&#xff0c;我们拿之前搭建的测试网站pikachu来测试&#xff0c;对应工具包也已经放在了工具里面&#xff0c;大家可以自行去下载。SQL注入攻击漏洞的原因&#xff0c;是由于程序员在编写…

C++智能指针详解:用法与实践指南

C智能指针详解&#xff1a;用法与实践指南 在C编程中&#xff0c;动态内存管理始终是开发者面临的重要挑战。手动分配和释放内存不仅繁琐&#xff0c;还容易因疏忽导致内存泄漏、悬垂指针等问题。为解决这些痛点&#xff0c;C标准库引入了智能指针&#xff08;Smart Pointers&a…

fastdds qos:DurabilityQosPolicy

假如DataWriter先起来&#xff0c;并且已经写了一些数据&#xff0c;之后有新的DataReader起来&#xff0c;那么新起来的DataReader能不能接收到它启动之前&#xff0c;DataWriter发布的数据呢。DurabilityQosPolicy用来做这种控制。VOLATILE_DURABILITY_QOS&#xff1a;易失的…

【读代码】SQLBot:开源自然语言转SQL智能助手原理与实践

一、项目简介 SQLBot 是 DataEase 团队开源的自然语言转 SQL 智能助手,致力于让非技术用户也能通过自然语言与数据库对话,自动生成 SQL 查询,实现自助数据分析、智能BI问答、报表生成等场景。SQLBot 结合了大语言模型(LLM)、数据库元数据解析、SQL解析与执行等多项技术,…

开题报告被退回?用《基于大数据的慢性肾病数据可视化分析系统》的Hadoop技术,一次通过不是梦

&#x1f496;&#x1f496;作者&#xff1a;计算机编程小咖 &#x1f499;&#x1f499;个人简介&#xff1a;曾长期从事计算机专业培训教学&#xff0c;本人也热爱上课教学&#xff0c;语言擅长Java、微信小程序、Python、Golang、安卓Android等&#xff0c;开发项目包括大数…

HEVC(H.265)与HVC1的关系及区别

HEVC&#xff08;H.265&#xff09;与HVC1的关系及区别可归纳如下&#xff1a;一、技术定义差异‌HEVC&#xff08;H.265&#xff09;‌国际标准化组织制定的通用视频编码标准&#xff0c;由ITU-T和ISO/IEC联合开发‌1支持8K分辨率&#xff0c;压缩效率较H.264提升约50%‌1‌HV…

Java获取被nginx代理的emqx客户端真实ip

Java获取被nginx代理的emqx客户端真实ip 契机 ⚙ 使用nginx作为负载均衡&#xff08;Load Balancing&#xff09;的时候&#xff0c;发现真实ip无法获取。几经折腾终于拿到真实ip&#xff0c;又发现被代理的端口又无法使用非代理模式连接&#xff0c;由于之前暴露的docker端口有…

Jenkins自动化部署服务到Kubernetes环境

在现代软件开发中,持续集成和持续部署(CI/CD)已成为提高开发效率和软件质量的关键实践。本文将介绍如何使用Jenkins自动化部署服务到Kubernetes环境,并重点介绍Maven与私服的配置。 环境准备 在开始之前,请确保您已准备好以下环境: Jenkins服务器 Kubernetes集群 Docker镜…

OpenAI重新开源!gpt-oss-20b适配昇腾并上线魔乐社区

2025年8月5日&#xff0c;OpenAI发布了两款全新的开源权重语言模型&#xff0c;均为混合专家&#xff08;MoE&#xff09;架构&#xff0c;其规模设计可在消费级GPU和云端的多种硬件上高效运行。这些模型采用 Apache 2.0 许可协议&#xff0c;因此可用于蒸馏到其他推理模型中、…

SpringCloud入门(简洁明了)

目录 一.创建微服务项目 (一)环境准备 (二)项目结构图 (三)流程 二. Nacos (一)注册中心 1.服务注册 2.服务发现 3.编写微服务API 4.远程调用基本实现 5.负载均衡 6.LoadBalanced注解式注解均衡 7.注册中心宕机&#xff0c;远程调用还能成功吗 (二)配置中心 1.基…

集成算法学习笔记

一、集成算法简介1. 核心思想类比“多个专家综合判断优于单个专家”&#xff0c;通过构建并结合多个个体学习器&#xff0c;提升模型的泛化能力&#xff08;降低过拟合风险、提高预测准确性&#xff09;&#xff0c;完成复杂的学习任务。2. 个体学习器与结合模块个体学习器&…

让Chrome信任自签名证书

让Chrome信任自签名证书&#xff08;Unix系列OS&#xff09; 背景 想在本地测试自己写的基于HTTPS连接的Web应用&#xff0c;跑在3001端口。但使用Chrome浏览器访问https://localhost:3001时显示连接不安全。解决了但没解决 使用mkcert一键创建证书&#xff1a; mkcert localho…