• 定义

    • 将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
    • 结构图:
  • 命令模式的角色划分:
    • Reciever(命令的接收者):接收命令,并知道如何进行必要的工作,实现具体的处理。任何类都可以当接收者。
    • Invoker(命令调用者):命令调用者持有一个命令对象,并在某个时间点调用命令对象的Execute()方法,将请求付诸实行。
    • Command(命令接口):Command为所有命令声明了一个接口。调用命令对象的Execute()方法,就可以让接收者进行相应的动作。这个接口也具备一个UnDo()方法,为了回滚动作使用,也可以不定义该方法。
    • ConcreteCommand(命令):ConcreateCommand定义了动作与接收者之间的绑定关系。调用者Execute()就可以发出请求,然后由ConcreateCommand调用就接收者的一个或者多个动作。
  • 应用示例:
    • 我们知道灯可以开、关,如果我们用程序去演示等的开关可以这么写:

      •  public class Program
        {
        public static void Main(string[] args)
        {
        Light light=new Light("light0001");
        // light turn on.
        light.On();
        // light turn off.
        light.Off();
        }
        } public class Light
        {
        public string Name { get; set; } public Light(string name)
        {
        this.Name = name;
        } /// <summary>
        /// Turn on the light
        /// </summary>
        public void On()
        {
        Console.WriteLine("light:{0} is turn on.", this.Name);
        } /// <summary>
        /// Turn off the light
        /// </summary>
        public void Off()
        {
        Console.WriteLine("light:{0} is turn off.", this.Name);
        }
        }
    • 上边的示例把灯的开、关动作调用都直接写在了Main的代码块中。但针对该例子这样写似乎没有看出有什么不妥的,但是如果是FTP的服务端接收命令解析时,我们把接收到的100左右个命令,分别定义出对应的函数到一个类中,这时就会发现这样做耦合度太高了,维护起来就不像单独On(),Off()两个动作这么轻松。
    • 试想,如果把FTP服务器端,接收到的命令拆分成不同的“命令”,有单一的“命令调用者”,这样会不会把结构划分的更清晰?

    • 备注:FTP向服务端请求命令:LS、DIR、MLS、MDIR、MKDIR、RMDIR

      LS有点像UNIX下的LS(LIST)命令:
      DIR相当于LS-L(LIST-LONG);
      MLS只是将远端某目 录下的文件存于LOCAL端的某文件里;
      MDIR相当于MLS;
      MKDIR像DOS下的MD(创建子目录)一样:
      RMDIR像DOS下的RD(删除子目录)一样。
    • 下边我们将上边的例子Light按照Command设计模式来改写,看下效果:
      • 命令接口:Command

         public interface ICommand
        { void Execute(); }
      • 命令的接收者:Reciever
        •  public class Light
          {
          public string Name { get; set; } public Light(string name)
          {
          this.Name = name;
          } /// <summary>
          /// Turn on the light
          /// </summary>
          public void On()
          {
          Console.WriteLine("light:{0} is turn on.", this.Name);
          } /// <summary>
          /// Turn off the light
          /// </summary>
          public void Off()
          {
          Console.WriteLine("light:{0} is turn off.", this.Name);
          }
          }
      • 命令的封装者:ConcreateCommand
         public class LightOnCommand : ICommand
        {
        private Light light; public LightOnCommand(Light light)
        {
        this.light = light;
        } public void Execute()
        {
        this.light.On();
        }
        }
      • 命令的封装者:ConcreateCommand
         public class LightOffCommand : ICommand
        {
        private Light light; public LightOffCommand(Light light)
        {
        this.light = light;
        } public void Execute()
        {
        this.light.Off();
        }
        }
      • 命令的封装者:命令的Invoker
         public class CommandControl
        {
        private ICommand command; public CommandControl() { } public void SetCommand(ICommand command)
        {
        this.command = command;
        } public void ButtonPresssed()
        {
        this.command.Execute();
        }
        }
      • 客户端:client
         class Program
        {
        static void Main(string[] args)
        {
        CommandControl control = new CommandControl();
        Light light = new Light("light_001");
        LightOnCommand lightOn = new LightOnCommand(light);
        LightOffCommand lightOff = new LightOffCommand(light); control.SetCommand(lightOn);
        control.ButtonPresssed(); control.SetCommand(lightOff);
        control.ButtonPresssed(); control.SetCommand(lightOn);
        control.ButtonPresssed(); control.SetCommand(lightOff);
        control.ButtonPresssed(); Console.ReadKey();
        }
        }
  • 开发中用到:
    •     
    • 怎么调用:
      •   
    • Command:
      •   
    • Reciever:
      •   
  • 应用场景:
    • 多级UnDo操作的应用场景;

      •   如何实现多层撤销操作?
      •   首先,不要只是记录最后一个被执行的命令,而使用一个堆栈记录操作过程的每一个命令。
      •   然后,不管什么时候操作了什么,都可以从堆栈中取出最上层的命令,然后调用它的UnDo()方法。
    •  命令可以将运算块打包(一个接收者和一组动作),让后将它传来传去,就像是一般的对象一样。现在,即使在命令对象被创建许久之后,运算依然可以被调用。事实上,它甚至可以在不同的线程中被调用。我们可以衍生一些应用,例如:日程安排(Scheduler)、线程池、工作队列等。
    • 日志请求:某些应用需要我们将所有的动作都记录在日志中,并能在系统死机之后,重新调用这些动作恢复到之前的状态。通过两个方法 Store(),Load(),命令模式就能够支持这一点。
  • 总结&优缺点:
    • 命令模式的实际应用还是挺广泛的,在FTP服务器端Socket接收到客户端命令请求,每个命令都需要做出对应的操作,为了业务的明细化,我们习惯上把这样的请求参数化,例如:SupperSocket.FTP中就是一个很好的命令模式的应用。
    • 优点:把‘请求’作为参数传递,将调用、参数、动作分开,解耦,业务更加清晰,代码逻辑更加明晰化,职责也更单一;
    • 缺点:子类划分的更多了,没出现一个新的请求,都需要new更多的类。

参考资料:《Head First 设计模式》

欢迎拍砖!请牛人们给指点。

(Command Pattern)命令模式的更多相关文章

  1. Command Pattern 命令模式

    定义: 命令模式将‘请求’封装成对象,以便使用不同的请求,队列或者日志来参数化其他对象,命令模式也支持可撤销的操作. 类图 如上图所示:Command类是用来声明执行操作的接口:ConcreteCom ...

  2. Command Pattern -- 命令模式原理及实现(C++)

    主要参考<大话设计模式>和<设计模式:可复用面向对象软件的基础>两本书.本文介绍命令模式的实现. What it is:Encapsulate a request as an ...

  3. [Design Pattern] Command Pattern 命令模式

    发现公司的代码好像有用到 Command Pattern,回顾重温下. Command Pattern 的类图结构如下: 参考 <Head First Design Patterns(英文版)& ...

  4. Design Pattern - 命令模式

    一般执行一个操作的过程, 创建对象, 并调用对象的函数, 函数执行, 返回 比如下面的类图, client直接调用Receiver.action 而命令模式, 抽象出command对象, 并在comm ...

  5. c#设计模式系列:命令模式(Command Pattern)

    引言 命令模式,我感觉"命令"就是任务,执行了命令就完成了一个任务.或者说,命令是任务,我们再从这个名字上并不知道命令的发出者和接受者分别是谁,为什么呢?因为我们并不关心他们是谁, ...

  6. 命令模式(Command)

    命令模式(Command) 命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行.这个过程好在,三者相互 ...

  7. Java设计模式の命令模式

    意图: 将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化:对请求排队或记录日志,以及支持可撤销的操作 动机: 将”发出请求的对象”和”接收与执行这些请求的对象”分隔开来. 效果: 1).c ...

  8. js命令模式

    命令模式(Command),将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化:对请求排队或记录请求日志,以及支持可撤销的操作. 从命令模式的结构图可以看出,它涉及到五个角色,它们分别是 ...

  9. Command and Query Responsibility Segregation (CQRS) Pattern 命令和查询职责分离(CQRS)模式

    Segregate operations that read data from operations that update data by using separate interfaces. T ...

随机推荐

  1. Introducing the Accelerated Mobile Pages Project, for a faster, open mobile web

    https://googleblog.blogspot.com/2015/10/introducing-accelerated-mobile-pages.html October 7, 2015 Sm ...

  2. os

    内核,Shell和文件结构一起形成了基本的操作系统结构. from:大学生攻克Linux系统教程(又名天下没有难学的Linux) 发问: 0-内核,再怎么分出层次呢?

  3. Java Map遍历方式的选择

    [原文] 1. 阐述 对于Java中Map的遍历方式,很多文章都推荐使用entrySet,认为其比keySet的效率高很多.理由是:entrySet方法一次拿到所有key和value的集合:而keyS ...

  4. 蓝牙4.0(包含BLE)简介

    1. BLE   (低功耗蓝牙)简介 国际蓝牙联盟( BT-SIG,TI  是 企业成员之一)通过的一个标准蓝牙无线协议. 主要的新特性是在蓝牙标准版本上添加了4.0 蓝牙规范 (2010 年6 月 ...

  5. JVM GC (一)

    1. 先来说GC工作在哪块区域呢? 程序计数器,虚拟机栈(也就平时所说的栈), 本地方法栈这三区域随着线程而生,随着线程而灭,出栈入栈的操作,在栈中分配配的多少内存都具 有确定性,在这几个区域就不用考 ...

  6. Unicode与UTF8相互转化(使用MultiByteToWideChar)

    1.简述 最近在发送网络请求时遇到了中文字符乱码的问题,在代码中调试字符正常,用抓包工具抓的包中文字符显示正常,就是发送到服务器就显示乱码了,那就要将客户端和服务器设置统一的编码(UTF-8),而我们 ...

  7. 转:VS2010调试NUnit测试项目 (Running or debugging NUnit tests from Visual Studio without any extensions)

    If you write unit tests and use NUnit test framework this may be helpful. I decided to write this si ...

  8. background:transparent的作用

    background的属性值 background : background-color | background-image | background-repeat | background-att ...

  9. 1001 数组中和等于K的数对

    1001 数组中和等于K的数对 基准时间限制:1 秒 空间限制:131072 KB 给出一个整数K和一个无序数组A,A的元素为N个互不相同的整数,找出数组A中所有和等于K的数对.例如K = 8,数组A ...

  10. Codeforces Round #376 (Div. 2)F. Video Cards(前缀和)

    题目链接:http://codeforces.com/contest/731/problem/F 题意:有n个数,从里面选出来一个作为第一个,然后剩下的数要满足是这个数的倍数,如果不是,只能减小为他的 ...