当前位置: 首页 > news >正文

设计模式大白话——命令模式

命令模式

    • 一、概述
    • 二、经典举例
    • 三、代码示例(Go)
    • 四、总结

一、概述

​ 顾名思义,命令模式其实和现实生活中直接下命令的动作类似,怎么理解这个命令是理解命令模式的关键!!!直接说结论是很不负责的行为,因此我将会结合之后的例子来向你介绍它,来帮助你更好的理解,而不是仅仅死记硬背它。这样你会在以后需要的时候想起它并且通过这个命令模式帮助你解决问题。

二、经典举例

  • 遥控器

    在这里插入图片描述

​ 现在你手里有一个遥控器,上面有很多的按钮,可以对应到生活中很对例子,如:电视遥控器、空调遥控器等等,使用起来非常的简单,我们只需要按下对应功能的按钮就可以,不需要知道怎么做的,比如说开关空调,调整温度等等。在这个场景中,按下按钮其实就会给设备发送一个命令,请先记住这个场景下的命令,后面会用到!!

  • 点餐

    在餐厅里点餐

    ​ 你到餐厅点餐,服务员会将菜单递给你,然后你会挑选菜单上的菜,然后服务就会记下来交给厨师,厨师收到后就会开始准备这些菜。在这个过程中,也是存在命令的,可选择的命令来自于菜单,然后你下达的命令会由服务员记录在小本本上然后传达给做菜的厨师。

  • 小结

    ​ 说完上面两个场景,现在我们来总结一下,这两个场景中的命令有什么共性。在你继续往下查看结果之前,我建议你先自己思考一下,然后再去下面的结果。

    ​ 有一天,你突然心血来潮想通过遥控器控制空调放音乐,这可能就没办法做到了,为什么呢?因为你手中的遥控器上就没有这个命令呀。同样的道理,你有一天去平日里最爱的餐厅里想点一道满汉全席,服务员看了直摇头,他不知所措了,为啥呢,因为菜单上没有这道菜。

    ​ 通过上面的假设,我想你应该知道了这个命令的特点了:

    这些 “命令” 都是提前预设好的,因此数量也是有限的,且无法做到每个命令都很灵活

    命令其实就是这样也应该这样,它不是一个模糊的东西,它的含义非常明确且简单。

    当然,你如果有其他的理解,也希望你能够在评论区不吝分享。

三、代码示例(Go)

​ 示例代码主要是围绕命令所做的抽象,命令的方法应该尽可能的减少入参或者没有入参。下面我们就遥控器的场景来书写示例代码:

package mainimport "fmt"// Command 命令对像
type Command interface {Execute()
}// TurnOnLightCommand 开灯命令
type TurnOnLightCommand struct {Light *Light
}// Execute 执行命令
func (c TurnOnLightCommand) Execute() {c.Light.On()
}// TurnOffLightCommand 关灯命令
type TurnOffLightCommand struct {Light *Light
}func (c TurnOffLightCommand) Execute() {c.Light.Off()
}// TurnOnFanCommand 开风扇命令
type TurnOnFanCommand struct {Fan *Fan
}func (c TurnOnFanCommand) Execute() {c.Fan.On()
}// TurnOffFanCommand 关风扇命令
type TurnOffFanCommand struct {Fan *Fan
}func (c TurnOffFanCommand) Execute() {c.Fan.Off()
}// ConcreteCommand 宏命令,可以执行多个命令
type ConcreteCommand struct {Commands []Command
}func (c ConcreteCommand) Execute() {for _, command := range c.Commands {command.Execute()}
}// Light 灯
type Light struct {Name string
}func (l Light) On() {fmt.Println(l.Name + " on")
}func (l Light) Off() {fmt.Println(l.Name + " off")
}// Fan 风扇
type Fan struct {Name string
}func (f Fan) On() {fmt.Println(f.Name + " on")
}func (f Fan) Off() {fmt.Println(f.Name + " off")
}// SimpleRemoteControl 简单的遥控器,只有一个按钮
type SimpleRemoteControl struct {Commands Command
}// ButtonWasPressed 按钮被按下
func (rc SimpleRemoteControl) ButtonWasPressed() {rc.Commands.Execute()
}// NormalRemoteControl 普通的遥控器,有多个按钮
type NormalRemoteControl struct {Commands []Command
}// ButtonWasPressed 按钮被按下
func (rc NormalRemoteControl) ButtonWasPressed(index int) {rc.Commands[index].Execute()
}func main() {// 简单的遥控器fmt.Println("简单的遥控器")light := Light{Name: "Living Room"}remoteControl := SimpleRemoteControl{Commands: TurnOnLightCommand{Light: &light}}remoteControl.ButtonWasPressed()// 普通的遥控器fmt.Println("普通的遥控器")fan := Fan{Name: "Living Room"}normalRemoteControl := NormalRemoteControl{Commands: []Command{TurnOnLightCommand{Light: &light}, TurnOffLightCommand{Light: &light}, TurnOnFanCommand{Fan: &fan}, TurnOffFanCommand{Fan: &fan}}}normalRemoteControl.ButtonWasPressed(0)normalRemoteControl.ButtonWasPressed(1)normalRemoteControl.ButtonWasPressed(2)normalRemoteControl.ButtonWasPressed(3)// 通过宏,让一个按钮可以执行多个命令fmt.Println("通过宏,让一个按钮可以执行多个命令")normalRemoteControl.Commands = []Command{ConcreteCommand{Commands: []Command{TurnOnLightCommand{Light: &light}, TurnOnFanCommand{Fan: &fan}}}, ConcreteCommand{Commands: []Command{TurnOffLightCommand{Light: &light}, TurnOffFanCommand{Fan: &fan}}}}normalRemoteControl.ButtonWasPressed(0)
}
  • 分析

    ​ 代码中最核心的是抽象了 Command ,通过这个接口的 Execute() 方法不关心命令究竟是如何执行的,毕竟它仅仅只是命令而已。

    ​ 有了这个接口,我们就可以在此基础之上拓展很多新的应用场景出来,就比如示例中的宏命令——由几个命令组合而成,除此之外,还有可以有很多其他拓展,希望你们能够自己亲自去实现,这样能够提升自己的理解。

    可拓展的应用场景:

    • 撤销/回滚功能

      这个很好理解,其实就是在 Command 接口中增加一个 Undo() 方法,然后再把执行过的命令放入栈中,进行命令回滚时,只需要执行出栈命令的 Undo() 方法即可。

    • 远程执行

      既然命令已经被抽象成了对象,那么也就和对象一样可以被序列化(变成可传输的字符串)然后传输到远端去或者持久化到数据库中。

    • 其他

四、总结

​ 其实命令模式并不难,最最最核心的正入它的名字一样,是对业务 “命令” 的抽象,因此也有些地方把这个设计模式成为 调用封装

​ 有了上面的理解后我们来体会一下此模式的定义,相信你会有更深刻的理解:

命令模式将 “请求” 封装成对象,以便使用不同的请求、队列或日志来参数化其他对象。命令模式支持可撤销的操作

​ 以上,便是此文章的全部内容了,希望你能有收获,如果内容存在错误的地方,也欢迎指出

相关文章:

设计模式大白话——命令模式

命令模式 一、概述二、经典举例三、代码示例(Go)四、总结 一、概述 ​ 顾名思义,命令模式其实和现实生活中直接下命令的动作类似,怎么理解这个命令是理解命令模式的关键!!!直接说结论是很不负责…...

[线程/C++(11)]线程池

文章目录 一、C实现线程池1. 头文件2. 测试部分 二、C11实现线程池1. 头文件2. 测试部分 一、C实现线程池 1. 头文件 #define _CRT_SECURE_NO_WARNINGS #pragma once #include<iostream> #include<string.h> #include<string> #include<pthread.h> #…...

VR防地质灾害安全教育:增强自然灾害知识,提高自我保护意识

VR防地质灾害安全教育系统是一种虚拟仿真技术&#xff0c;可以通过虚拟现实技术模拟地震、泥石流、滑坡等地质灾害的发生和应对过程&#xff0c;帮助人们提高应对突发自然灾害的能力。这种系统的优势在于可以增强自然灾害知识&#xff0c;提高自我保护意识&#xff0c;锻炼人们…...

Mybatis多对多查询案例!

在MyBatis中执行多对多查询需要使用两个主要表和一个连接表&#xff08;通常称为关联表&#xff09;来演示。在这个示例中&#xff0c;我们将使用一个示例数据库模型&#xff0c;其中有三个表&#xff1a;students、courses 和 student_courses&#xff0c;它们之间建立了多对多…...

Android OpenCV(七十五): 看看刚”转正“的条形码识别

前言 2021年,我们写过一篇《OpenCV 条码识别 Android 平台实践》,当时的条形码识别模块位于 opencv_contrib 仓库,但是 OpenCV 4.8.0 版本开始, 条形码识别模块已移动到 OpenCV 主仓库,至此我们无需自行编译即可轻松地调用条形码识别能力。 Bar code detector and decoder…...

数据结构——布隆计算器

文章目录 1.什么是布隆过滤器&#xff1f;2.布隆过滤器的原理介绍3.布隆过滤器使用场景4.通过 Java 编程手动实现布隆过滤器5.利用Google开源的 Guava中自带的布隆过滤器6.Redis 中的布隆过滤器6.1介绍6.2使用Docker安装6.3常用命令一览6.4实际使用 1.什么是布隆过滤器&#xf…...

金融学复习博迪(第6-9章)

第6章 投资项目分析 学习目的&#xff1a;解释资本预算&#xff1b;资本预算基本法则 资本预算过程包含三个基本要素&#xff1a; 一提出针对投资项目的建议 一对这些建议进行评价 一决定接受和拒绝哪些建议 6.1项目分析的特性 资本预算的过程中的基本单位是单个的投资项目。投…...

解决idea登录github copilot报错问题

试了好多方案都没用&#xff0c;但是这个有用&#xff0c; 打开idea-help-edit custonm vm options 然后在这个文件里面输入 -Dcopilot.agent.disabledtrue再打开 https://github.com/settings/copilot 把这个设置成allow&#xff0c;然后重新尝试登录copilot就行就行 解决方…...

什么是Flex布局?请列举一些Flex布局的常用属性。

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ Flex布局&#xff08;Flexible Box Layout&#xff09;⭐ Flex布局的常用属性⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之…...

React + TypeScript + antd 常见开发场景

时间戳转格式 // 获取当前时间戳&#xff08;示例&#xff09; const timestamp Date.now(); // 或者使用特定的时间戳值// 创建一个新的Date对象&#xff0c;并传入时间戳 const date new Date(timestamp);// 获取年、月、日的值 const year date.getFullYear(); const mon…...

前端基础踩坑记录

前言&#xff1a;在做vue项目时&#xff0c;有时代码没有报错&#xff0c;但运行时却各种问题&#xff0c;没有报错排查起来就很费劲&#xff0c;本人感悟&#xff1a;写前端&#xff0c;需要好的眼神&#xff01;&#xff01;&#xff01;谨以此博客记录下自己的踩坑点。 一、…...

k8s删除pod镜像没响应marking for deletion pod TaintManagerEviction

使用命令强制删除 Pod的状态为"Marking for deletion"表示该Pod正在被标记为待删除状态&#xff0c;但实际上并没有被删除。这可能是因为以下原因之一&#xff1a; 删除操作被阻塞&#xff1a;可能是由于某些资源或容器正在使用该Pod&#xff0c;导致删除操作被阻塞…...

Nginx 使用 lua-nginx-module 来获取post请求中的request和response信息

如果想要在nginx中打印出 http request 的所有 header&#xff0c;需要在编译nginx时开启 1、安装编译所需的依赖 apt-get install build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev libssl-dev2、创建下载路径 mkdir -p /opt/download3、下载所需的文件 # 不要下载…...

【Opencv】三维重建之cv::recoverPose()函数(1)

官网链接 从估计的本质矩阵和两幅图像中的对应点恢复相机之间的旋转和平移&#xff0c;使用光束法则进行检验。返回通过检验的内点数目。 #include <opencv2/calib3d.hpp>int cv::recoverPose ( InputArray E, InputArray points1, InputArray points2, InputArray …...

Perl兼容正则表达式函数-PHP8知识详解

在php8中有两类正则表达式函数&#xff0c;一类是perl兼容正则表达式函数&#xff0c;另一类是posix扩展正则表达式函数。二者区别不大&#xff0c;我们推荐使用Perl兼容正则表达式函数。 1、使用正则表达式对字符串进行匹配 用正则表达式对目标字符串进行匹配是正则表达式的主…...

Python处理空值NaN

fork_address_tempread_excel_column_to_list(./eqp_info.xls,Sheet1,车辆地址)for i in fork_address_temp:print(type(i))fork_address[0 if address nan else address for address in fork_address_temp]fork_address结果 <class float><class float><class…...

软件机器人助力交通运输局数据录入,实现高效管理

随着科技的迅速发展&#xff0c;许多传统的行业正在寻求通过科技创新优化工作流程、提升效率。在这样的大背景下&#xff0c;交通运输部门也开始注重引入科技手段改善工作流程。博为小帮软件机器人正逐步改变着交通运输局的工作方式。 软件机器人&#xff1a;交通管理的利器 博…...

时序分解 | MATLAB实现基于SGMD辛几何模态分解的信号分解分量可视化

时序分解 | MATLAB实现基于SGMD辛几何模态分解的信号分解分量可视化 目录 时序分解 | MATLAB实现基于SGMD辛几何模态分解的信号分解分量可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 SGMD分解算法&#xff08;辛几何模态分解&#xff09;&#xff0c;分解结果可视…...

FinalShell报错:Swap file “.docker-compose.yml.swp“ already exists

FinalShell中编辑docker-compose.yml文件&#xff0c;保存时报错&#xff1a;Swap file ".docker-compose.yml.swp" already exists&#xff1b;报错信息截图如下&#xff1a; 问题原因&#xff1a;有人正在编辑docker-compose.yml文件或者上次编辑没有保存&#xff…...

卷积过程详细讲解

1&#xff1a;单通道卷积 以单通道卷积为例&#xff0c;输入为&#xff08;1,5,5&#xff09;&#xff0c;分别表示1个通道&#xff0c;宽为5&#xff0c;高为5。假设卷积核大小为3x3&#xff0c;padding0&#xff0c;stride1。 卷积过程如下&#xff1a; 相应的卷积核不断…...

RestClient

什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端&#xff0c;它允许HTTP与Elasticsearch 集群通信&#xff0c;而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级&#xff…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用

1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...