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

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

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

定义

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

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

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

  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. 状态模式,状态模式允许对象在内部状态改变时改变其行为,看起来好像修改了其类,状态模式与命令模式在处理对象行为方面有一定的相似性,但它们解决的问题不同,状态模式关注的是对象状态变化时的行为改变,而命令模式则关注于将请求封装为对象以实现解耦和撤销等操作。

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

完!

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

物理与网络安全

物流环境安全 场地选择考虑抗震、承重、防火、防水、供电、空气调节、电磁防护、雷击及静电 场地因素&#xff1a; 自然灾害&#xff0c;社会因素&#xff08;加油站、化工厂&#xff09;&#xff0c;配套条件&#xff08;消防&#xff0c;交通&#xff0c;电力&#xff0c;…

QT项目中添加资源文件和使用qss样式

时间记录&#xff1a;2024/1/6 一、添加使用资源文件 1.1 创建项目并打开项目&#xff08;带ui界面的项目&#xff09; 1.2 使用快捷键Ctrln创建文件&#xff0c;选择"QT"下面的"QT Resource File" 1.3 输入文件名和文件保存路径 1.4 将需要添加的文件…

钡铼网关 只需一台,解锁智慧无人搅拌站系统,绿色又环保

行业需求 为了提高搅拌站的自动化&#xff0c;减少人工繁琐的操作&#xff0c;同时记录物料的增减记录&#xff0c;实现对于物料从进场到出场的全周期管理。 系统介绍 针对搅拌站各个环节的需求大致相同&#xff0c;市场中逐渐流行一整套基本的智慧搅拌站解决方案。各种搅拌站…

水母目标检测数据集VOC格式500张

水母&#xff0c;一种美丽而神秘的海洋生物&#xff0c;以其独特的形态和生态习性而备受瞩目。 水母的体型呈伞状&#xff0c;身体透明&#xff0c;有各种颜色和花纹。它们没有骨骼&#xff0c;身体由胶状物质组成&#xff0c;非常柔软和脆弱。水母通过触手上的刺细胞释放毒素…

哪个洗地机质量好?2024年洗地机推荐

如今&#xff0c;家用洗地机已经成为越来越多家庭的不可或缺之物。然而&#xff0c;在市场上琳琅满目的洗地机品牌和型号中&#xff0c;究竟哪一个性价比高、质量好&#xff0c;很多消费者常常陷入选择困难。为了帮助大家能够更加轻松地购买到适合自己的洗地机&#xff0c;我们…

dotdotdot插件快速实现多行文本的省略

jQuery.dotdotdot 前言 在“css新增文本样式&#xff08;完整&#xff09;”这篇&#xff0c;我们介绍了text-overflow属性省略多余的文本。用text-overflow属性可以直接省略单行文本&#xff0c;但省略多行文本&#xff0c;单独使用CSS是无法实现&#xff0c;今天我们介绍一…

【Linux 内核源码分析】关于Linux内核源码目录结构

Linux内核源码采用树形结构。功能相关的文件放到不同的子目录下面&#xff0c;使程序更具有可读行。 使用Source Insight打开源码&#xff0c;如下图所示&#xff0c;可以看到源码是树形结构。 目录含义描述arch存放与体系结构相关的代码&#xff0c;包括不同硬件平台的特定代…

深度解析分布式算法:构建高效稳定的分布式系统

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

高校选课系统需求分析开发源码

高校学生选课报名系统包括学生、教师和管理员三方的功能需求&#xff0c;学生的需求是查询院系的课程、选课情况及个人信息修改&#xff1b;教师则需要查看和查询所有课程信息及自己的课程信息以及教师信息的修改&#xff1b;管理员则负责更为复杂的任务&#xff0c;包括对学生…

2023年阿里云云栖大会:前沿技术发布与未来展望

在2023年的阿里云云栖大会上&#xff0c;我见证了云计算和人工智能领域的又一历史性时刻。这次大会不仅是对未来科技趋势的一次深入探索&#xff0c;更是阿里云技术实力和创新能力的集中展示。 首先&#xff0c;千亿级参数规模的大模型通义千问2.0的发布&#xff0c;无疑将人工…

【大厂秘籍】系列 - Java多线程面试题

Java多线程面试题 友情提示&#xff0c;看完此文&#xff0c;在Java多线程这块&#xff0c;基本上可以吊打面试官了 线程和进程的区别 进程是资源分配的最小单位&#xff0c;线程是CPU调度的最小单位 线程是进程的子集&#xff0c;一个进程可以有很多线程&#xff0c;每条线…

【MongoDB】MongoDB查询语句find的使用,和提高查询速度的游标的使用,非常详细!!!

&#x1f601; 作者简介&#xff1a;一名大四的学生&#xff0c;致力学习前端开发技术 ⭐️个人主页&#xff1a;夜宵饽饽的主页 ❔ 系列专栏&#xff1a;MongoDB数据库 &#x1f450;学习格言&#xff1a;成功不是终点&#xff0c;失败也并非末日&#xff0c;最重要的是继续前…

MySQL基础篇(三)约束

一、概述 概念&#xff1a;约束是作用于表中字段上的规则&#xff0c;用于限制存储在表中的数据。 目的&#xff1a;保证数据库中数据的正确、有效性和完整性。 分类&#xff1a; 注意&#xff1a;约束是作用于表中字段上的&#xff0c;可以在创建表/修改表的时候添加约束。 二…

Java BIO、NIO、AIO、Netty知识详解(值得珍藏)

1. 什么是IO Java中I/O是以流为基础进行数据的输入输出的&#xff0c;所有数据被串行化(所谓串行化就是数据要按顺序进行输入输出)写入输出流。简单来说就是java通过io流方式和外部设备进行交互。 在Java类库中&#xff0c;IO部分的内容是很庞大的&#xff0c;因为它涉及的领…

【每日一题】在链表中插入最大公约数

文章目录 Tag题目来源解题思路方法一&#xff1a;迭代 写在最后 Tag 【迭代】【辗转相除法】【链表】【2024-01-06】 题目来源 2807. 在链表中插入最大公约数 解题思路 方法一&#xff1a;迭代 思路 首先需要求两个数的最大公约数&#xff0c;使用辗转相除法。实现代码如下…

PyQT 多进程

在PyQt中&#xff0c;图形化界面&#xff08;GUI&#xff09;是运行在主线程中的&#xff0c;而多进程是在独立的进程中执行的。默认情况下&#xff0c;多进程之间是无法直接共享图形化界面的。 然而&#xff0c;有几种方法可以在多进程中与PyQt的图形化界面进行通信&#xff…

【大数据】Zookeeper 客户端的命令行操作

Zookeeper 客户端的命令行操作 1.显示某个路径下的所有节点&#xff1a;ls2.显示某个路径下的所有节点&#xff0c;以及当前节点的详细信息&#xff1a;ls23.创建节点&#xff1a;create4.创建临时节点&#xff1a;create -e5.创建顺序&#xff08;带编号&#xff09;节点&…

程序员副业之无人直播助眠

介绍和概览 大家好&#xff0c;我是小黑&#xff0c;本文给大家介绍一个比较轻松简单的副业&#xff0c;无人直播助眠副业。 这个项目的核心就是通过直播一些助眠素材来赚钱。比如你可以放一些舒缓的雨声之类的&#xff0c;吸引观众进来。然后&#xff0c;咱们可以挂个小程序…

DS|哈希查找

题目一&#xff1a;DS哈希查找 -- 线性探测再散列 题目描述&#xff1a; 定义哈希函数为H(key) key%11&#xff0c;输入表长&#xff08;大于、等于11&#xff09;。输入关键字集合&#xff0c;用线性探测再散列构建哈希表&#xff0c;并查找给定关键字。 输入要求&#xf…

【C语言】Linux实现高并发处理的过程

一、实现高并发的几种策略 C语言本身并没有内建的多线程支持&#xff08;新版C语言支持&#xff0c;但用得不多&#xff09;&#xff0c;但是在多数操作系统中&#xff0c;可以使用库来实现多线程编程。例如&#xff0c;在POSIX兼容系统上&#xff0c;可以使用 pthreads 库来创…