一.命令模式的定义

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

二.我们来看一个类图

从命令模式的结构图可以看出,它涉及到五个角色,它们分别是:

  • 客户角色:发出一个具体的命令并确定其接受者。
  • 命令角色:声明了一个给所有具体命令类实现的抽象接口
  • 具体命令角色:定义了一个接受者和行为的弱耦合,负责调用接受者的相应方法。
  • 请求者角色:负责调用命令对象执行命令。
  • 接受者角色:负责具体行为的执行。

三、生活中的例子  

1.场景模拟

首先我们考虑,当项目多,人手不够时,我们需要一个人去同时兼有好几个项目的工作,可能有开发也可能有运维!

那么第一步我们先设计一个项目类,分别代表需要参与的项目,代码如下:


    /// <summary>
    /// Web项目类
    /// </summary>
    public class WebProject
    {
        /// <summary>
        /// 项目名称
        /// </summary>
        public string ProjectName { get; set; }
    }

接下来第二步,我们设计一个开发者类表示具体的开发人员,并且开发人员具有开发项目和运维项目的操作,代码如下:


    /// <summary>
    /// 开发者
    /// </summary>
    public class Developer
    {
        /// <summary>
        /// 开发项目
        /// </summary>
        /// <param name="project">项目</param>
        public void Coding(WebProject project)
        {
            Console.WriteLine("开发{0}项目",project.ProjectName);
        }         /// <summary>
        /// 运维项目
        /// </summary>
        /// <param name="project">项目</param>
        public void UpdateRoutine(WebProject project)
        {
            Console.WriteLine("维护{0}项目", project.ProjectName);
        }
    }

主函数调用方法如下:


    static void Main(string[] args)
    {
            //表示一个具体的项目名字叫:项目A
            WebProject wpA = new WebProject() { ProjectName = "项目A" };
            //表示一个具体的项目名字叫:项目B
            WebProject wpB = new WebProject() { ProjectName = "项目B" };             //实例化一个开发者,程序员
            Developer dp = new Developer();             //命令他开发项目A
            dp.Coding(wpA);
            //命令他开发项目B
            dp.UpdateRoutine(wpB);
    }

这么简单的程序运行结果我就不贴出来啦,我们来看看上边的代码,我们把主函数的调用当做客户,我们发现如果客户直接和开发者形成这种紧耦合的状态是非常不好的,比如说,客户会提出很多业务上的要求,而开发者并不能很准确的估算出开发成本与时间,所以我们需要一个项目经理来与客户沟通,并且将客户提出的不合理需求过滤掉,在给开发人员发出一个命令将项目交给开发人员!也就是说需要将“命令发起者”与“命令实现者”分离!

 2.命令模式

那么我们来看看实现“命令发起者”与“命令实现者”分离的设计模式,命令模式:

命令模式:将一个请求封装为一个对象,从而可用不同的的请求对客户进行参数化,队请求排队或者记录请求日志,以及支持可撤销的操作。

命令模式结构图如下:

①接口ICommand:用来声明抽象一个命令操作的接口。

②ConcreteCommand:具体的命令实现,用来表示命令的类型例如上边场景中的开发项目和维护项目可分别实现为:开发项目命令,维护项目命令等。

③Receiver:具体命令的实现者,也就是说命令的执行者,相对于上边场景中的开发者。

④Invoker:命令的指挥者,用来设置命令,并且通知执行命令,相对于上边场景中的项目经理。

了解完命令模式的概念后,我们用命令模式来实现上边的场景:

首先是命令接口ICommand的实现,代码如下:

    public interface ICommand
    {
        void Excute(WebProject project);
    }

接下来就是我们接受命令并且执行的实现者啦,也就是我们场景中的开发者,代码如下:


    /// <summary>
    /// 开发者
    /// </summary>
    public class Developer
    {
        /// <summary>
        /// 开发项目
        /// </summary>
        /// <param name="project">项目</param>
        public void Coding(WebProject project)
        {
            Console.WriteLine("开发{0}项目", project.ProjectName);
        }         /// <summary>
        /// 运维项目
        /// </summary>
        /// <param name="project">项目</param>
        public void UpdateRoutine(WebProject project)
        {
            Console.WriteLine("维护{0}项目", project.ProjectName);
        }
    }

现在有了命令的实现者,就应该有具体的命令啦,上边那场景中的命令分别有,开发命令与维护命令,具体代码如下:


  /// <summary>
    /// 开发项目命令
    /// </summary>
    public class CodingCommand : ICommand
    {
        Developer dp { get; set; }
        public WebProject project { get; set; }//所对应的项目         public void Excute()
        {
            dp.Coding(project);
        }
    }     /// <summary>
    /// 维护项目命令
    /// </summary>
    public class UpdateRoutineCommand : ICommand
    {
        Developer dp { get; set; }
        public WebProject project { get; set; }//所对应的项目         public void Excute()
        {
            dp.UpdateRoutine(project);
        }
    }

命令与实现者都就绪了,指挥调度者就该出现了,也就是项目经理,项目经理用来负责设置命令调度命令执行,代码如下:


    /// <summary>
    /// 项目经理类
    /// </summary>
    public class ProjectManager
    {
        List<ICommand> cmdList = new List<ICommand>();         /// <summary>
        /// 设置命令
        /// </summary>
        /// <param name="cmd"></param>
        public void SetCommand(ICommand cmd)
        {
            cmdList.Add(cmd);
        }         /// <summary>
        /// 发起命令
        /// </summary>
        public void ExcuteCommand()
        {
            cmdList.ForEach(p => p.Excute());
        }
    }

代码写好后,我们来模拟一下调用吧,现在项目经理需要分配给开发者两个任务,一个是开发项目A,另一个是维护项目B!主函数调用如下:


            //表示一个具体的项目名字叫:项目A
            WebProject wpA = new WebProject() { ProjectName = "项目A" };
            //表示一个具体的项目名字叫:项目B
            WebProject wpB = new WebProject() { ProjectName = "项目B" };
            //开发者
            Developer developer= new Developer();             //开发者所需要接收的命令
            ICommand codeCmd = new CodingCommand() { project = wpA, dp = developer };
            ICommand UpdateCmd = new UpdateRoutineCommand() { project = wpB, dp = developer };             //项目经理
            ProjectManager pm = new ProjectManager();
            //设置命令
            pm.SetCommand(codeCmd);
            pm.SetCommand(UpdateCmd);
            //发起命令让开发者去完成
            pm.ExcuteCommand();
}

那么一个简单的命令模式就完成了。

  3.更加灵活的分配任务

接下来我们在增加一些要求:

①项目经理每次发起的命令都需要记录下来,年底好根据开发者的工作量评估奖金。

②项目经理可以撤销发起的命令,例如:撤销维护项目B的命令,让开发者专心做项目A的开发工作。

回顾下命令模式的定义,上边两条需求就等同于定义中的“队请求排队或者记录请求日志,以及支持可撤销的操作

那么我们给需求中加入一个方法用来撤销命令,因为项目经理类中已经有一个集合来记录命令的总数了,所以已经实现了请求记录的功能啦,我们只需加入撤销功能就好了。

首先,给ICommand接口加入一个Cancel方法,用来表示撤销操作,因为命令撤销了需要通知实现者做一些撤销的工作,代码如下:

    public interface ICommand
    {
        void Excute();
        void Cancel();
    }

接下来既然有取消了,那么开发者受到取消命令后要执行一些关于取消的代码,所以开发者类加入一个方法StopCoding,表示停止项目的工作,代码如下:


  /// <summary>
    /// 开发者
    /// </summary>
    public class Developer
    {
        /// <summary>
        /// 开发项目
        /// </summary>
        /// <param name="project">项目</param>
        public void Coding(WebProject project)
        {
            Console.WriteLine("开发{0}项目", project.ProjectName);
        }         /// <summary>
        /// 运维项目
        /// </summary>
        /// <param name="project">项目</param>
        public void UpdateRoutine(WebProject project)
        {
            Console.WriteLine("维护{0}项目", project.ProjectName);
        }         /// <summary>
        /// 停止项目的工作
        /// </summary>
        /// <param name="project"></param>
        public void StopCoding(WebProject project)
        {
            Console.WriteLine("停止{0}项目",project.ProjectName);
        }
    }

修改实际的命令类实现撤销命令的方法,并调用开发者类的StopCoding方法做撤销命令的工作,代码如下:


/// <summary>
    /// 开发项目命令
    /// </summary>
    public class CodingCommand : ICommand
    {
        public Developer dp { get; set; }
        public WebProject project { get; set; }//所对应的项目         public void Excute()
        {
            dp.Coding(project);
        }         /// <summary>
        /// 撤销操作
        /// </summary>
        public void Cancel()
        {
            dp.StopCoding(project);
        }
    }     /// <summary>
    /// 维护项目命令
    /// </summary>
    public class UpdateRoutineCommand : ICommand
    {
        public Developer dp { get; set; }
        public WebProject project { get; set; }//所对应的项目         public void Excute()
        {
            dp.UpdateRoutine(project);
        }         /// <summary>
        /// 撤销操作
        /// </summary>
        public void Cancel()
        {
            dp.StopCoding(project);
        }
    }

接下来就是命令的指挥者啦,项目经理首先设置一个撤销命令,并且将执撤销命令,在执行撤销命令的时候首先要将命令从集合中移除,因为该开发者已经不参与该撤销项目的工作了,所以不记录考核依据中,在调用命令中的撤销方法让开发者做具体的撤销工作。代码如下:


/// <summary>
    /// 项目经理类
    /// </summary>
    public class ProjectManager
    {
        List<ICommand> cmdList = new List<ICommand>();         /// <summary>
        /// 设置命令
        /// </summary>
        /// <param name="cmd"></param>
        public void SetCommand(ICommand cmd)
        {
            cmdList.Add(cmd);
        }         /// <summary>
        /// 发起命令
        /// </summary>
        public void ExcuteCommand()
        {
            cmdList.ForEach(p => p.Excute());
        }         /// <summary>
        /// 返回记录行数
        /// </summary>
        /// <returns></returns>
        public int GetCommandCount()
        {
            return cmdList.Count;
        }         /// <summary>
        /// 撤销命令
        /// </summary>
        /// <param name="cmd"></param>
        public void CancelExectueCommand(ICommand cmd)
        {
            cmd.Cancel();
            cmdList.Remove(cmd);
        }
    }

可见对功能的封装和“请求与实现分离”,可以让我们很容易的进行撤销与记录的工作,可以再命令执行前作一些必须要的操作。主函数调用如下:


        static void Main(string[] args)
        {
            //表示一个具体的项目名字叫:项目A
            WebProject wpA = new WebProject() { ProjectName = "项目A" };
            //表示一个具体的项目名字叫:项目B
            WebProject wpB = new WebProject() { ProjectName = "项目B" };
            //开发者
            Developer developer= new Developer();             //开发者所需要接收的命令
            ICommand codeCmd = new CodingCommand() { project = wpA, dp = developer };
            ICommand UpdateCmd = new UpdateRoutineCommand() { project = wpB, dp = developer };             //项目经理
            ProjectManager pm = new ProjectManager();
            //设置命令
            pm.SetCommand(codeCmd);
            pm.SetCommand(UpdateCmd);
            //发起命令让开发者去完成
            pm.ExcuteCommand();             pm.CancelExectueCommand(UpdateCmd);//撤销UpdateCmd命令
         }

命令模式总结:

①它能很容易的维护所有命令的集合。

②它可以很方便的实现撤销和恢复命令。

③可以很方便的将每个执行记录日志。

④最重要的就是将发起者与实现者分离。

参考链接:http://www.cnblogs.com/doubleliang/archive/2012/04/14/2447815.html

设计模式学习之命令模式(Command,行为型模式)(12)的更多相关文章

  1. 设计模式学习之单例模式(Singleton,创建型模式)(4)

    假如程序中有一个Person类,我的需求就是需要在整个应用程序中只能new一个Person,而且这个Person实例在应用程序中进行共享,那么我们该如何实现呢? 第一步: 新建一个Person类,类中 ...

  2. 设计模式学习之观察者模式(Observer,行为型模式)(7)

    1.观察者模式又叫做发布-订阅模式. 2.观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己. 3 ...

  3. 命令模式 Command 行为型 设计模式(十八)

    命令模式(Command) 请分析上图中这条命令的涉及到的角色以及执行过程,一种可能的理解方式是这样子的: 涉及角色为:大狗子和大狗子他妈 过程为:大狗子他妈角色 调用 大狗子的“回家吃饭”方法 引子 ...

  4. 设计模式14:Command 命令模式(行为型模式)

    Command 命令模式(行为型模式) 耦合与变化 耦合是软件不能抵御变化的根本性原因.不仅实体对象与实体对象之间存在耦合关系,实体对象与行为操作之间也存在耦合关系. 动机(Motivation) 在 ...

  5. 设计模式17:Iterator 迭代器模式(行为型模式)

    Iterator 迭代器模式(行为型模式) 动机(Motivation) 在软件构建过程中,集合对象内部结构常常变化各异.但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码可以透 ...

  6. 设计模式16:Mediator 中介者模式(行为型模式)

    Mediator 中介者模式(行为型模式) 依赖关系的转化 动机(Motivation) 在软件构建过程中,经常出现多个对象互相关联交互的情况,对象之间经常会维持一种复杂的应用关系,如果遇到一些需求的 ...

  7. 设计模式23:Visitor 访问者模式(行为型模式)

    Visitor 访问者模式(行为型模式) 动机(Motivation)在软件构造过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的修改,将会给子类带来繁重的 ...

  8. 设计模式22:Strategy 策略模式(行为型模式)

    Strategy 策略模式(行为型模式) 动机(Motivation) 在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂:而且有时候支持 ...

  9. 设计模式21:State 状态模式(行为型模式)

    State 状态模式(行为型模式) 动机(Motivation) 在软件构建过程中,某些对象的状态如果改变,其行为也会随之而发生变化,比如文档处于只读状态,其支持的行为和读写状态的行为就可能完全不同. ...

  10. 设计模式20:Memento 备忘录模式(行为型模式)

    Memento 备忘录模式(行为型模式) 对象状态的回溯 对象状态的变化无端,如何回溯.恢复对象在某个点的状态? 动机(Motivation) 在软件构建过程中,某些对象的状态在转换过程中,可能由于某 ...

随机推荐

  1. ZUI前段框架简介

    一.说明 基于Bootstrap定制 ZUI继承了Bootstrap 3中的大部分基础内容,但出于与Bootstrap不同的目的,一些组件都进行了定制和修改.这些变化包括: 移除了部分插件的限制,增加 ...

  2. NOIP2015 斗地主

    Sol 暴力搜索...不用搜1,2张的情况,直接统计出来就可以了. 还有处理一下2和大王小王的位置,扔到最后面或者最前面就可以了. 搜索就搜 3+3,2+2+2,1+1+1+1+1 这三个就可以了. ...

  3. HDU 1069 dp最长递增子序列

    B - Monkey and Banana Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I6 ...

  4. time()函数,dirname(__FILE__) 的使用总结

    time()函数将返回从1970-1-1 0:0:0到当前时间的秒数.(整型) dirname(__FILE__) php中定义了一个很有用的常数,即 __file__ 这个内定常数是当前php程序的 ...

  5. postgresql 函数&存储过程 ; 递归查询

    函数:http://my.oschina.net/Kenyon/blog/108303 紧接上述,补充一下: 输入/输出参数的函数demo(输入作为变量影响sql结果,输出作为结果返回) create ...

  6. 获取Spring容器Bean

    WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext(); ManageResolver mr = (Ma ...

  7. phpcms调用某个栏目下的所有二级栏目

    在html中直接这样调用就可以了,subcat中的值为父栏目的catid {loop subcat(93) $r}{/loop} 注意subcat方法的参数,如果只写一个参数,也就是父栏目id,那该父 ...

  8. linux 终端报错 Out of memory: Kill process[PID] [process name] score问题分析

    从Out of memory来看是内存超出了,后面的 Kill process[PID] [process name] score好像和进程有关了,下面我们就一起来看看linux 终端报错 Out o ...

  9. iOS_隐藏顶部状态栏方式

    关键词:IOS.UIViewController. Status Bar iOS6和iOS7在隐藏 Status Bar 三种方式比较: Storyboard 界面上选中UIViewControlle ...

  10. 除Hadoop大数据技术外,还需了解的九大技术

    除Hadoop外的9个大数据技术: 1.Apache Flink 2.Apache Samza 3.Google Cloud Data Flow 4.StreamSets 5.Tensor Flow ...