• 设计模式:命令模式


    命令模式  


    一、引言 
        忙里偷闲,终于动笔了。命令模式是从界面设计中提取出来的一种分离耦合,提高重用 
    的方法。被认为是最优雅而且简单的模式,它的应用范围非常广泛。让我们一起来认识下它 
    吧。 
        先从起源说起。在设计界面时,大家可以注意到这样的一种情况,同样的菜单控件,在 
    不同的应用环境中的功能是完全不同的;而菜单选项的某个功能可能和鼠标右键的某个功能 
    完全一致。按照最差、最原始的设计,这些不同功能的菜单、或者右键弹出菜单是要分开来 
    实现的,你可以想象一下,word  文档上面的一排菜单要实现出多少个“形似神非”的菜单类 
    来?这完全是行不通的。这时,就要运用分离变化与不变的因素,将菜单触发的功能分离出 
    来,而制作菜单的时候只是提供一个统一的触发接口。这样修改设计后,功能点可以被不同 
    的菜单或者右键重用;而且菜单控件也可以去除变化因素,很大的提高了重用;而且分离了 
    显示逻辑和业务逻辑的耦合。这便是命令模式的雏形。 
        下面我们将仔细的讨论下命令模式。 


    二、定义与结构 
         《设计模式》中命令模式的定义为:将一个请求封装为一个对象,从而使你可用不同的 
    请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。 
        看起来,命令模式好像神通广大。其实命令模式的以上功能还要看你是怎么写的——程 
    序总是程序员写出来的,你写啥它才能干啥:) 
        在我看来,其实命令模式像很多设计模式一样——通过在你的请求和处理之间加上了一 
    个中间人的角色,来达到分离耦合的目的。通过对中间人角色的特殊设计来形成不同的模式。 
    当然命令模式就是一种特殊设计的结果。 
        看下命令模式是有哪些角色来组成的吧。 
    1)  命令角色(Command):声明执行操作的接口。有java 接口或者抽象类来实现。 
    2)  具体命令角色(Concrete   Command):将一个接收者对象绑定于一个动作;调用接收 
        者相应的操作,以实现命令角色声明的执行操作的接口。 
    3)  客户角色(Client):创建一个具体命令对象(并可以设定它的接收者)。 
    4)  请求者角色(Invoker):调用命令对象执行这个请求。 
    5)  接收者角色(Receiver):知道如何实施与执行一个请求相关的操作。任何类都可能作 
        为一个接收者。 


        以下是命令模式的类图,从中可以大致的了解到各个角色之间是怎么来协调工作的。 


              Client                  Invoker 


                                                                    Command 


                                                                      execute() 


                                     Receiver                       Concrete 
                                                                    Command 
                                   defaultAction() 


    三、举例 
        本来想接着我的JUnit 分析来讲解命令模式。但是由于在JUnit 中,参杂了其它的模式 
    在里面,使得命令模式的特点不太明显。所以这里将以命令模式在Web 开发中最常见的应 
    用——Struts 中Action 的使用作为例子。 
        在Struts 中Action 控制类是整个框架的核心,它连接着页面请求和后台业务逻辑处理。 
    按照框架设计,每一个继承自Action 的子类,都实现execute 方法——调用后台真正处理业 
    务的对象来完成任务。 


        注:继承自DispatchAction 的子类,则可以一个类里面处理多个类似的操作。这个在这不做讨论。 


        下面我们将Struts 中的各个类与命令模式中的角色对号入座。 
        先来看下命令角色——Action 控制类 


    public class Action { 
         …… 
          /* 
          *可以看出,Action 中提供了两个版本的执行接口,而且实现了默认的空实现。 


          */ 
        public ActionForward execute( ActionMapping mapping, 
                                      ActionForm form, 
                                      ServletRequest request, 
                                      ServletResponse response) 
             throws Exception { 
             try { 
                 return execute(mapping, form, (HttpServletRequest) request, 
                                  (HttpServletResponse) response); 
             } catch (ClassCastException e) { 
                 return null; 
             } 
        } 


        public ActionForward execute( ActionMapping mapping, 
                                      ActionForm form, 
                                   HttpServletRequest request, 
                                   HttpServletResponse response) 
            throws Exception { 
            return null; 
        } 



    下面的就是请求者角色,它仅仅负责调用命令角色执行操作。 


    public class RequestProcessor { 
        …… 
        protected ActionForward processActionPerform(HttpServletRequest request, 
                                               HttpServletResponse response, 
                                               Action action, 
                                               ActionForm form, 
                                               ActionMapping mapping) 
            throws IOException, ServletException { 
            try { 
                return (action.execute(mapping, form, request, response)); 
            } catch (Exception e) { 
                return (processException(request, response,e, form, mapping)); 
            } 
        } 



        Struts 框架为我们提供了以上两个角色,要使用struts 框架完成自己的业务逻辑,剩下 
    的三个角色就要由我们自己来实现了。步骤如下: 
        1)  很明显我们要先实现一个Action  的子类,并重写execute 方法。在此方法中调用 
            业务模块的相应对象来完成任务。 
        2)  实现处理业务的业务类,来充当接收者角色。 
        3)  配置struts-config.xml 配置文件,将自己的Action 和Form 以及相应页面结合起来。 
        4)  编写jsp,在页面中显式的制定对应的处理Action。 
        一个完整的命令模式就介绍完了。当你在页面上提交请求后,Struts 框架会根据配置文 
    件中的定义,将你的 Action              对象作为参数传递给 RequestProcessor               类中的 
    processActionPerform()方法,由此方法调用Action 对象中的执行方法,进而调用业务层中 
    的接收角色。这样就完成了请求的处理。 


    四、Undo、事务及延伸 
        在定义中提到,命令模式支持可撤销的操作。而在上面的举例中并没有体现出来。其实 
    命令模式之所以能够支持这种操作,完全得益于在请求者与接收者之间添加了中间角色。为 
    了实现undo 功能,首先需要一个历史列表来保存已经执行过的具体命令角色对象;修改具 
    体命令角色中的执行方法,使它记录更多的执行细节,并将自己放入历史列表中;并在具体 
    命令角色中添加undo 方法,此方法根据记录的执行细节来复原状态(很明显,首先程序员 
    要清楚怎么来实现,因为它和execute 的效果是一样的)。 
        同样,redo 功能也能够照此实现。 
        命令模式还有一个常见的用法就是执行事务操作。这就是为什么命令模式还叫做事务模 
    式的原因吧。它可以在请求被传递到接收者角色之前,检验请求的正确性,甚至可以检查和 
    数据库中数据的一致性,而且可以结合组合模式的结构,来一次执行多个命令。 
        使用命令模式不仅仅可以解除请求者和接收者之间的耦合,而且可以用来做批处理操 
    作,这完全可以发挥你自己的想象——请求者发出的请求到达命令角色这里以后,先保存在 
    一个列表中而不执行;等到一定的业务需要时,命令模式再将列表中全部的操作逐一执行。 
        哦,命令模式实在太灵活了。真是一个很有用的东西啊! 


    五、优点及适用情况 
        由上面的讲解可以看出命令模式有以下优点: 
    1)  命令模式将调用操作的请求对象与知道如何实现该操作的接收对象解耦。 
    2)  具体命令角色可以被不同的请求者角色重用。 
    3)  你可将多个命令装配成一个复合命令。 
    4)  增加新的具体命令角色很容易,因为这无需改变已有的类。 
        GOF 总结了命令模式的以下适用环境。 
    1)  需要抽象出待执行的动作,然后以参数的形式提供出来——类似于过程设计中的回调机 
        制。而命令模式正是回调机制的一个面向对象的替代品。 
    2)  在不同的时刻指定、排列和执行请求。一个命令对象可以有与初始请求无关的生存期。 
    3)  需要支持取消操作。 
    4)  支持修改日志功能。这样当系统崩溃时,这些修改可以被重做一遍。 
    5)  需要支持事务操作。 


    六、总结 
        从面向对象的角度来看,命令模式是不完美的。命令角色仅仅包含一个方法,没有任何 
    属性存在。这是将函数层面的人物提升到了类的层面。但不可否认的是:命令模式很成功的 

    解决了许多问题,正如遍布在Struts 那样。 

    下载:

    http://download.csdn.net/detail/undoner/5335717

    深入浅出设计模式-中文版



    1.为什么命令模式归属于行为模式类?

    行为型模式描述的是类或对象如何交互以及如何分配责任,而对于命令模式通 过装配对象或类之间的函数调用链,把责任进行分配的过程,这一特征归属于行为型模式。

    2.定义中提到,不同的请求对客户进行参数化什么意思?

    把客户的开机请求封装成为一个OpenCommand对象,客户的开机操作就变成了执 行OpenCommand对象的方法了。如果还有其它的命令对象,比如让机器重启的 ResetCommand对象;那么客户按下按钮的动作,就可以用这不同的命令对象去匹配,也 就是对客户进行参数化了。

       客户按下一个按钮,到底是开机还是重启,那要看参数化配置的是哪一个具体 的按钮对象,如果参数化的是开机的命令对象,那就执行开机的功能,如果参数化的是 重启的命令对象,那就执行重启的功能。虽然按下的是同一个按钮,但是请求是不同的 ,对应执行的功能也就不同了。

     所谓命令模式的参数化配置,指的是:可以用不同的命 令对象,去参数化配置客户的请求。

    3.在定义中提到,命令模式支持可撤销等操作,是如何实现的?

    命令模式之所以能够支持这种操作,完全得益于在请求者与接收者之间添加了 中间角色。为了实现undo功能,首先需要一个历史列表来保存已经执行过的具体命令角 色对象;修改具体命令角色中的执行方法,使它记录更多的执行细节,并将自己放入历 史列表中;并在具体命令角色中添加undo方法,此方法根据记录的执行细节来复原状态



  • 相关阅读:
    浅谈javascript中一些与DOM元素相关的属性名词
    一个弹出层插件
    模仿京东商城的一个图片轮播
    jQuery设计思想
    模仿自动完成效果
    linux下如何用GDB调试c++程序(转)
    Linux makefile 教程
    boost库解析
    记一次MongoDB性能问题,附原理解析
    深入分析Linux内核源码
  • 原文地址:https://www.cnblogs.com/wuyida/p/6301015.html
Copyright © 2020-2023  润新知