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

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

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

定义

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

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

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

  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.hqwc.cn/news/326939.html

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

相关文章

EBU7140 Security and Authentication(三)密钥管理;IP 层安全

B3 密钥管理 密钥分类&#xff1a; 按时长&#xff1a; short term&#xff1a;短期密钥&#xff0c;用于一次加密。long term&#xff1a;长期密钥&#xff0c;用于加密或者授权。 按服务类型&#xff1a; Authentication keys&#xff1a;公钥长期&#xff0c;私钥短期…

航空公司管理系统(迷你版12306)

要求 今天分享一个之前辅导留学生的作业&#xff0c;作业要求如下&#xff1a; Project E: Airways Management System Overall description: Your team is employed by an Airways company for the implementation of a computer system responsible for a large part of th…

60天零基础干翻C++————初识C++

初识c 命名空间命名空间的定义命名空间的使用 输入输出流缺省参数引用引用定义常量的引用引用的使用场景做函数参数引用做返回值 命名空间 命名空间的定义 在c语言中会有下面问题 上述代码中&#xff0c;全局变量rand 可能会命名冲突&#xff0c;如下图 此时编译失败&…

Python编程技巧 – 编写单行if条件语句

Python编程技巧 – 编写单行if条件语句 Python Programming Skills – Program Single-liner if Conditionals By JacksonML 通常&#xff0c;我们在写Python代码的时候&#xff0c;都会按部就班地一行行写完&#xff0c;代码的丰富足以让自己骄傲和充实。 实际上&#xff0…

PostGIS学习教程十八:维数扩展的9交集模型

PostGIS学习教程十八&#xff1a;维数扩展的9交集模型 文章目录 PostGIS学习教程十八&#xff1a;维数扩展的9交集模型一、什么是维数扩展的9交集模型二、查找具有特定关系的几何图形三、数据质量测试四、本文涉及的函数五、更多相关资料 一、什么是维数扩展的9交集模型 “维数…

我的第一个前端项目,vue项目从零开始创建和运行

​入门前端&#xff0c;从基础做起&#xff0c;从零开始新建项目 背景&#xff1a;VUE脚手架项目是一个“单页面”应用&#xff0c;即整个项目中只有1个网页&#xff01; 在VUE脚手架项目中&#xff0c;主要是设计各个“视图组件”&#xff0c;它们都是整个网页中某个部分&…

jetson AGC orin 配置pytorch和cuda使用、yolov8 TensorRt测试

文章目录 1、安装环境1.1、检查系统环境1.2、下载安装pytorch1.3、下载安装torchvision1.3、测试安装是否成功 2、yolov8测试2.1、官方python脚本测试2.2、tensorrt 模型转换2.3、tensorrt c 测试 1、安装环境 1.1、检查系统环境 检查系统环境、安装jetpack版本&#xff0c;执…

Android14之禁掉Selinux的两种方式(一百七十四)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

【读书笔记】网空态势感知理论与模型(九)

对分析人员数据分类分流操作的研究 1.概述 本章节介绍一种以人员为中心的智能数据分类分流系统&#xff0c;该系统利用了入侵检测分析人员的认知轨迹。整合了3个维度的动态网络-人系统&#xff08;cyber-humber system&#xff09;&#xff1a;网空防御分析人员、网络监测数据…

VMware中找到存在但是不显示的虚拟机(彻底发现)VMware已创建虚拟机不显示

删除VMware中的虚拟机的时候&#xff0c;可能没有把虚拟机完全删除&#xff0c;或者说 “移除” 后找不到虚拟机在哪里&#xff0c;内存空间也没有得到释放&#xff0c;那该如何解决呢&#xff1f; 1.明确&#xff1a; “移除” 不等于 “从磁盘删除” 移除&#xff1a;只…

聚道云软件连接器助力某半导体行业公司实现访客管理自动化

客户介绍&#xff1a; 某半导体行业公司是一家全球领先的半导体公司&#xff0c;在全球拥有众多研发中心和生产基地。该公司每天都有大量的访客来访&#xff0c;需要严格的访客管理制度。 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 客户痛点&#…

[NAND Flash 5.2] SLC、MLC、TLC、QLC、PLC NAND_闪存颗粒类型

依公知及经验整理&#xff0c;原创保护&#xff0c;禁止转载。 专栏 《深入理解NAND Flash》 <<<< 返回总目录 <<<< 前言 闪存最小物理单位是 Cell, 一个Cell 是一个晶体管。 闪存是通过晶体管储存电子来表示信息的。在晶体管上加入了浮动栅贮存电子…