Design Pattern - 命令模式

一般执行一个操作的过程, 创建对象, 并调用对象的函数, 函数执行, 返回

比如下面的类图, client直接调用Receiver.action

而命令模式, 抽象出command对象, 并在command对象封装对Receiver.action的调用

而client只负责创建command对象(invocation), 并提交给Invoker(通过setCommand), 而command真正的执行(execution)由Invoker控制

从而实现invocation和invoker的分离和解耦合

当然会问, 直接调用那么简单, 为什么要绕那么大的圈来用command?

a. 简化调用过程, 整个调用过程比较繁杂或调用前后需要进行某些额外处理,比如日志,缓存,记录历史操作等

b. 作为"CallBack"在面向对象系统中的替代

c. 支持undo, redo

d. 更关键的是, 命令模式往往会用于异步或并发处理模式

   比如, producer和consumer模式, producer可以不断产生command放到queue里面, 然后可以通过consumer来异步的执行, 达到并发和异步

 

 

命令模式 (Command), 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。 
这个模式的基本思路是解耦合和责任单一, “行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的, 所以要使用命令模式来解耦合.

场景, 这个模式在平时用的很多, 编辑器里面很常用的'undo/redo', 就是用的这种模式. 
经常举的例子就是点菜, 客人可以直接对厨师进行点菜, 这样做有两点不好 
1. 客户和厨师紧耦合, 比如厨师换了, 或菜名换了等等, 客户代码需要跟着改变. 
2. 厨师除了要烹饪外, 还需要记录每个客户请求, 当客户比较多时, 且客户请求是会变化的, 换个菜, 退个菜, 这个厨师就忙不过来了, 很容易记错. 这就违反了单一责任原则, 厨师就应该负责烹饪, 其他的事应该有专门的waiter负责. 
这就是命令模式, 当你需要对请求者的请求(函数调用)进行记录, undo, redo等操作时, 需要单独抽象出Invoker类来处理, 而不要直接耦合在Receiver中. 
上面的例子中, 客户是Client, 服务生是Invoker, 厨师是Receiver, 客户请求是Command

class Receiver //最终命令的执行者, 厨师
{
    public void Action()
    {
        //具体的命令执行, 烹饪菜肴
    }
}

class ConcreteCommand : Command //具体的客户请求, 点一道菜
{
    protected Receiver rec; //该情况的执行者, 这道菜哪个厨师烧
    public Command(Receiver rec)
    {
        this.rec = rec;
    }

    public void Execute()
    {
        rec.Action()//让执行者执行命令
    }
}

class Invoker //Command模式的核心, 客户请求的管理者, 服务生waiter
{
    private List<Command> cmds = new List<Command>(); //用于保存客户请求

    public void SetOrder(Command cmd)
    {
        if Valid(cmd) //需要判断请求是否合理, 比如是否有这道菜
        {
            cmds.Add(cmd); //增加请求
        }
    }

    public void CancelOrder(Command cmd)
    {
        cmds.Remove(cmd); //删除请求
    }

    public void Execute() //执行所有请求
    {
        for cmd in cmds
        {
            cmd.Excute();
        }
    }
}

public class Client {
    public void client(){
        //创建接收者
        Receiver receiver = new Receiver();
        //创建命令对象,设定它的接收者
        Command command = new ConcreteCommand(receiver);
        //创建Invoker,把命令对象设置进去
        Invoker invoker = new Invoker();
       invoker.SetOrder(command);
    }
}

Invoker具体的实现可能变化多端, 但是本质就是通过Invoke来把客户请求抽象成command, 并且保存和管理commands, 向用户提供如do, redo, undo, rollback等操作, 而这些操作对receiver都是透明的, 这取决于客户和receiver的解耦. 

本文章摘自博客园,原文发布日期:2013-11-19

时间: 2024-10-03 01:10:15

Design Pattern - 命令模式的相关文章

Design Pattern: Prototype 模式

  学习是分享和合作式的! 转载请注明出处:http://blog.csdn.net/wdzxl198/article/details/9271773: 文章摘自: http://www.riabook.cn/doc/designpattern/: 您从图书馆的期刊从发现了几篇您感兴趣的文章,由于这是图书馆的书,您不可以直接在书中作记号或写字,所以您将当中您所感兴趣的几个主题影印出来,这下子您就可在影印的文章上画记重点. Prototype模式的作用有些类似上面的描述,您在父类别中定义一个clo

Design Pattern: Singleton 模式

一句话概括:保证一个类仅有一个实例,并提供一个访问它的全局访问点. Singleton的英文意义是独身,也就是只有一个人,应用在物件导向语言上,通常翻译作单例:单一个实例(Instance).  很多时候,您会需要Singleton模式,例如印表机管理,您希望程式中只能有一个Print Spooler,以避免两个列印动作同时输入至印表机中:例如资料库管理,因为建立连接(Connection)物件会耗用资源,您希望程式中只能有一个 连接物件,所有其它的程式都透过这个物件来连接资料库,以避免连接物件

Design Pattern: Builder 模式

  学习是分享和合作式的! 转载请注明出处:http://blog.csdn.net/wdzxl198/article/details/9248365:  文章摘自: http://www.riabook.cn/doc/designpattern/:  您想要建立一个迷宫产生程式,迷宫使用二维阵列来定义,0表示道路,1表示墙,2表示宝物,根据所定义的二维迷宫阵列,您想要程式自动产生各种不同材质的迷宫,例如砖墙迷宫,钻石迷宫等等. 您可以在程式中定义两个角色,一个是指导迷宫建立的Director角

Design Pattern: Proxy 模式

学习是分享和合作式的! 转载请注明出处:http://blog.csdn.net/wdzxl198/article/details/10472999: 文章摘自: http://www.riabook.cn/doc/designpattern/: 在 Gof 的书中对Proxy模式的目的给定为:为其它的物件提供一种代理,以控制对这个物件的访问.由这句话所延伸出来的意思是,根据您的目的不同,您的代理物件将负有不同的责任,因为产生多种不同的代理情况. 根据不同的代理目的,而有不同的代理情况,在Gof

Design Pattern: Adapter 模式 - Object Adapter

您的电脑是个旧电脑,新的滑鼠都在使用USB接口了,而您的电脑上并没有USB,而只有一个PS2接口,这时您可以使用一个USB转PS2的接头作为转换,这样您的电脑就可以使用新滑鼠了(当然您也可以使用USB扩充卡,意思是相同的).  类似的概念,有时候您想在原来的程式中加入一个外部元件,例如一个类别,但是这个类别与您目前所设计的程式在介面上并不一致,为了让这个外部类与原程式的介面一致,您必须使用一个类别作为中介来配接它们,这时您可以采用Adapter模式.  举个例子来说,在Java 1.0中有个En

Design Pattern: Adapter 模式 - Class Adapter

Adapter模式的另一种作法是Class Adapter模式,在这个模式下,Adapter直接继承Adaptee(要引进的新类别),以拥有当中的成员及方法,在C++中的话可以这么作: C++中可以多重继承,但在Java中不行,所以在Java中若要采用Class Adapter,必须作点修改,一方面继承Adaptee,一方面实作Target的介面: 代码的实现是这样的: public class Adapter extends Adaptee implements Target {      /

Design Pattern: Flyweight 模式

学习是分享和合作式的! 转载请注明出处:http://blog.csdn.net/wdzxl198/article/details/10472999: 文章摘自: http://www.riabook.cn/doc/designpattern/: 在 Gof 的书中指出,Flyweight的目的在于运用共享技术,使得一些细粒度的物件可以共享. Flyweight在牛津字典中的解释是"boxer of the lightest class".意思是特轻量级拳击手?其实应该是取"

Design Pattern: Strategy 模式

  学习是分享和合作式的! 转载请注明出处:http://blog.csdn.net/wdzxl198/article/details/9306775: 文章摘自: http://www.riabook.cn/doc/designpattern/: 考虑您要设计一个更换各种符号的工具类TextCharChange,您是否会采用这样的方式: 1: public void replace() { 2: switch(getChangeType()) { 3: case RN_TYPE: 4: rep

Design Pattern: Composite 模式

  学习是分享和合作式的! 转载请注明出处:http://blog.csdn.net/wdzxl198/article/details/9417163: 文章摘自: http://www.riabook.cn/doc/designpattern/: 如果以绘图为例的话,一个文字是一个绘图元件,一个线段是一个绘图元件,而一个长方形也是一个绘图元件,这些绘图元件可以组成一个图片,如果将这个图片也 视作一个绘图元件,则这么递回绘图下去,就可以组合成一个较大的.复杂的图形元件,这样的目的可以使用Comp