行为型设计模式—命令模式

命令模式:它通过将请求封装为一个独立的对象即命令对象,来解耦命令的调用者和接收者,使得调用者和接收者不直接交互。在命令对象里会包含请求相关的全部信息,每一个命令都是一个操作的请求: 请求方发出请求要求执行一个操作; 接收方收到请求,并执行操作。

命令模式结构:

  • Receiver:命令的接收方,唯一包含业务逻辑的类,命令对象会将请求传递给它,它是请求的最终处理者
  • Command:命令对象,组装了一个Receiver成员,并绑定实现了Receiver的一个特定行为的调用
  • Invoker:请求的发送者,组装了Command成员,通过调用Command实例的execute()方法来触发对应的指令
  • Client:通过将Receiver实例和请求信息传递给Command构造器来创建Command对象,之后会将创建的对象同Invoker绑定。

假设PS5的CPU支持A、B、C三个命令操作,

type CPU struct{}func (CPU) ADoSomething() {fmt.Println("a do something")
}
func (CPU) BDoSomething() {fmt.Println("b do something")
}type PS5 struct {cpu CPU
}func (p PS5) ACommand() {p.cpu.ADoSomething()
}
func (p PS5) BCommand() {p.cpu.ADoSomething()
}
func main() {cpu := CPU{}ps5 := PS5{cpu}ps5.ACommand()ps5.BCommand()
}

后续还可能会给CPU增加其他命令操作,以及需要支持命令宏(即命令组合操作)。如果每次都修改PS5的类定义,显然不符合面向对象开闭原则(Open close principle)的设计理念。

通过命令模式,我们把PS5抽象成命令发送者、CPU对象作为执行业务逻辑的命令接收者,然后引入引入Command 接口把两者做解耦,来满足开闭原则。

type CPU struct{}func (CPU) ADoSomething(param int) {fmt.Printf("a do something with param %v\n", param)
}
func (CPU) BDoSomething(param1 string, param2 int) {fmt.Printf("b do something with params %v and %v \n", param1, param2)
}
func (CPU) CDoSomething() {fmt.Println("c do something with no params")
}// 接口中仅声明一个执行命令的方法 Execute()
type Command interface {Execute()
}// 命令对象持有一个指向接收者的引用,以及请求中的所有参数,
type ACommand struct {cpu *CPUparam int
}
// 命令不会进行逻辑处理,调用Execute方法会将发送者的请求委派给接收者对象。 
func (a ACommand) Execute() {a.cpu.ADoSomething(a.param)a.cpu.CDoSomething()// 可以执行多个接收者的操作完成命令宏
}func NewACommand(cpu *CPU, param int) Command {return ACommand{cpu, param}
}"本文使用的完整可运行源码
去公众号「网管叨bi叨」发送【设计模式】即可领取"
type BCommand struct {state bool // Command 里可以添加些状态用作逻辑判断cpu *CPUparam1 stringparam2 int
}func (b BCommand) Execute() {if b.state {return}b.cpu.BDoSomething(b.param1, b.param2)b.state = trueb.cpu.CDoSomething()
}func NewBCommand(cpu *CPU, param1 string, param2 int) Command {return BCommand{false,cpu, param1, param2}
}type PS5 struct {commands map[string]Command
}// SetCommand方法来将 Command 指令设定给PS5。
func (p *PS5) SetCommand(name string, command Command) {p.commands[name] = command
}
// DoCommand方法选择要执行的命令
func (p *PS5) DoCommand(name string) {p.commands[name].Execute()
}func main() {cpu := CPU{}// main方法充当客户端,创建并配置具体命令对象, 完成命令与执行操作的接收者的关联。ps5 := PS5{make(map[string]Command)}ps5.SetCommand("a", NewACommand(&cpu, 1))ps5.SetCommand("b", NewBCommand(&cpu, "hello", 2))ps5.DoCommand("a")ps5.DoCommand("b")
}

命令模式作用可以类比于一个场景—在市中心逛了很久的街后, 你找到了一家不错的餐厅, 坐在了临窗的座位上。 一名友善的服务员走近你, 迅速记下你点的食物, 写在一张纸上。 服务员来到厨房, 把订单贴在墙上。 过了一段时间, 厨师拿到了订单, 他根据订单来准备食物。 厨师将做好的食物和订单一起放在托盘上。 服务员看到托盘后对订单进行检查, 确保所有食物都是你要的, 然后将食物放到了你的桌上。那张纸就是一个命令, 它在厨师开始烹饪前一直位于队列中。 命令中包含与烹饪这些食物相关的所有信息。 厨师能够根据它马上开始烹饪, 而无需跑来直接和你确认订单详情。

如果你需要通过操作来参数化对象可使用命令模式

  • 命令模式可将特定的方法调用转化为独立对象。 这一改变也带来了许多有趣的应用: 你可以将命令作为方法的参数进行传递、 将命令保存在其他对象中, 或者在运行时切换已连接的命令等。

如果你想要将操作放入队列中操作的执行或者远程执行操作可使用命令模式

  • 同其他对象一样, 命令也可以实现序列化 (序列化的意思是转化为字符串), 从而能方便地写入文件或数据库中。 一段时间后, 该字符串可被恢复成为最初的命令对象。 因此, 你可以延迟或计划命令的执行。 但其功能远不止如此! 使用同样的方式, 你还可以将命令放入队列、 记录命令或者通过网络发送命令。

如果你想要实现操作回滚功能可使用命令模式

  • 为了能够回滚操作, 你需要实现已执行操作的历史记录功能。 命令历史记录是一种包含所有已执行命令对象及其相关程序状态备份的栈结构。这种方法有两个缺点。 首先, 程序状态的保存功能并不容易实现, 因为部分状态可能是私有的。 你可以使用备忘录模式来在一定程度上解决这个问题。

可以同时使用 命令 和 备忘录模式 来实现 “撤销”。 在这种情况下, 命令用于对目标对象执行各种不同的操作, 备忘录用来保存一条命令执行前该对象的状态。

可以将 访问者模式 视为 命令模式 的加强版本, 其对象可对不同类的多种对象执行操作。

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

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

相关文章

CSRF靶场练习

简述:CSRF漏洞实际很少;条件限制很多;局限性很大;实验仅供参考,熟悉csrf概念和攻击原理即可 Pikachu靶场 CSRF GET 登录用户vince的账户可以看到用户的相关信息; 点击修改个人信息,发现数据包…

轻量式RPC调用日志链路设计方案

导语: 调用链跟踪系统,又称为tracing,是微服务设计架构中,从系统层面对整体的monitoring和profiling的一种技术手 背景说明 由于我们的项目是微服务方向,中后台服务调用链路过深,追踪路径过长,其中某个服务报错或者异…

【Redis】实现购物秒杀及分布式锁

Redis实现购物秒杀及分布式锁 全局唯一ID Redis自增ID策略 ID构造是:时间戳 + 计数器 每天一个key,方便统计订单量 业务实现 获取指定时间的秒数 LocalDateTime timeBegin = LocalDateTime.of(2024, 1, 1, 0, 0, 0); long second = timeBegin.toEpochSecond(ZoneOffset…

Redis stream特性了解

在发布订阅中我们了解到发布订阅模式存在的无法持久化保存消息和对于离线重连的客户端不能读取历史消息的缺陷,以下就来了解一下stream是如何解决这个问题的 steam是类似于仅添加log的数据结构,提供了以下基本命令 XADD: 添加新条目到stream # 语法xadd…

Java面试题宝典(万字长文)

Java 基础 1. JDK 和 JRE 有什么区别? JRE是Java运行环境,即(Java Runtime Environment),也就是Java平台。所有的Java程序都要在JRE下才能运行。 JDK是开发工具包,即(Java Development Kit&am…

Python——turtle库笔记②

填充颜色 turtle.fillcolor(colorstring) turtle.fillcolor() turtle.fillcolor((r, g, b)) turtle.fillcolor(r, g, b) 填充 turtle.filling() #返回填充状态 (填充为 True,否则为 False)。 turtle.begin_fill() #在绘制要填充的形状之前调用 turtle.end_fill()…

【Docker与微服务】基础篇

1 Docker简介 1.1 docker是什么 1.1.1 问题:为什么会有docker出现? 假定您在开发一个项目,您使用的是一台笔记本电脑而且您的开发环境具有特定的配置。其他开发人员身处的环境配置也各有不同。您正在开发的应用依赖于您当前的配置且还要依…

数据结构与算法面试系列-02

1. 一个整数,它加上100后是一个完全平方数,加上168又是一个完全平方数,请问该数是多少? 程序分析:在10万以内判断,先将该数加上100后再开方,再将该数加上168后再开方,如果开方后的结果满足如下条件,即是结果。请看具体分析: 程序代码如下: package com.yoodb.uti…

js获取dom元素的宽度数值

有的时候我们需要依赖元素的具体的宽高数值&#xff0c;那这个时候就需要你来进行调用了相关的api进行获取了 这个需要搭配 getComputedStyle getPropertyValue <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8">&…

android 自定义下拉框

一、 简介&#xff1a; 原生Android 提供的spinner下拉框不怎么方便&#xff0c;样式有点丑。修改起来麻烦&#xff0c;于是就自己动手写了一下拉列表。 实现原理使用的是&#xff0c;popwindow弹框&#xff0c;可实现宽高自定义&#xff0c;下拉列表使用listview. 二、pop弹框…

vue3兼容超宽屏、超窄屏、4K屏幕等等

vue3兼容超宽屏、超窄屏、4K屏幕等等 在你的项目src下创建一个hooks文件夹 在里面创建一个 UserScalePage.ts文件 在里面写上 import { onMounted, onUnmounted } from "vue"; import _ from "lodash"; export default function UserScalePage(opstion: …

大模型基础架构的变革:剖析Transformer的挑战者(中)

上一篇文章中&#xff0c;我们介绍了RetNet、RWKV、Mamba等有可能会替代Transformer的模型架构&#xff0c;这一篇文章我们将要介绍另外三个有可能会替代Transformer的模型架构&#xff0c;它们分别是UniRepLKNet、StripedHyena、PanGu-π&#xff0c;想了解之前的内容&#xf…

vue项目改造服务端渲染

vue项目改造服务端渲染 概述 【定义】 服务器渲染的Vue应用程序被认为是"同构"或"通用"&#xff0c;因为应用程序的大部分代码都可以在服务器和客户端上运行 【优点】 与传统SPA相比&#xff0c;服务器端渲染(SSR)的优势主要在于&#xff1a; 1、更好的…

二叉树-堆应用(1)

目录 堆排序 整体思路 代码实现 Q1建大堆/小堆 Q2数据个数和下标 TopK问题 整体思路 代码实现 Q1造数据CreateData Q2建大堆/小堆 建堆的两种方法这里会用到前面的向上/向下调整/交换函数。向上调整&向下调整算法-CSDN博客 堆排序 整体思路 建堆&#xff08;直…

【硬件产品经理】锂电池充电时间怎么计算?

目录 前言 电池容量 充电器功率 电能转换效率 充电时间计算 作者简介<

LeetCode2670. 找出不同元素数目差数组:哈希表(预处理)

【LetMeFly】2670.找出不同元素数目差数组&#xff1a;哈希表&#xff08;预处理&#xff09; 力扣题目链接&#xff1a;https://leetcode.cn/problems/find-the-distinct-difference-array/ 给你一个下标从 0 开始的数组 nums &#xff0c;数组长度为 n 。 nums 的 不同元素…

Qt 5.9.4 转 Qt 6.6.1 遇到的问题总结(三)

1.QSet: toList 中的toList 函数已不存在&#xff0c;遇到xx->toList改成直接用&#xff0c;如下&#xff1a; 2.开源QWT 图形库中QwtDial中的 setPenWidth 变成 setPenWidthF函数。 3.QDateTime 中无setTime_t 改为了setSecsSinceEpoch函数。 4.QRegExp 类已不存在 可以用Q…

【定位·HTML】

定位布局可以分为以下四种&#xff1a; 静态定位&#xff08;inherit&#xff09; 相对定位&#xff08;relative&#xff09; 绝对定位&#xff08;absolute&#xff09; 固定定位&#xff08;fixed&#xff09; 一般的标签元素不加任何定位属性时&#xff0c;默认都属于静态…

百川终入海 ,一站式海量数据迁移工具 X2Doris 正式发布

在大数据分析领域&#xff0c;Apache Doris 作为广受认可的开源实时数据仓库&#xff0c;已经在越来越多行业用户的真实业务场景中得到广泛应用&#xff0c;成为许多企业数据分析基础设施的重要基座。尤其在过去一年多的时间里&#xff0c;越来越多企业选择基于 Apache Doris 进…

node.js(nest.js控制器)学习笔记

nest.js控制器&#xff1a; 控制器负责处理传入请求并向客户端返回响应。 为了创建基本控制器&#xff0c;我们使用类和装饰器。装饰器将类与所需的元数据相关联&#xff0c;并使 Nest 能够创建路由映射&#xff08;将请求绑定到相应的控制器&#xff09;。 1.获取get请求传参…