程序员必知!命令模式的实战应用与案例分析

程序员必知!命令模式的实战应用与案例分析 - 程序员古德

命令模式是一种行为设计模式,它将请求封装为对象以实现客户端参数化、请求排队、日志记录及撤销操作,旨在解耦调用者与操作实现者,以智能家居为例,用户通过界面发送命令对象,设备作为接收者执行相应操作,无需用户了解具体执行方式,从而增强了系统的灵活性和可扩展性。

定义

程序员必知!命令模式的实战应用与案例分析 - 程序员古德

命令模式是一种行为设计模式,它允许将一个请求封装为一个对象,从而可用不同的请求把客户端参数化,对请求排队或记录请求日志,以及支持可撤销的操作,主要目的是将调用操作的对象与知道如何实现该操作的对象解耦。

举一个业务中的例子来说明命令模式:假如有一个智能家居系统,其中有各种设备如灯光、空调、窗帘等,用户可以通过手机应用、语音助手或墙壁开关来控制这些设备,在这个场景中,命令模式可以很好地应用,在命令模式中有如下几个角色和分工:

  1. 命令对象:每个设备操作(如“打开灯光”、“关闭空调”)都可以封装为一个命令对象,这个命令对象包含了执行该操作所需的所有信息,包括目标设备、操作类型等。
  2. 调用者:用户或用户通过的界面(如手机应用)是调用者,他们不知道具体如何执行某个操作,但他们可以创建和发送命令对象。
  3. 接收者:实际执行操作的设备(如灯光设备、空调设备)是接收者,它们知道如何响应特定的命令。
  4. 撤销操作:命令模式还支持撤销操作,例如,如果用户误操作打开了灯光,他们可以发送一个“关闭灯光”的命令来撤销之前的操作。

这个例子中,用户(调用者)只需要发送命令,而不需要知道如何执行这些命令,设备(接收者)则负责根据接收到的命令执行相应的操作,这使得系统更加灵活和可扩展。

代码案例

程序员必知!命令模式的实战应用与案例分析 - 程序员古德

日常开发中,未使用命令模式时,代码通常会直接将调用者和接收者紧密耦合在一起,下面是一个简单的反例,展示了未使用命令模式时如何实现一个智能家居系统中的灯光控制功能。首先,定义一个Light类作为接收者,它负责执行打开和关闭灯光的操作,如下代码:

// 灯光类,作为接收者  
public class Light {  private boolean isOn;  public Light() {  this.isOn = false;  }  // 打开灯光  public void turnOn() {  this.isOn = true;  System.out.println("Light is on.");  }  // 关闭灯光  public void turnOff() {  this.isOn = false;  System.out.println("Light is off.");  }  // 检查灯光状态  public boolean isOn() {  return isOn;  }  
}

接下来,定义一个SmartHomeController类作为调用者,在这个类中,直接调用了Light对象的方法,如下代码:

// 智能家居控制器类,作为调用者  
public class SmartHomeController {  private Light light;  public SmartHomeController(Light light) {  this.light = light;  }  // 控制灯光打开  public void controlLightOn() {  light.turnOn();  }  // 控制灯光关闭  public void controlLightOff() {  light.turnOff();  }  
}

最后,在client代码中使用SmartHomeController来控制灯光的打开和关闭,如下代码:

// 客户端代码  
public class Client {  public static void main(String[] args) {  // 创建灯光对象  Light light = new Light();  // 创建智能家居控制器对象,并将灯光对象传递给它  SmartHomeController controller = new SmartHomeController(light);  // 控制灯光打开  controller.controlLightOn();  // 控制灯光关闭  controller.controlLightOff();  // 如果需要添加撤销操作或者记录操作日志,这种直接调用的方式将会变得复杂  }  
}

输出结果:

Light is on.  
Light is off.

在这个反例中,SmartHomeController直接调用了Light对象的方法,这意味着如果需要添加额外的功能,比如记录操作日志,则需要在SmartHomeController中添加相应的逻辑,这会增加代码的复杂性,并且违反了开闭原则(对扩展开放,对修改关闭),此外,如果需要替换灯光控制的实现,比如使用远程服务器来控制灯光,也需要修改SmartHomeController的代码。

下面是一个使用命令模式的正例代码,展示了如何实现一个智能家居系统中的灯光控制功能,当使用命令模式时,可以将请求封装为对象,并在调用者和接收者之间引入一个间接层,首先,定义一个Light类作为接收者,它负责执行打开和关闭灯光的操作,如下代码:

// 灯光类,作为接收者  
public class Light {  private boolean isOn;  public Light() {  this.isOn = false;  }  // 打开灯光  public void turnOn() {  this.isOn = true;  System.out.println("Light is on.");  }  // 关闭灯光  public void turnOff() {  this.isOn = false;  System.out.println("Light is off.");  }  // 检查灯光状态(此例中未使用,仅为完整性)  public boolean isOn() {  return isOn;  }  
}

接下来,定义命令接口和它的实现类,如下代码:

// 命令接口  
public interface Command {  void execute();  void undo();  
}  // 打开灯光命令实现  
public class TurnOnLightCommand implements Command {  private Light light;  public TurnOnLightCommand(Light light) {  this.light = light;  }  @Override  public void execute() {  light.turnOn();  }  @Override  public void undo() {  light.turnOff();  }  
}  // 关闭灯光命令实现  
public class TurnOffLightCommand implements Command {  private Light light;  public TurnOffLightCommand(Light light) {  this.light = light;  }  @Override  public void execute() {  light.turnOff();  }  @Override  public void undo() {  light.turnOn();  }  
}

然后,定义一个调用者类SmartHomeController,它不直接调用接收者,而是使用命令对象来执行操作,如下代码:

// 智能家居控制器类,作为调用者  
import java.util.Stack;  public class SmartHomeController {  private Stack<Command> commandStack = new Stack<>();  // 执行命令,并将命令压入栈中以支持撤销  public void executeCommand(Command command) {  command.execute();  commandStack.push(command);  }  // 撤销上一个命令  public void undoLastCommand() {  if (!commandStack.isEmpty()) {  Command lastCommand = commandStack.pop();  lastCommand.undo();  }  }  
}

最后,在客户端代码中使用SmartHomeController来控制灯光的打开和关闭,并展示撤销功能:

// 客户端代码  
public class Client {  public static void main(String[] args) {  // 创建灯光对象  Light light = new Light();  // 创建智能家居控制器对象  SmartHomeController controller = new SmartHomeController();  // 创建并打开灯光命令  Command turnOnCommand = new TurnOnLightCommand(light);  controller.executeCommand(turnOnCommand);  // 创建并关闭灯光命令  Command turnOffCommand = new TurnOffLightCommand(light);  controller.executeCommand(turnOffCommand);  // 撤销上一个命令(关闭灯光),灯光应该会再次打开  controller.undoLastCommand();  }  
}

输出结果:

Light is on.  
Light is off.  
Light is on.

在这个例子中,SmartHomeController不再直接调用Light对象的方法,而是通过Command接口间接地执行操作,这种间接层允许在不修改调用者和接收者的情况下添加新功能,比如日志记录。此外,由于调用者和接收者之间的解耦,可以轻松地替换灯光控制的实现,比如使用远程服务器来控制灯光,而不需要修改SmartHomeController的代码。

核心总结

程序员必知!命令模式的实战应用与案例分析 - 程序员古德

命令模式,作为行为设计模式的一种,其核心思想在于将请求或操作封装成对象,这种封装不仅让请求本身变得更加具体和可管理,更重要的是,它实现了请求发送者与请求接收者之间的解耦。在传统的程序设计中,请求的发送者往往直接调用接收者的方法,这种方式虽然简单直接,但会导致发送者和接收者之间紧密耦合,不利于代码的维护和扩展。

在复杂的业务场景中,特别是当存在多个调用者和接收者,并且这些组件之间需要解耦时,命令模式就显得尤为有用。通过命令模式可以将调用者和接收者完全分离,调用者不再直接调用接收者的方法,而是通过一个中间的命令对象来间接地发出请求,这个命令对象封装了接收者的方法调用和相关的参数,它可以在调用者和接收者之间传递,并在适当的时候由调用者执行。

其它思考

命令模式在一些设计模式中可能会与其他模式产生混淆,尤其是与策略模式和状态模式,如下:

  1. 策略模式,策略模式定义了一系列算法,并将每个算法封装起来,使它们可以互换,策略模式关注的是算法的替换问题,允许在运行时选择使用哪个算法,而命令模式则更关注于请求的封装、排队和撤销等操作,尽管两者都涉及到了行为的封装和替换,但它们的意图和使用场景是不同的。
  2. 状态模式,状态模式允许对象在内部状态改变时改变其行为,看起来好像修改了其类,状态模式与命令模式在处理对象行为方面有一定的相似性,但它们解决的问题不同,状态模式关注的是对象状态变化时的行为改变,而命令模式则关注于将请求封装为对象以实现解耦和撤销等操作。

总结:策略模式关注算法的替换,状态模式关注状态变化时的行为改变,而命令模式关注请求的封装、排队和撤销等操作。

完!

关注我,每天学习互联网编程技术 - 程序员古德

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

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

相关文章

Ubuntu 安装Nginx服务

文章目录 前言一、Nginx安装1. Nginx默认安装2. Nginx指定版本安装3. Nginx验证4. Nginx服务控制4.1 查看服务状态4.2 停止服务4.3 启动服务4.4 重启服务 5. Nginx文件存放目录 二、自己编译Nginx1. 下载源码2. 依赖配置3. 编译 三、Nginx卸载总结 前言 Nginx&#xff08;发音为…

SDN和NFV最明显的区别,SDN和传统网络有什么区别

目录 SDN和NFV最明显的区别是 SDN和传统网络有什么区别 一、SDN概述 1.1 S

Fixed win size sliding window

这篇记录灵神题单中的定长滑窗环节&#xff0c;不跟之前的Sliding Window一起了。 1. LC 1423 可获得的最大点数 这题其实有点思维的。实现上简单。 每次从首或者尾部拿&#xff0c;总共拿k次。相当于有n-k张牌不拿。因为不可能从中间截断着拿&#xff0c;因此必然这n-k张牌…

分布式(2)

目录 6.Ribbon和Feign的区别有哪些&#xff1f; 7.SpringCloud和dubbo有什么区别&#xff1f; 8.服务注册和发现是什么意思&#xff1f;SpringCloud如何实现&#xff1f; 9.介绍下网关的作用&#xff1f; 10.谈谈服务降级&#xff0c;熔断&#xff0c;服务隔离&#xff1f;…

机器学习(四) -- 模型评估(3)

系列文章目录 机器学习&#xff08;一&#xff09; -- 概述 机器学习&#xff08;二&#xff09; -- 数据预处理&#xff08;1-3&#xff09; 机器学习&#xff08;三&#xff09; -- 特征工程&#xff08;1-2&#xff09; 机器学习&#xff08;四&#xff09; -- 模型评估…

通货紧缩,通货膨胀

目录 通货紧缩 通货膨胀 通货紧缩 是一个与通货膨胀相对应的经济学概念&#xff0c;是由货币供给量相对于经济增长和劳动生产率提高等要素减少而引致的有效需求严重不足 一般物价水平持续下跌&#xff0c;货币供给量持续下降和经济衰退的现象。当市场上流通的货币减少&…

探索 OceanBase 中图数据的实现

在数据管理和处理的现代环境中&#xff0c;对能够处理复杂数据结构的复杂数据模型和方法的需求从未如此迫切。图数据的出现以其自然直观地表示复杂关系的独特能力&#xff0c;开辟了数据分析的新领域。 虽然 Neo4j 等成熟的图形数据库为处理图形数据提供了强大的解决方案&…

Python从入门到网络爬虫(模块详解)

模块 我们知道&#xff0c;函数和类都是可以重复调用的代码块。在程序中使用位于不同文件的代码块的方法是&#xff1a;导入 (import) 该对象所在的模块 (mudule)。当程序变得越来越大时&#xff0c;将程序的不同部分根据不同分类方法保存在不同文件中通常会更加方便。 导入模…

python抓取异步数据(加载更多类型的),并下载图片,保存到文件夹,实例

源码: {"subjects":[{"episodes_info":"","rate":"8.8","cover_x":3000,"title":"奥本海默","url":"https:\/\/movie.douban.com\/subject\/35593344\/","playabl…

hydra爆破

导语 Hydra 是一个用于暴力破解登录凭据的网络安全工具。它可以通过尝试多个用户名和密码组合来攻击目标系统,以获得未经授权的访问权限。 Hydra 支持多种协议和服务,包括常见的 SSH、FTP、Telnet、HTTP、SMTP 等。它可以通过使用字典文件或生成密码的模式进行强制破解。可以…

如何理解Transformer论文中的positional encoding,和三角函数有什么关系?

大家好&#xff0c;我分享交流下这个问题。 Positional Encoding 掏出一张被无数人讲述的架构图。 Transformer 模型中的位置编码&#xff08;Positional Encoding&#xff09;是为了让模型能够考虑单词在句子中的位置。 由于 Transformer 的自注意力&#xff08;Self-Atte…

bat批处理文件_bat注释汇总

文章目录 1、示例&#xff08;直接结合脚本和结果进行理解&#xff09; 1、示例&#xff08;直接结合脚本和结果进行理解&#xff09; %这是一个注释% %这是另一个注释%rem 这是一个注释 rem 这是另一个注释:这是一个注释 ::这是一个注释 :?这是另一个注释if 1 1 ( %这里会执…

Golang 切片

前言 在Go语言中&#xff0c;切片是一个引用类型&#xff0c;它提供了对数组的动态窗口。切片并不存储任何数据&#xff0c;它只是描述了底层数组中的一个片段。切片的定义包括三个部分&#xff1a;指向数组的指针、切片的长度和切片的容量 基本使用 声明切片&#xff1a;声…

Django在urls.py利用函数path()配置路由时传递参数给调用的视图函数的方法

01-单个参数的传递 问&#xff1a;在urls.py利用函数path()配置路由时能不能传递一些参数给调用的视图函数&#xff1f;因为我有很多路由调用的其实是同一个视图函数&#xff0c;所以希望能传递一些额外的参数。比如路由的PATH信息如果能传递就好了。 答&#xff1a;在Django中…

伐木工 - 华为OD统一考试

OD统一考试 题解&#xff1a; Java / Python / C 题目描述 一根X米长的树木&#xff0c;伐木工切割成不同长度的木材后进行交易&#xff0c;交易价格为每根木头长度的乘积。规定切割后的每根木头长度都为正整数,也可以不切割&#xff0c;直接拿整根树木进行交易。请问伐木工如…

MySQL 日期比较

MySQL日期格式比较常用的两个函数是DATEDIFF()和TIMESTAMPDIFF()。 DATEDIFF SELECT DATEDIFF(2023-12-31 23:59:59, 2023-12-30); # 1SELECT DATEDIFF(2023-11-30 23:59:59, 2023-12-31); # -31第一个参数减去第二个参数得到的天数。 TIMESTAMPDIFF SELECT TIMESTAMPDIFF(…

libexif库介绍

libexif是一个用于解析、编辑和保存EXIF数据的库。它支持EXIF 2.1标准(以及2.2中的大多数)中描述的所有EXIF标签。它是用纯C语言编写的&#xff0c;不需要任何额外的库。源码地址&#xff1a;https://github.com/libexif/libexif &#xff0c;最新发布版本为0.6.24&#xff0c;…

计算圆弧的起始角度、终止角度和矩形信息并使用drawArc绘制圆弧

Qt中常用绘制圆弧的库函数&#xff1a; //函数原型 void QPainter::drawArc(const QRectF &rectangle, int startAngle, int spanAngle)Qt规定1约占16个像素&#xff0c;比如一个完整的圆等于360度&#xff0c;对应的像素角度就是 5760度&#xff08;16 * 360&#xff09;…

深度学习中的自动化标签转换:对数据集所有标签做映射转换

在机器学习中&#xff0c;特别是在涉及图像识别或分类的项目中&#xff0c;标签数据的组织和准确性至关重要。本文探讨了一个旨在高效转换标签数据的 Python 脚本。该脚本在需要更新或更改类标签的场景中特别有用&#xff0c;这是正在进行的机器学习项目中的常见任务。我们将逐…

基于JavaWeb+SSM+Vue家政项目微信小程序系统的设计和实现

基于JavaWebSSMVue家政项目微信小程序系统的设计和实现 源码获取入口Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 Lun文目录 目录 1系统概述 1 1.1 研究背景 1 1.2研究目的 1 1.3系统设计思想 1 2相关技术 2…