概述

在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。这就是本文要说的Command模式。

意图

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。[GOF 《设计模式》]

结构图

<Design Pattern>Command模式结构图如下:

图1 Command模式结构图

西游记中例子:玉帝传美猴王上天

命令模式不是新的发明,在美猴王大闹天宫之前就有了。那时玉帝命令太白金星召美猴王上天:"金星径入(水帘洞)当中,面南立定道:'我是西方太白金星,奉玉帝招安圣旨,下界请你上大,拜受仙录。'"玉帝是系统的客户端,太白金星是命令的发出者,猴王是命令的接收者,圣旨就是命令。玉帝的这一道命令就是要求猴王到上界报到。玉帝只管发出命令,而不管命令是怎样传达到美猴王的。太白金星负责将圣旨传到,可是美猴王怎么执行圣旨、何时执行圣旨是美猴王自己的事。果不然,个久美猴王就大闹了天宫。

这个模拟系统的设计如下:

生活中的例子

Command模式将一个请求封装为一个对象,从而使你可以使用不同的请求对客户进行参数化。用餐时的账单是Command模式的一个例子。服务员接受顾客的点单,把它记在账单上封装。这个点单被排队等待烹饪。注意这里的"账单"是不依赖于菜单的,它可以被不同的顾客使用,因此它可以添入不同的点单项目。

图2 使用用餐例子的Command模式对象图

示例用例结构图:

手机操作系统包括开机、关机、打电话、挂电话、发短信和删除短信功能,其实这就是一个命令模式,类结构示意图如下:

先创建接口ICommand.cs:

    public interface ICommand{/// <summary>/// 执行命令/// </summary>string Execute();/// <summary>/// 撤消命令/// </summary>string UnExecute();}

再创建SystemCommand.cs:

  public  class SystemCommand{public string StartUp(){return "手机开机";}public string ShutDown(){return "手机关机";}public string SendSMS(){return "发短信";}public string RemoveSMS(){return "删短信";}public string CallPhone(){return "打电话";}public string RingOff(){return "挂电话";}}

再创建MobileSystem.cs:

 public abstract class MobileSystem :ICommand{private SystemCommand mobileCommand;public SystemCommand MobileCommand{get {return mobileCommand;}set {mobileCommand = value;}}public MobileSystem(SystemCommand command){this.mobileCommand = command;}#region ICommand 成员public abstract string Execute();public abstract string UnExecute();#endregion}

再创建StartUp.cs:

   public  class StartUp :MobileSystem{#region MobileSystem 成员public override string  Execute(){return MobileCommand.StartUp();}public override string UnExecute(){return MobileCommand.ShutDown();}#endregionpublic StartUp(SystemCommand command): base(command){ }}

再创建Call.cs:

 public class Call : MobileSystem{#region MobileSystem 成员public override string Execute(){return MobileCommand.CallPhone();}public override string UnExecute(){return MobileCommand.RingOff();}#endregionpublic Call(SystemCommand command): base(command){ }}

再创建SMS.cs:

    public class SMS : MobileSystem{#region MobileSystem 成员public override string Execute(){return MobileCommand.SendSMS();}public override string UnExecute(){return MobileCommand.RemoveSMS();}#endregionpublic SMS(SystemCommand command): base(command){ }}

再创建MobileServer.cs:

  public class MobileServer : ICommand{private MobileSystem mobileSystem;public MobileServer(MobileSystem system){this.mobileSystem = system;}public string Execute(){return  mobileSystem.Execute();}public string UnExecute(){return mobileSystem.UnExecute();}}

最后再调用:

 public partial class Run : Form{public Run(){InitializeComponent();}private void btnRun_Click(object sender, EventArgs e){SystemCommand command = new SystemCommand();MobileServer server = new MobileServer(new StartUp(command));rtbResult.AppendText(server.Execute() + "\n");rtbResult.AppendText(server.UnExecute() + "\n");server = new MobileServer(new Call(command));rtbResult.AppendText(server.Execute() + "\n");rtbResult.AppendText(server.UnExecute() + "\n");server = new MobileServer(new SMS(command));rtbResult.AppendText(server.Execute() + "\n");rtbResult.AppendText(server.UnExecute() + "\n");}}

看结果:

效果及实现要点

1.Command模式的根本目的在于将“行为请求者”与“行为实现者”解耦,在面向对象语言中,常见的实现手段是“将行为抽象为对象”。

2.实现Command接口的具体命令对象ConcreteCommand有时候根据需要可能会保存一些额外的状态信息。

3.通过使用Compmosite模式,可以将多个命令封装为一个“复合命令”MacroCommand。

4.Command模式与C#中的Delegate有些类似。但两者定义行为接口的规范有所区别:Command以面向对象中的“接口-实现”来定义行为接口规范,更严格,更符合抽象原则;Delegate以函数签名来定义行为接口规范,更灵活,但抽象能力比较弱。

5.使用Command模式会导致某些系统有过多的具体命令类。某些系统可能需要几十个,几百个甚至几千个具体命令类,这会使命令模式在这样的系统里变得不实际。

6.从活动序列上来说通常是这样的一个过程:客户端指定一个命令的接受者;客户端创建一个具体的命令对象,并且告知接受者;客户端通过调用者对象来执行具体命令;调用者对象在合适的时候发出命令的执行指令;具体命令对象调用命令接受者的方法来落实命令的执行。

7.Command模式从结构上说变化非常多,要点就是一个抽象命令接口。抽象命令接口包含两个含义,一是把方法提升到类的层次,二是使用统一的接口来执行命令。

8.有了前面说的这个前提,我们才可以在调用者角色中做很多事情。比如,延迟命令的执行、为执行的命令记录日志、撤销执行的命令等等。

9.在应用的过程中可以省略一些不重要的角色。比如,如果只有一个执行者或者执行的逻辑非常简单的话,可以把执行的逻辑合并到具体命令角色中;如果我们并不需要使用调用者来做额外的功能,仅仅是希望通过命令模式来解除客户端和接受者之间耦合的话可以省略调用者角色。

10.如果需要实现类似于宏命令的命令组可以使用组合模式来封装具体命令。

11. 如果需要实现undo操作,那么命令接受者通常也需要公开undo的接口。在应用中,undo操作往往不是调用一下undo方法这么简单,因为一个操作执行后所改变的环境往往是复杂的。

适用性

在下面的情况下应当考虑使用命令模式:

1.使用命令模式作为"CallBack"在面向对象系统中的替代。"CallBack"讲的便是先将一个函数登记上,然后在以后调用此函数。

2.需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命期。换言之,原先的请求发出者可能已经不在了,而命令对象本身仍然是活动的。这时命令的接收者可以是在本地,也可以在网络的另外一个地址。命令对象可以在串形化之后传送到另外一台机器上去。

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

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

5.命令的发起人和命令的接收人有不同的生命周期。比如,下遗嘱的这种行为就是命令模式,一般来说遗嘱执行的时候命令的发起人已经死亡,命令是否得到有效的执行需要靠律师去做的。

6.希望能让命令具有对象的性质。比如,希望命令能保存以实现撤销;希望命令能保存以实现队列化操作。撤销的行为在GUI中非常常见,队列化命令在网络操作中也非常常见。

7.把命令提升到类的层次后我们对类行为的扩展就会灵活很多,别的不说,我们可以把一些创建型模式和结构型模式与命令模式结合使用。

总结

1.Command模式是非常简单而又优雅的一种设计模式,它的根本目的在于将“行为请求者”与“行为实现者”解耦。

2. 不要被命令模式复杂的结构所迷惑,如果你不能理解的话请思考这句话“把方法提升到类的层次的好处也就是命令模式的好处”。

3.和把状态或算法提到类的层次的状态模式或策略模式相比,命令模式可能会产生更多的类或对象。

转载于:https://www.cnblogs.com/springyangwc/archive/2011/04/13/2015456.html

步步为营 .NET 设计模式学习笔记 九、Command(命令模式)相关推荐

  1. 【HeadFirst 设计模式学习笔记】6 命令模式

    1.这一节我们的任务是创建一个类似智能家居的万能遥控器,控制各种家电.我们需要将"请求"封装成对象(一个命令对象通过在特定接收者上绑定一组动作来封装请求),以便使用不同的请求.队列 ...

  2. 设计模式学习笔记九:原型模式(Prototype Pattern)

    1.概述     意图:我们将已经存在的对象作为原型,用户可以通过复制这些原型创建新的对象.     使用场合:当一个系统应该独立于产品的创建.构造和表示时,可以使用原型模式.在原型模式中,产品的创建 ...

  3. 步步为营 .NET 设计模式学习笔记系列总结

    设计模式我从开篇到23种设计模式的讲解总共花了进两个月的时间,其间有很多读者给我提出了很好的建议,同时也指出了我的不足,对此我表示感谢,正是由于很多读者的支持我才能坚持的写到最后.在此表示我真诚的谢意 ...

  4. 设计模式自学笔记007_Real(命令模式、备忘录模式、桥接模式)

    设计模式自学笔记007_Real(命令模式.备忘录模式.桥接模式) 一.命令模式 在软件设计的过程中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道请求的操作是哪个.我们只需要 ...

  5. 设计模式学习笔记——享元(Flyweight)模式

    设计模式学习笔记--享元(Flyweight)模式 @(设计模式)[设计模式, 享元模式, flyweight] 设计模式学习笔记享元Flyweight模式 基本介绍 享元案例 类图 实现代码 Big ...

  6. 设计模式学习笔记——中介者(Mediator)模式

    设计模式学习笔记--中介者(Mediator)模式 @(设计模式)[设计模式, 中介者模式, Mediator] 设计模式学习笔记中介者Mediator模式 基本介绍 中介者案例 类图 实现代码 Me ...

  7. 设计模式学习笔记——单例(Singleton)模式

    设计模式学习笔记--单例(Singleton)模式 @(设计模式)[设计模式, 单例模式, Singleton, 懒汉式, 饿汉式] 设计模式学习笔记单例Singleton模式 基本介绍 单例案例 类 ...

  8. 步步为营 .NET 设计模式学习笔记 一、开篇(设计模式之泡妞二十三招)

    园子里讲设计模式的太多了,最近我也在学设计模式,把我自己练的一些代码整理下,写个.NET设计模式学习笔记来让自己在设计模式的功底更深一层. 记得金庸小说里风清扬教令狐冲的时候,说过独孤九剑的总纲,无招 ...

  9. 设计模式学习笔记--Mediator 中介者模式

    我们知道面向对象应用程序是由一组为了提供某种服务而彼此交互的对象组成.当彼此引用的对象数量比较少时,此时对象之间就为直接交互(点对点).而当对象的数量增加时,这种直接交互会导致对象之间复杂的.混乱的引 ...

最新文章

  1. producer send源码_Kafka源码深度剖析系列(七)——Producer核心流程初探
  2. DocKer linux Centos 安装DocKer 只需要十步
  3. Windows Azure NotificationHub+Firebase Cloud Message 实现消息推动(付源码)
  4. 最简单的Docker镜像教程:从头基于空镜像scratch创建一个新的Docker镜像
  5. AX5243与AX5043方案对比及应用设计
  6. Docker容器引导完整CentOS
  7. 算法训练+乘法表c语言,[蓝桥杯][算法提高VIP]输出九九乘法表 (C语言代码)
  8. wordpress直接处理$_post_实战:Drupal迁移到WordPress
  9. jxls对比_jxls-2.x导出excel入门——基本操作
  10. proxmox 控制台无法连接_Proxmox VE 5的SPICE控制台和虚拟机声音设置
  11. 玩家可以输入辅助指令_三菱FX系列PLC输入输出与辅助继电器之间有什么关系?...
  12. NSGA2算法原理及python实现
  13. 高德sdk android加载3857,GCJ02-Correct
  14. 谈谈反向代理Nginx
  15. CentOS上如何顺利地安装MySQL?
  16. 【寻找最佳小程序】09期:轻芒杂志——杂志感设计,美美地阅读各领域杂志...
  17. 虚拟机与Linu系统安装与配置详细教程
  18. zstuoj 4246 萌新吃果果
  19. 23种设计模式 -----Day01:简单工厂模式
  20. 数独 ( 二 ) ——生成数独终局

热门文章

  1. postgres复制数据库
  2. Ubuntu 18.04 rc.local systemd设置
  3. zabbix server报错:FATAL: password authentication failed for user zabbix
  4. 质数(素数) 与 加密(密码学上的应用)
  5. 快速搭建开发环境(Vs Code)
  6. java中,正则表达式的使用 (最普通使用,Group,贪婪模式)
  7. 本地与服务器连接,更改连接使用的用户名,再次登录;如何操作.
  8. charles抓包显示乱码解决方法
  9. vue本地静态图片的路径问题解决方案
  10. Autowired,Qualifier,Spring 按名称注入bean属性