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

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

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

定义

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

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

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

  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;发音为…

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

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

探索 OceanBase 中图数据的实现

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

如何理解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 ( %这里会执…

伐木工 - 华为OD统一考试

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

libexif库介绍

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

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

在机器学习中&#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…

MyBatis 进阶

MyBatis 进阶 复杂CURD返回设置返回类型&#xff1a;resultType返回字典映射&#xff1a;resultMap 多表查询动态SQL使⽤<<if>if>标签<trim\>标签<where\>标签<set\>标签<foreach\>标签 其他打开日志单元测试不污染数据库 复杂CURD 返回设…

Model::unguard()的作用

这是在生成假数据时碰见的&#xff0c;浅查了一下 Model::unguard() 是 Laravel 框架中的一个方法&#xff0c;它的作用是取消对 Eloquent 模型的属性赋值的安全性保护。 在默认情况下&#xff0c;Laravel 的 Eloquent 模型会对属性赋值做一些安全性检查&#xff0c;例如防止…

Java:IO流详解

文章目录 基础流1、IO概述1.1 什么是IO1.2 IO的分类1.3 顶级父类们 2、字节流2.1 一切皆为字节2.2 字节输出流 OutputStream2.3 FileOutputStream类2.3.1 构造方法2.3.2 写出字节数据2.3.3 数据追加续写2.3.4 写出换行 2.4 字节输入流 InputStream2.5 FileInputStream类2.5.1 构…

LeetCode 225.用队列实现栈(详解) ૮꒰ ˶• ༝ •˶꒱ა

题目详情&#xff1a; 思路&#xff1a;1.定义两个队列用于存储栈的数据&#xff0c;其中一个为空。 2.对我们定义的栈进行入数据&#xff0c;就相当于对不为空的队列进行入数据。 3.对我们定义的栈进行删除&#xff0c;相当于取出不为空的队列中的数据放到为空的队列中&#x…

Python基础入门第八课笔记(自定义函数 lambda)

什么时候用lambda表达式&#xff1f; 当函数有一个返回值&#xff0c;且只有一句代码&#xff0c;可以用lambda简写。 2、lanbda语法 lambda 形参 : 表达式 注意&#xff1a; 1、形参可以省略&#xff0c;函数的参数在lambda中也适用 2、lambda函数能接收任何数量的参数但只能…

MySQL之视图内连接、外连接、子查询案例

目录 一.视图 1.1 含义 1.2 操作 二.案例 三.思维导图 一.视图 1.1 含义 虚拟表&#xff0c;查询方面和普通表一样使用。 1.2 操作 1.创建视图&#xff1a; create or replace view 视图名 as 查询语句&#xff1b; 2.视图的修改&#xff1a; 方式1 create or replace view …

了解长短期记忆 (LSTM) 网络:穿越时间和记忆的旅程

一、说明 在人工智能和机器学习的迷人世界中&#xff0c;长短期记忆 (LSTM) 网络作为一项突破性创新脱颖而出。LSTM 旨在解决传统循环神经网络 (RNN) 的局限性&#xff0c;尤其是在学习长期依赖性方面的局限性&#xff0c;彻底改变了我们在各个领域建模和预测序列的能力。本文深…

Nacos与Eureka

一、前言 在构建和管理微服务架构时&#xff0c;选择适当的服务注册中心至关重要。Nacos和Eureka都是微服务体系结构中常用的服务注册和发现工具。本文将探讨它们之间的区别&#xff0c;帮助开发者在选择适合其项目需求的注册中心时做出明智的决策。 二、架构和适用场景 Nacos …

Java/JDK下载安装与环境配置

Java由Sun Microsystems&#xff08;现在是Oracle的子公司&#xff09;于1995年首次发布。它是一种面向对象的编程语言&#xff0c;广泛应用于Web开发、移动应用程序开发、桌面应用程序开发和企业级应用程序开发等领域。 Java语言的主要特点是跨平台、可移植性强、安全性高和具…

【开源】基于JAVA语言的智能教学资源库系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 课程档案模块2.3 课程资源模块2.4 课程作业模块2.5 课程评价模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 课程档案表3.2.2 课程资源表3.2.3 课程作业表3.2.4 课程评价表 四、系统展示五、核心代…

VLM,LLM等大模型如何应用于机器人控制(以强化学习为例)

VLM&#xff1a;视觉语义模型&#xff0c;准确识别图中有什么&#xff0c;处于什么状态&#xff0c;以及不同物体之间的关联。 LLM&#xff1a;语言大模型&#xff0c;可以针对当前的环境&#xff0c;自动生成可执行的任务&#xff0c;或者将人类指令重新分成可执行的子任务。…