基本介绍:

  命令模式,顾名思义就是将命令抽象化,然后将请求者和接收者通过命令进行绑定。

  而命令的请求者只管下达命令,命令的接收者只管执行命令。

  从而实现了解耦,请求者和接受者二者相对独立。

  单独理解起来比较困难,咱们还是通过具体实例来说明吧。

举例说明:

  生活中遥控控制电器就是命令模式,比如智能开关控制一盏灯。

  首先将灯的开和关封装成一个命令,然后将这个命令绑定到智能开关上。

  智能开关的执行其实就是灯开关的执行,灯和智能开关是通过命令来进行交互的。

  这个时候如果想控制电视的开关,那就可以将电视的开关封装成一个命令。

  然后将这个命令绑定到智能开关上,智能开关的执行这个时候就变成了电视的开关。

  如果即想控制电视又想控制灯,那就可以将灯的命令和电视的命令都绑定到智能开关上。

  又比如皇帝写了一道圣旨,命令张三即刻回京。

  圣旨跟张三是绑定关系,也就是说张三是实际执行者,圣旨是命令,起到的作用就是跟张三进行绑定和传达信息。

  那想个问题传达圣旨的宦官是谁有关系吗,是没有的,是谁都可以。但又是不可或缺的。它就相当于一个遥控或者说是中间人、传话人。

  是皇帝将这个宦官和圣旨进行了绑定(客户端就是那个皇帝)。那在想个问题,是先有圣旨再有张三呢,还是先有张三再有圣旨。

  答案是肯定的,先有张三,也就是说得先有具体执行者。

  然后再有圣旨即命令,通过名字将张三和圣旨绑定到一起。

  如果皇帝想命令李四也回京,那这个圣旨可以命令李四吗?是不可以的,需要重新写一份圣旨。

  所以从这个不难看出,命令类和具体的执行者是强绑定关系,当然也可以做成工厂。

  这个时候大家想个问题,两道圣旨是不是可以让同一个宦官去传达就可以。因为宦官和执行者不存在绑定,它只和圣旨绑定。

  所以说宦官就相当于遥控,它只需要宣读圣旨而不必在意具体是怎么执行的,就可以让不同的执行人进行不同的操作。

  这就是命令模式的本质。

基本结构:

  通过上述例子不难看出,命令模式主要核心构件有以下几个:

  ◊ 命令的执行者(接收者Receiver):它单纯的只具体实现了功能。(实例中对应的则是张三和李四)

  ◊ 命令的下达者(调用者Invoker)

    就是起到遥控的作用,首先在类中声明抽象命令类的引用,并通过参数注入等方式进行实例化。

    然后通过调用命令对象来告诉执行者执行功能。起到一个传声筒的作用。(实例中对应的是宦官)

  ◊ 抽象命令类Command

    主要作用有两点,其一就是为了规范具体命令类,声明其执行方法。

    其二就是为了方便调用者Invoker调用,使其不需要知道具体命令类,只需要执行抽象命令类即可。(实例中对应的是泛指圣旨)

  ◊ 具体命令类ConcreteCommand

    继承自抽象类,在类中首先声明了执行者的引用,通过参数注入等方式进行创建执行者对象,将命令类和执行者进行绑定。

    其次具体实现了抽象类中声明的执行方法,调用执行者对象中方法进行执行。(实例中对应的是张三和李四具体的两道圣旨)

  ◊ 客户端角色Client

    主要是负责将执行者和命令类进行绑定,其次将命令类和调用者进行绑定。

    使其调用者可以通过命令类给执行者传递消息进行执行。(实例中对应的是皇帝角色)

优缺点:

  优点

    1.降低了系统的耦合性。将调用操作对象和知道如何实现该操作对象的解耦。

    2.通过命令模式,新的命令可以很轻松的加入系统中,且不会影响其他类,符合开闭原则。

  缺点

    使用命令模式可能会导致某些系统存在过多的命令类,会影响使用。

  命令模式适用情形

    1.将用户界面和行为分离。

    2.对请求进行排队,记录请求日志以及支持可撤销的操作等。

具体实例:

  就以皇帝下达圣旨作为实例吧,首先先说下顺序。

    1.得先有执行者也就是接收圣旨的人,它主要是执行具体的行为和功能,如果是遥控控制灯,那这里就是那盏灯本身。这里命名为Receiver_ZhangSan类。

    2.其次就需要创建圣旨,也就是传达命令的媒介。但每道圣旨都是一样的材质和规则,所以需要先创建一个抽象类来规范。这里将抽象类命名为Command类,将具体命令类即圣旨内容命名为ConcreteCommand类它继承自抽象类。

    3.有了执行人也有了圣旨,那就可以安排一个传旨的人就可以了(如果是遥控控制灯,这里相当于遥控)。这里命名为Invoker类。

    4.执行人有了,圣旨有了,传旨的人也有了,那皇帝就可以下令执行了。这里就相当于客户端调用了。

  实现方式说明:

    1.创建Receiver_ZhangSan类,设置具体执行方法,这里可以是骑马回京,可以是其他的操作。它只负责具体功能的实现。

    2.创建Command抽象类,声明Execute方法。

    3.创建ConcreteCommand命令类,继承自抽象类。在该类中创建Receiver_ZhangSan类的引用,并通过参数注入或属性赋值等方式实例化该类,并在Execute方法中具体调用该类中的具体实现方法。

    4.创建Invoker类,在该类中创建Command抽象类的引用,并通过参数注入或属性赋值等方式将ConcreteCommand类实例化,并在该类中创建方法来执行抽象类中的Execute方法。

    5.客户端直接调用Invoker类中的方法来执行命令。

    具体的执行过程就是,通过Invoker类中的方法来调用抽象类中的Execute方法其实就是调用ConcreteCommand中的Execute方法,

    然后ConcreteCommand中的Execute调用的又是Receiver_ZhangSan类中的具体实现方法。

    这样就相当于通过Invoker类调用了Receiver_ZhangSan类中的方法,只不过是中间加了一个传话人即命令类。

  为什么这么做呢?这样做有什么好处呢?

    好处也很好理解,那就是Invoker类不必知道具体执行人是谁,也不必知道具体怎么执行,只需要将命令注入给该类就好了。

    这样如果命令改变了,那直接改变注入的命令类就可以了,方便快捷,而且不需要修改现有的类,符合开闭原则。

    而且还可以按顺序批量执行命令,只需要将命令依次注入给Invoker类进行调用就可以了。即任务队列。

    也可以一次性注入多条命令,同时执行。

 1     /// <summary>
2 /// 张三类,它是实际执行者,也就是命令接收者
3 /// </summary>
4 public class Receiver_ZhangSan
5 {
6 private string strName = "张三";
7 public void Eat()
8 {
9 Console.WriteLine(strName + ":吃饭。");
10 }
11 public void Behavior1()
12 {
13 Console.WriteLine(strName + ":骑马回京。");
14 }
15 public void Behavior2()
16 {
17 Console.WriteLine(strName + ":原地待命。");
18 }
19 }
20
21 /// <summary>
22 /// 命令抽象类,规范命令类,并提供执行方法。
23 /// </summary>
24 public abstract class Command
25 {
26 //执行方法
27 public abstract void Execute();
28 }
29
30 /// <summary>
31 /// 具体命令类,也就是圣旨书写的具体内容。
32 /// </summary>
33 public class ConcreteCommand1 : Command
34 {
35 //创建执行人的引用,这里使用readonly,规定只可以在构造函数中进行赋值。
36 private readonly Receiver_ZhangSan _Receiver_ZhangSan;
37
38 //构造函数,参数注入方式,将执行人注入到命令类中。好比是将人名写在圣旨上。
39 public ConcreteCommand1(Receiver_ZhangSan receiver_ZhangSan)
40 {
41 this._Receiver_ZhangSan = receiver_ZhangSan;
42 }
43
44 //具体命令
45 public override void Execute()
46 {
47 //下达立刻回京的命令
48 this._Receiver_ZhangSan.Behavior1();
49 }
50 }
51
52 /// <summary>
53 /// 调用者类,命令下达者,即宦官。
54 /// </summary>
55 public class Invoker
56 {
57 //创建抽象命令类的引用,这里不同于具体命令类,方便演示,使用了参数赋值的形式进行注入。
58 public Command command { get; set; }
59
60 //下达命令,即宣读圣旨
61 public void Reading()
62 {
63 if (command != null)
64 {
65 command.Execute();
66 }
67 }
68 }
69
70 /// <summary>
71 /// 客户端
72 /// </summary>
73 class Client
74 {
75 static void Main(string[] args)
76 {
77 //首先创建接收人,即张三。
78 Receiver_ZhangSan receiver_ZhangSan = new Receiver_ZhangSan();
79
80 //然后创建命令类,即圣旨。这里通过构造函数参数注入的形式将张三和命令进行绑定。
81 Command command = new ConcreteCommand1(receiver_ZhangSan);
82
83 //其次创建调用者,即宦官。
84 Invoker invoker = new Invoker();
85 //为了演示不同注入方式,这里通过属性赋值的方式将命令和调用者绑定。
86 invoker.command = command;
87
88 //调用,即宣读圣旨
89 invoker.Reading();
90
91 Console.WriteLine("\r\n");
92
93 Console.ReadKey();
94 }
95 }

  如果皇帝想让张三吃饱饭再回京,同时想让李四原地待命该如何实现呢。

  两种方式,如果张三和李四在一起,就可以写在一个圣旨里即写在一个命令类里。

  如果不在一起,那就需要写两道圣旨,即两个命令类。

添加李四类:

 1     /// <summary>
2 /// 李四类,它是实际执行者,也就是命令接收者
3 /// </summary>
4 public class Receiver_LiSi
5 {
6 private string strName = "李四";
7 public void Eat()
8 {
9 Console.WriteLine(strName + ":吃饭。");
10 }
11 public void Behavior1()
12 {
13 Console.WriteLine(strName + ":坐马车回京。");
14 }
15 public void Behavior2()
16 {
17 Console.WriteLine(strName + ":原地待命。");
18 }
19 }

添加新命令类:

 1     /// <summary>
2 /// 圣旨,即命令类
3 /// </summary>
4 public class ConcreteCommand3 : Command
5 {
6 //创建执行人的引用
7 private readonly Receiver_LiSi _Receiver_LiSi;
8 private readonly Receiver_ZhangSan _Receiver_ZhangSan;
9
10 //将执行人注入到命令类中。
11 public ConcreteCommand3(Receiver_LiSi receiver_LiSi, Receiver_ZhangSan receiver_ZhangSan)
12 {
13 this._Receiver_LiSi = receiver_LiSi;
14 this._Receiver_ZhangSan = receiver_ZhangSan;
15 }
16
17 //具体命令
18 public override void Execute()
19 {
20 //让张三吃饱饭再回京
21 this._Receiver_ZhangSan.Eat();
22 this._Receiver_ZhangSan.Behavior1();
23 //让李四原地待命
24 this._Receiver_LiSi.Behavior2();
25 }
26 }

客户端调用:

 1     /// <summary>
2 /// 客户端
3 /// </summary>
4 class Client
5 {
6 static void Main(string[] args)
7 {
8 //首先创建接收人,即张三和李四。
9 Receiver_ZhangSan receiver_ZhangSan = new Receiver_ZhangSan();
10 Receiver_LiSi receiver_LiSi = new Receiver_LiSi();
11
12 //然后创建命令类,即圣旨。这里通过构造函数参数注入的形式将张三和命令进行绑定。
13 Command command = new ConcreteCommand3(receiver_LiSi, receiver_ZhangSan);
14
15 //其次创建调用者,即宦官。
16 Invoker invoker = new Invoker();
17 //为了演示不同注入方式,这里通过属性赋值的方式将命令和调用者绑定。
18 invoker.command = command;
19
20 //调用,即宣读圣旨
21 invoker.Reading();
22
23 Console.WriteLine("\r\n");
24
25 Console.ReadKey();
26 }
27 }

  以上实例则是张三和李四在一起,写在一个圣旨里即写在一个命令类里。如果不在一起呢?

创建新命令类:

 1     /// <summary>
2 /// 让李四原地待命的圣旨
3 /// </summary>
4 public class ConcreteCommand2 : Command
5 {
6 //创建执行人的引用
7 private readonly Receiver_LiSi _Receiver_LiSi;
8
9 //将执行人注入到命令类中。
10 public ConcreteCommand2(Receiver_LiSi receiver_LiSi)
11 {
12 this._Receiver_LiSi = receiver_LiSi;
13 }
14
15 //具体命令
16 public override void Execute()
17 {
18 //让李四原地待命
19 this._Receiver_LiSi.Behavior2();
20 }
21 }

客户端调用:

 1     /// <summary>
2 /// 客户端
3 /// </summary>
4 class Client
5 {
6 static void Main(string[] args)
7 {
8 //首先创建接收人,即张三和李四。
9 Receiver_ZhangSan receiver_ZhangSan = new Receiver_ZhangSan();
10 Receiver_LiSi receiver_LiSi = new Receiver_LiSi();
11
12 //然后创建命令类,即圣旨1
13 Command command1 = new ConcreteCommand1(receiver_ZhangSan);
14 //然后创建命令类,即圣旨2
15 Command command2 = new ConcreteCommand2(receiver_LiSi);
16
17 //其次创建调用者,即宦官。
18 Invoker invoker = new Invoker();
19 invoker.command = command1;
20 invoker.Reading();
21
22 invoker.command = command2;
23 invoker.Reading();
24
25 Console.WriteLine("\r\n");
26
27 Console.ReadKey();
28 }
29 }

  另一种方式,可以在Invoker里申明数组保存命令依次执行。

 1     /// <summary>
2 /// 调用者类,命令下达者,即宦官。
3 /// </summary>
4 public class InvokerList
5 {
6 public List<Command> commands;
7 public void AddCommand(Command command)
8 {
9 if (commands == null)
10 {
11 commands = new List<Command>();
12 }
13 commands.Add(command);
14 }
15
16 //下达命令,即宣读圣旨
17 public void Reading()
18 {
19 if (commands != null)
20 {
21 foreach (Command item in commands)
22 {
23 item.Execute();
24 }
25 }
26 }
27 }

客户端调用:

 1     /// <summary>
2 /// 客户端
3 /// </summary>
4 class Client
5 {
6 static void Main(string[] args)
7 {
8 //首先创建接收人,即张三和李四。
9 Receiver_ZhangSan receiver_ZhangSan = new Receiver_ZhangSan();
10 Receiver_LiSi receiver_LiSi = new Receiver_LiSi();
11
12 //然后创建命令类,即圣旨1
13 Command command1 = new ConcreteCommand1(receiver_ZhangSan);
14 //然后创建命令类,即圣旨2
15 Command command2 = new ConcreteCommand2(receiver_LiSi);
16
17 //其次创建调用者,即宦官。
18 InvokerList invoker = new InvokerList();
19 invoker.AddCommand(command1);
20 invoker.AddCommand(command2);
21 invoker.Reading();
22
23 Console.WriteLine("\r\n");
24
25 Console.ReadKey();
26 }
27 }

从以上事例中不难看出,命令的调用者即Invoker类无需知道执行人和具体执行什么内容,更换命令也很方便,只需要改变注入的命令类就可以了。

但也可以从实例中看出,新增一个命令就需要新增一个命令类,这可能会导致某些系统存在过多的命令类,会影响使用。

  读后思考:书读百遍其义自见,读不够百遍,那不如自己动手写写,不如就拿遥控控制不同电器为例子。

  友情提示:
      创建一盏灯
      创建一个命令,该命令控制继承自抽象类,执行灯的一些操作,所以需要将创建好的灯注入到命令对象里。
      创建一个遥控,将遥控的某一个按钮绑定上该命令。可以是参数注入,也可以是属性赋值。
      具体执行就是执行遥控的函数,然后传递到命令类 最后才是具体灯的操作。

      关键点就是灯跟命令进行绑定,方式就是在命令里声明一个灯的引用,然后参数注入等方式进行实例化。
      然后命令再跟遥控进行绑定,方式相同,在遥控里声明一个命令的引用,然后参数注入等方式进行实例化。
      最后实行执行操作的是遥控。

      为什么要将命令抽象化,因为要跟遥控绑定,遥控只是执行命令,具体是什么样的命令,就交给命令本身去决定了。
      还有一个问题就是,有多少个电器 就需要多少个命令 。那其实可以合并成一个。大家可以动手试试。

总结:

  将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化。适用于需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。

  命令模式的实现要点在于把某个具体的命令抽象化为具体的命令类,并通过加入命令请求者角色来实现将命令发送者对命令执行者的依赖分割开。

  命令模式的目的是解除命令发出者和接收者之间的紧密耦合关系,使二者相对独立,有利于程序的并行开发和代码的维护。

  命令模式的核心思想是将请求封装为一个对象,将其作为命令发起者和接收者的中介,而抽象出来的命令对象又使得能够对一系列请求进行操作,如对请求进行排队,记录请求日志以及支持可撤销的操作等。

  

c#中命令模式详解的更多相关文章

  1. 虚拟机中linux系统常用命令解释及vim3种命令模式详解

    1.man man 加上一个命令可以打开此命令具体使用方法,方便我们更好的了解新命令的使用(下图为我输入命令“man ls”虚拟机界面) 2.cd  切换目录 cd ..(返回上一级目录) cd ~( ...

  2. 设计模式 - 命令模式详解及其在JdbcTemplate中的应用

    基本介绍 在软件设计中,我们经常需要向某些对象发送一些请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需要在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来设计,使得 ...

  3. 深入浅出的webpack构建工具---devTool中SourceMap模式详解(四)

    阅读目录 一:什么是SourceMap? 二:理解webpack中的SourceMap的eval,inline,sourceMap,cheap,module 三:开发环境和线上环境如何选择source ...

  4. 批处理中的echo命令图文详解

    批处理中的echo命令图文详解 1. Echo 显示当前ECHO的状态:ECHO ON 或者ECHO OFF 2. ECHO ON 将ECHO状态设置为ON,将显示命令行,也就是前面的C:\>类 ...

  5. Maven依赖中的scope详解,在eclipse里面用maven install可以编程成功,到服务器上用命令执行报VM crash错误

    Maven依赖中的scope详解 项目中用了<scope>test</scope>在eclipse里面用maven install可以编译成功,到服务器上用命令执行报VM cr ...

  6. (转)shell中test命令方法详解

    test命令用法.功能:检查文件和比较值 shell中test命令方法详解 原文:https://www.cnblogs.com/guanyf/p/7553940.html 1)判断表达式 if te ...

  7. CentOS 7.X 中systemctl命令用法详解

    systemctl是RHEL 7 的服务管理工具中主要的工具,它融合之前service和chkconfig的功能于一体.可以使用它永久性或只在当前会话中启用/禁用服务,下面来看CentOS 7.X 中 ...

  8. Linux中mpstat命令参数详解

    Linux中mpstat命令参数详解 mpstat 是 Multiprocessor Statistics的缩写,是实时系统监控工具.其报告与CPU的一些统计信息,这些信息存放在 /proc/stat ...

  9. linux中cat、more、less命令区别详解##less 最合适最好用,和vim一样好用

    linux中cat.more.less命令区别详解 caoxinyiyi关注 0.0362018.07.02 15:46:17字数 641阅读 516 linux中命令cat.more.less均可用 ...

  10. JavaScript严格模式详解

    转载自阮一峰的博客 Javascript 严格模式详解   作者: 阮一峰 一.概述 除了正常运行模式,ECMAscript 5添加了第二种运行模式:"严格模式"(strict m ...

随机推荐

  1. MISC杂项解题思路

    首先拿到一个杂项的附件 第一步要判断 是什么类型的杂项题目 附件是什么内容 是图片? 是压缩包? 是磁盘文件? 还是其他未知的东西 第一步的判断能够直接将解题思路精准定位到正确的区域下 加快解题速度 ...

  2. [oracle]拆分多用户的公共表空间

    前言 开发环境之前多个用户共用一个表空间,后期维护比较麻烦,因此需要将这些用户拆出来,一个用户一个表空间,以后清理这些用户也更方便. 大致思路:假设A.B.C用户共用一个表空间,将A.B.C的用户数据 ...

  3. 定义一个函数,传入一个字典和一个元组,将字典的值(key不变)和元组的值交换,返回交换后的字典和元组

    知识点:zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表. li=[3,4,5] t=(7,8,9) print(list(zip(li,t ...

  4. 现代 CSS 解决方案:数学函数 Round

    在 CSS 中,存在许多数学函数,这些函数能够通过简单的计算操作来生成某些属性值,例如在现代 CSS 解决方案:CSS 数学函数一文中,我们详细介绍了 calc():用于计算任意长度.百分比或数值型数 ...

  5. Netease研发实习生一面

    最想去的公司和部门..今天终于面了,感觉跪了..实质性的问题的确打得不好..庆幸的是拿到了B公司的offer,实习是不愁了.. 记下问题,希望对之后的同学有一定帮助. 问的问题总结: 1.进程线程协程 ...

  6. 对比 MyBatis 和 MyBatis-Plus 批量插入、批量更新的性能和区别

    1 环境准备 demo 地址:learn-mybatis · Sean/spring-cloud-alibaba - 码云(gitee.com) 1.1 搭建 MyBatis-Plus 环境 创建 m ...

  7. Linux-Stream内存带宽及MLC内存延迟性能测试方法

    1.Stream内存带宽测试   Stream是业界主流的内存带宽测试程序,测试行为相对简单可控.该程序对CPU的计算能力要求很小,对CPU内存带宽压力很大.随着处理器核心数量的增大,而内存带宽并没有 ...

  8. JAVA动态增强一个BaseController的已经存在的接口

    使用场景 前提场景 我们多个系统同时继承了某一个通用系统,通用系统的接口是不会允许随意改变的,其他子系统都依赖于Base系统的通用接口 目标需求场景 但是有一个业务,需要给某一个公共接口增加子系统独有 ...

  9. 深入解析 C++ 中的 ostringstream、istringstream 和 stringstream 用法

    引言: 在 C++ 中,ostringstream.istringstream 和 stringstream 是三个非常有用的字符串流类,它们允许我们以流的方式处理字符串数据.本文将深入探讨这三个类的 ...

  10. VS Code代码提示( AcWing算法模板,C++实现)

    算法模板提取于AcWing上的代码提示 作者:yxc 链接:https://www.acwing.com/file_system/file/content/whole/index/content/21 ...