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

C# 设计模式之命令模式

总目录


前言

命令模式在日常中,也是比较常见的,就比如:妈妈和爸爸说,你去让孩子把地扫一下;这就是是一个命令,命令中的 下达命令的是妈妈,传达命令的是爸爸,接受命令做事的是孩子;那为什么还要爸爸传达呢?直接去让孩子做事不是更直接,这个好回答,就是因为妈妈省时省力啊!


1 基础介绍

  1. 定义:将一个请求封装为一个对象,从而使你可用不同的请求对客户(客户程序,也是行为的请求者)进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

命令模式属于对象的行为型模式。命令模式是把一个操作或者行为抽象为一个对象,通过对命令的抽象化来使得发出命令的责任和执行命令的责任分隔开。命令模式的实现可以提供命令的撤销和恢复功能。

  1. 命令模式,顾名思义就是将命令抽象化,然后将请求者和接收者通过命令进行绑定。而命令的请求者只管下达命令,命令的接收者只管执行命令。从而实现了解耦,请求者和接受者二者相对独立。
  2. 命令模式通过加入命令请求者角色来实现 将发出命令的责任和执行命令的责任分割开,并协助发出命令者来传达命令,使得执行命令的接收者可以收到命令并执行命令。
  3. 命令模式的目的是解除命令发出者和接收者之间的紧密耦合关系,使二者相对独立,有利于程序的并行开发和代码的维护。
  4. 命令模式中的角色:
    • 客户角色(Client):创建具体的命令对象,并且设置命令对象的接收者。注意这个不是我们常规意义上的客户端,而是在组装命令对象和接收者,或许,把这个Client称为装配者会更好理解,因为真正使用命令的客户端是从Invoker来触发执行。(主要是负责将执行者和命令类进行绑定,其次将命令类和调用者进行绑定。使其调用者可以通过命令类给执行者传递消息进行执行。)

    • 命令角色(Command):声明了一个给所有具体命令类实现的抽象接口。

    • 具体命令角色(ConcreteCommand):命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。

    • 请求者角色(Invoker):要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。

    • 接受者角色(Receiver):接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。

2 使用场景

(1)、系统需要支持命令的撤销(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo方法把命令所产生的效果撤销掉。命令对象还可以提供redo方法,以供客户端在需要时,再重新实现命令效果。

(2)、系统需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命周期。意思为:原来请求的发出者可能已经不存在了,而命令对象本身可能仍是活动的。这时命令的接受者可以在本地,也可以在网络的另一个地址。命令对象可以串行地传送到接受者上去。

(3)、如果一个系统要将系统中所有的数据消息更新到日志里,以便在系统崩溃时,可以根据日志里读回所有数据的更新命令,重新调用方法来一条一条地执行这些命令,从而恢复系统在崩溃前所做的数据更新。

(4)、系统需要使用命令模式作为“CallBack(回调)”在面向对象系统中的替代。Callback即是先将一个方法注册上,然后再以后调用该方法。

3 实现方式

下面我们以一个案例,循序渐进的来了解命令模式。
案例:有了小家庭里,有妈妈和爸爸,还有两个孩子,一个叫童童,一个叫睦睦
现在 妈妈准备做饭了,家里没有了,让 童童 去买瓶油回来!

1. 娃儿买东西V1.0

需求:妈妈让 童童 买油

    public class TongTong{public void BuyOil(){Console.WriteLine("童童买好油了!");}}

客户端就相当于妈妈的角色

        static void Main(string[] args){TongTong tt = new TongTong();tt.BuyOil();Console.ReadKey();}

需求变了,现在妈妈发现家里没有可乐了,让睦睦 去买可乐

    public class MuMu{public void BuyCola(){Console.WriteLine("睦睦买好可乐了!");}}

现在我们需求变了,我们新增了一个类,并且还需将客户端的代码改一下

        static void Main(string[] args){TongTong tt = new TongTong();tt.BuyOil();MuMu mm = new MuMu();mm.BuyCola();Console.ReadKey();}

我们发现只要我们需求发生一点改变,就需要改动代码,并且客户端直接调用实现类,耦合度太高了,这个违背了依赖倒置和开闭原则。

2. 娃儿买东西V2.0

我们发现在这个事件中,妈妈不管叫 童童还是睦睦 去买东西,去买东西的人和买的物品会发生变化。
不变的是买东西 这个命令是 他们都需要遵守的,因此我们将命令封装起来。

 //定义一个命令的抽象类public abstract class AbstractCommand{//规定实现类必须实现的方法//该方法表示 执行 具体的命令public abstract void Execute();}public class TongTongCommand : AbstractCommand{private TongTong _TongTong;public TongTongCommand(TongTong tong){_TongTong = tong;}public override void Execute(){_TongTong.BuyOil();}}public class MuMuCommand : AbstractCommand{private MuMu _MuMu;public MuMuCommand(MuMu muMu){_MuMu = muMu;}public override void Execute(){_MuMu.BuyCola();}}

客户端就相当于妈妈的角色,此时我们需要实现让童童买油,让睦睦买可乐的需求,可以这样实现。

        static void Main(string[] args){//童童买水TongTong tongTong = new TongTong();AbstractCommand command = new TongTongCommand(tongTong);command.Execute();//睦睦买可乐MuMu muMu = new MuMu();command = new MuMuCommand(muMu);command.Execute();Console.ReadKey();}

通过将命令定义成抽象类,规定实现类必须实现的执行命令的方法,我们实现了不同命令的解耦。
如果此时妈妈 需要童童 去买水,我们可以在TongTong 类中 新增了BuyWater的方法,然后修改TongTongCommand类即可。

3. 娃儿买东西V3.0

但是还有一个问题,每次要买点啥,都得妈妈亲自过来和童童或睦睦说,太浪费时间了,于是和一旁帮厨的爸爸说,一会儿我缺什么要买的,我就给你说下,至于你叫谁去我不管了,给我买回来就行!

    public class BaBa{private AbstractCommand _AbstractCommand;public BaBa(AbstractCommand command){_AbstractCommand = command;}//告知相关的命令public void Invoke() {_AbstractCommand.Execute();}}

此时于爸爸而言,不管执行,只管将妈妈的命令传达出去,你们命令中对应的人去做该做的事情就行。
此时与妈妈而言,不管过程,也不管谁去执行,只管告诉爸爸 一个命令(需要谁去做什么)就行。
与孩子而言,只管执行!

客户端调用:

        static void Main(string[] args){var tongTong = new TongTong();var command = new TongTongCommand(tongTong);BaBa baBa = new BaBa(command);baBa.Invoke();Console.ReadKey();}

4. 代码汇总

    public class TongTong{public void BuyOil(){Console.WriteLine("童童买好油了!");}}public class MuMu{public void BuyCola(){Console.WriteLine("睦睦买好可乐了!");}}//定义一个命令的抽象类public abstract class AbstractCommand{//规定实现类必须实现的方法//该方法表示 执行 具体的命令public abstract void Execute();}public class TongTongCommand : AbstractCommand{private TongTong _TongTong;public TongTongCommand(TongTong tong){_TongTong = tong;}public override void Execute(){_TongTong.BuyOil();}}public class MuMuCommand : AbstractCommand{private MuMu _MuMu;public MuMuCommand(MuMu muMu){_MuMu = muMu;}public override void Execute(){_MuMu.BuyCola();}}public class BaBa{private AbstractCommand _AbstractCommand;public BaBa(AbstractCommand command){_AbstractCommand = command;}//告知相关的命令public void Invoke() {_AbstractCommand.Execute();}}

客户端调用:

        static void Main(string[] args){var tongTong = new TongTong();var command = new TongTongCommand(tongTong);BaBa baBa = new BaBa(command);baBa.Invoke();Console.ReadKey();}

后续有什么命令,那就新增一个命令实现类即可,实现了命令发起者 与 接收者之间的解耦。

4 优缺点分析

  • 优点:

    • 命令模式使得新的命令很容易被加入到系统里。
    • 可以设计一个命令队列来实现对请求的Undo和Redo操作。
    • 可以较容易地将命令写入日志。
    • 可以把命令对象聚合在一起,合成为合成命令。合成命令式合成模式的应用。
    • 降低了系统的耦合性。将调用操作对象和知道如何实现该操作对象的解耦。
  • 缺点:

    • 使用命令模式可能会导致系统有过多的具体命令类。这会使得命令模式在这样的系统里变得不实际。

结语

希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。


参考资料:
C#设计模式之十四命令模式(Command Pattern)【行为型】
C#设计模式(15)——命令模式(Command Pattern)
c#中命令模式详解

相关文章:

C# 设计模式之命令模式

总目录 前言 命令模式在日常中,也是比较常见的,就比如:妈妈和爸爸说,你去让孩子把地扫一下;这就是是一个命令,命令中的 下达命令的是妈妈,传达命令的是爸爸,接受命令做事的是孩子&a…...

pod详解 list-watch机制 预选优选策略 如何指定节点调度pod

K8S是通过 list-watch 机制实现每个组件的协同工作 controller-manager、scheduler、kubelet 通过 list-watch 机制监听 apiserver 发出的事件,apiserver 也会监听 etcd 发出的事件 scheduler的调度策略: 预选策略(Predicates)…...

深入探索:【人工智能】、【机器学习】与【深度学习】的全景视觉之旅

目录 第一部分:人工智能、机器学习与深度学习概述 1.1 人工智能的概念与发展 代码示例:简单的AI决策系统 1.2 机器学习的定义与分类 代码示例:简单的线性回归模型 1.3 深度学习的基础与应用 代码示例:构建简单的神经网络 …...

使用js和css 实现div旋转围绕圆分布排列

记录,以防忘记 围绕圆 import React, { useEffect } from react; import ./index.scoped.scss;const Test () > {const arr Array.from({ length: 28 }, (_, index) > index 1);useEffect(() > {const dayTotal arr.length;// 动态设置每个点的旋转角…...

SQL Server中CPU使用率过高的排查

CPU使用率过高有许多可能原因,但以下原因最为常见: 1.由于以下情况,表或索引扫描导致的高逻辑读取: 过期统计信息 缺少索引 参数敏感计划 (PSP) 问题 设计不佳的查询 2.工作负荷增加 对于安装了sqlserver的服务器,可…...

AUTOSAR AP常用文档前缀

AUTOSAR AP常用文档前缀总结如下表: 缩写全称含义EXPExplanation文档类别,跟踪类别 讨论其他文件中已经显示的内容的说明材料MODModel文档类别,跟踪类别 元级别1(模型)上的建模内容(模型或从模型生成的内容)RSRequirement Specification文档…...

服务器迁移基于Tomcat部署的java应用,没有源码怎么办?

文章目录 反编译创建java工程编译新的数据库配置类DbUtilclass文件替换到Tomcat配置的应用路径 docBase背景:非国产化项目服务器审计不通过,需要迁移到外部公司。由于项目是第三方公司开发,丢失java项目源码。 部署环境:Tomcat7,JDK1.8 涉及JAVA项目的有两个服务,一个电台…...

kafka-go使用:以及kafka一些基本概念说明

关于kafka 作为开发人员kafka中最常关注的几个概念,是topic,partition和group这几个概念。topic是主题的意思,简单的说topic是数据主题,这样解释好像显得很苍白,只是做了个翻译。一图胜前言,我们还是通过图解来说明。…...

景联文科技:破解数据标注行业痛点,引领高质量AI数据服务

数据标注行业是人工智能和机器学习领域中一个非常重要的组成部分。随着AI技术的发展,对高质量标注数据的需求也在不断增长。 数据标注市场的痛点 1. 团队管理 在众包和转包模式下,管理大量的标注人员是一项挑战。 需要确保标注人员的专业性、稳定性和…...

C#获取Network的相关信息

1,获取网络的通断。 //方法1:无效果,并不能反映当前网络通断 bool availableSystem.Windows.Forms.SystemInformation.Network//方法2:通过VB获取网络状态,可反映当前网络通断 Microsoft.VisualBasic.Devices.Network…...

Jenkins 部署Vue项目指引: Vue项目本地跨域代理 、解决ERR_UNSAFE_PORT

文章目录 引言I Jenkins 部署Vue项目配置插件安装系统配置NodeJS安装目录和别名设置新建任务(通用类型)构建环境Build Steps(构建步骤)II nginx部署站点(端口和站点目录的映射)查找Nginx配置文件端口和站点目录的映射III Vue项目本地跨域代理,屏蔽掉后端服务API的网关IP…...

C语言电子画板

目录 开头程序程序的流程图程序的效果结尾 开头 大家好&#xff0c;我叫这是我58。今天&#xff0c;我们来看一下我用C语言编译的电子画板和与之相关的一些东西。 程序 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <Windows.h> int main() …...

Android Gradle开发与应用技术原理

Android Gradle开发与应用技术原理 Android Gradle开发与应用技术原理一、概述二、Gradle构建原理1. Gradle架构2. Gradle构建过程3. 构建脚本 三、Gradle插件机制四、在Android应用中实现Text-to-Speech&#xff08;TTS&#xff09;功能1. 配置Gradle依赖2. 实现TTS功能示例代…...

Midjourney入门-提示词基础撰写与公式

​ 前言 在前几篇教程里我们已经可以初步使用Midjourney进行出图了。 包括也了解了Midjourney的指令与参数。 但如果你想用Midjourney去生成各种各样高质量的图片&#xff0c; 并且生成的图片是你想要的画面内容&#xff0c;也就是更好控制生成图片的画面内容与风格&#xf…...

Apache Tomcat服务器版本号隐藏

渗透测试时发现有一台服务器的404报错页面中&#xff0c;有Apache Tomcat的版本号信息显示&#xff0c;发生了信息泄露&#xff0c;可能导致服务器被攻击。如下所示&#xff1a; 解决步骤如下&#xff1a; 1. 隐藏HTTP响应头中的Server信息 Tomcat默认会在HTTP响应头中包含S…...

【Qt】Qt编程注意事项

目录 Qr中的命名规范 Qt Creator中的快捷键 查询文档的方式 Qt窗口坐标体系 Qr中的命名规范 在学习编程语言阶段&#xff0c;给变量、函数、文件、类命名是非常有讲究的。 命名要有描述性&#xff0c;不要使用abc&#xff0c;xyz这种比较无规律的名字类描述。如果名字比较…...

在Linux系统安装Kafka

注意&#xff1a;我的是在云服务器上基于Docker配 在防火墙上放行端口号 2181(Zookeeper) 9092(Kafka) 一、先配置 Docker 守护进程&#xff08;daemon&#xff09;的镜像加速器&#xff08;registry mirrors&#xff09; sudo mkdir -p /etc/docker sudo tee /etc/docker/da…...

【CSharp】简单定义一个异步方法

【CSharp】定义一个异步方法 1.背景2.异步方法3.代码说明1.背景 相关博客: 【CSharp】使用异步事件处理程序和委托来进行异步调用 https://blog.csdn.net/jn10010537/article/details/140898179在 C# 中,异步方法和同步方法是两种执行代码的方式, 它们主要区别在于处理任务…...

贪心算法之货仓选址问题

#include<stdio.h> #include<stdlib.h> #include<math.h>//贪心算法之货仓选址问题/*** void* p是万能指针&#xff0c;可以和其它任意类型的指针进行转换&#xff0c;前提是确保转换是合法的*/ //写好用于qsort的比较函数&#xff0c;这里写的函数一般用于…...

Java网络编程——Request Response 对象

Response - 网页 上一章我们学习了 Java 中使用 Okhttp3 库请求网页或调用 API 的知识。 使用一条语句执行调用请求&#xff0c;并取得返回结果字符串&#xff1a; call.execute().body().string()execute() 方法是真正执行发送请求&#xff0c;前面的一系列代码是做前置准备…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案

这个问题我看其他博主也写了&#xff0c;要么要会员、要么写的乱七八糟。这里我整理一下&#xff0c;把问题说清楚并且给出代码&#xff0c;拿去用就行&#xff0c;照着葫芦画瓢。 问题 在继承QWebEngineView后&#xff0c;重写mousePressEvent或event函数无法捕获鼠标按下事…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分&#xff1a; 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...

springboot 日志类切面,接口成功记录日志,失败不记录

springboot 日志类切面&#xff0c;接口成功记录日志&#xff0c;失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...