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

news2024/11/24 7:42:07

命令模式

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

一、概述

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

二、经典举例

  • 遥控器

    在这里插入图片描述

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

  • 点餐

    在餐厅里点餐

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

  • 小结

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

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

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

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

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

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

三、代码示例(Go)

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

package main

import "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() 方法即可。

    • 远程执行

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

    • 其他

四、总结

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

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

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

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

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

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

相关文章

树形结构的快速生成

背景 相信大家都遇到过树形结构,像是文件列表、多级菜单、评论区的设计等等,我们都发现它有很多层级,第一级可以有多个,下边的每一个层级也可以有多个;有的可以设计成无限层级的,有的只能设计成两级。那么…

工程师使用IT服务台软件可以解决哪些问题?

现如今企业数字化建设已初具规模,业务系统基本已告一段落,而下一步关注的重点则从技术转向管理,如何能让这些系统更好运行起来,如何提高管理效率已是重中之重。在此向您推荐一款高效的IT服务管理工具——ServiceDesk Plus&#xf…

elementui的el-tabs标签页样式修改

一、官网样式: 二、修改样式 1.去掉下划线 效果: 代码: /* 去掉tabs标签栏下的下划线 */ ::v-deep .el-tabs__nav-wrap::after {position: static !important;/* background-color: #fff; */ } 2.改变下划线颜色 效果: 代码:…

使用VisualStudio制作上位机(三)

文章目录 使用VisualStudio制作上位机(三)第三部分:GUI内部函数设计使用VisualStudio制作上位机(三) Author:YAL 第三部分:GUI内部函数设计 这一部分,主要实现CAN设备的打开 将CAN厂家的二次开发文件添加到工程里调用相关函数打开或关闭CAN首先,添加“类文件”,类主…

死锁的典型情况、产生的必要条件和解决方案

前言 死锁:多个线程同时被阻塞,他们中的一个或全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。 目录 前言 一、死锁的三种典型情况 (一)一个线程一把锁 (二)…

聊一聊a_bogus

前言 可以关注我哟,一起学习,主页有更多练习例子 如果哪个练习我没有写清楚,可以留言我会补充 如果有加密的网站可以留言发给我,一起学习共享学习路程 如侵权,联系我删除 此文仅用于学习交流,请勿于商用&a…

保护隐私为先的话,最好是不登录用ChatGPT,6种方法助你轻松接入-纯分享

ChatGPT是OpenAI研发的强大AI语言模型,用户可以通过它进行有意义的对话,并获取问题解答。但是,一些用户可能更倾向于在不需要创建账号或不登录的情况下使用ChatGPT。在这篇指南中,我们将探讨各种无需账号即可访问ChatGPT的方法。无…

续二:《你的医书是假的!批评付施威的《DDD诊所——聚合过大综合症》

DDD领域驱动设计批评文集 “软件方法建模师”不再考查基础题 《软件方法》各章合集 我写了一篇文章,批评付施威的《DDD诊所——聚合过大综合症》(以下简称《DDD诊所》),文章是《你的医书是假的!批评付施威的《DDD诊…

【AI模型】Windows端深度学习环境配置

😏★,:.☆( ̄▽ ̄)/$:.★ 😏 这篇文章主要介绍Windows端深度学习环境配置。 学其所用,用其所学。——梁启超 欢迎来到我的博客,一起学习,共同进步。 喜欢的朋友可以关注一下,下次更新不…

zhm_real/MotionPlanning运动规划库中A*算法源码详细解读

本文主要对zhm_real/MotionPlanning运动规划库中A*算法源码进行详细解读,即对astar.py文件中的内容进行详细的解读,另外本文是 Hybrid A * 算法源码解读的前置文章,为后续解读Hybrid A * 算法源码做铺垫。 astar.py文件中的源码如下&#xff…

python pipenv环境部署django项目实践

将代码上传到服务器: 安装pipenv: pip3 install pipenv 安装项目虚拟环境: cd /www/wwwroot/python-django pipenv install 如果提示python版本问题,修改Pipfile文件内的python版本即可。 然后进入虚拟环境安装依赖包&#x…

Java学数据结构(1)——抽象数据类型ADT 表List、栈Stack和队列Qeue

目录 引出抽象数据类型(abstract data type,ADT)表ListArrayList,Vector, LinkedListArrayList手动实现与分析Vector的分析(线程安全)LinkedList 的手动实现与分析 栈stack—后进先出java中stack源码分析栈的应用:检查…

Android 市场的变化,影响多少开发被迫……

前言 Android 开发在2010年时,广受市场需要,那时候在一线城市很容易拿到10K的起步薪资,Android开发的市场空缺大概有30万左右。那时引起了大量java开发者开始学习Android开发,招聘市场面试要求上只要有一定java语法基础&#xff…

使用Nodejs创建简单的HTTP服务器,借助内网穿透工具实现公网访问的方法分享

文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation&#xff0…

淘宝销量采集(淘宝店的销售数据如何获取),淘宝API接口申请指南

淘宝销量采集是在网络商业流程中的一个非常重要的环节,可以帮助商家更好地了解市场的消费状况和竞争情况。通过对淘宝商品的销量数据进行采集与分析,商家可以有效地了解市场上的热销商品、竞争情况、市场价格趋势等重要信息,从而制定相应的营…

【DevOps视频笔记】4.Build 阶段 - Maven安装配置

一、Build 阶段工具 二、Operate阶段工具 三、服务器中安装 四、修改网卡信息 五、安装 jdk 和 maven Stage1 : 安装 JDK Stage 2 : 安装 Maven 2-1 : 更换文件夹名称 2-2 : 替换配置文件 settings.xml- 2-3 : 修改settings.xml详情 A. 修改maven仓库地址 - 阿里云 B…

RISC-V IOPMP实际用例-Andes SoC‘s Rapid-k模型

安全之安全(security)博客目录导读 2023 RISC-V中国峰会 安全相关议题汇总 说明:本文参考RISC-V 2023中国峰会如下议题,版权归原作者所有。

Tomcat漏洞复现+哥斯拉利用

Tomcat 8 启动tomcat8漏洞 将jsp木马保存到zip压缩包中&#xff0c;再修改后缀名为war上传 <%page import"java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){retu…

prometheus监控JVM(接入tomcat)

一、整合jmx_exporter及tomcat 1、 jmx_exporter下载地址 https://github.com/prometheus/jmx_exporter/releases 2、 tomcat配置文件下载地址 https://github.com/prometheus/jmx_exporter/blob/main/example_configs/tomcat.yml 3、创建tomcat_exporter目录 [rootlocalhost ~…

怎么快速生成gif?三步教你一键生成gif动图

相信大家都有使用过表情包吧&#xff0c;那么自己怎么将图片转gif动图表情包呢&#xff0c;其实很简单&#xff0c;只要使用这款在线gif制作&#xff08;https://www.gif.cn&#xff09;工具&#xff0c;就可以一键生成gif动图&#xff0c;下面一起来看看。 打开网站&#xff…