当前位置: 首页 > 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;前面的一系列代码是做前置准备…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...

华为OD机试-最短木板长度-二分法(A卷,100分)

此题是一个最大化最小值的典型例题&#xff0c; 因为搜索范围是有界的&#xff0c;上界最大木板长度补充的全部木料长度&#xff0c;下界最小木板长度&#xff1b; 即left0,right10^6; 我们可以设置一个候选值x(mid)&#xff0c;将木板的长度全部都补充到x&#xff0c;如果成功…...

libfmt: 现代C++的格式化工具库介绍与酷炫功能

libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库&#xff0c;提供了高效、安全的文本格式化功能&#xff0c;是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全&#xff1a…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !

我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...

Spring Security 认证流程——补充

一、认证流程概述 Spring Security 的认证流程基于 过滤器链&#xff08;Filter Chain&#xff09;&#xff0c;核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤&#xff1a; 用户提交登录请求拦…...

ubuntu22.04 安装docker 和docker-compose

首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...