三、举例
本来想接着我的JUnit分析来讲解命令模式。但是由于在JUnit中,参杂了其它的模式在里面,使得命令模式的特点不太明显。所以这里将以命令模式在Web开发中最常见的应用——Struts中Action的使用作为例子。
在Struts中Action控制类是整个框架的核心,它连接着页面请求和后台业务逻辑处理。按照框架设计,每一个继承自Action的子类,都实现execute方法——调用后台真正处理业务的对象来完成任务。
注:继承自DispatchAction的子类,则可以一个类里面处理多个类似的操作。这个在这不做讨论。
下面我们将Struts中的各个类与命令模式中的角色对号入座。
先来看下命令角色——Action控制类publicclassAction {
……/**可以看出,Action中提供了两个版本的执行接口,而且实现了默认的空实现。*/publicActionForward execute( ActionMapping mapping,
ActionForm form,
ServletRequest request,
ServletResponse response)throwsException {try{returnexecute(mapping, form, (HttpServletRequest) request,
(HttpServletResponse) response);
}catch(ClassCastException e) {returnnull;
}
}publicActionForward execute( ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)throwsException {returnnull;
}
}
下面的就是请求者角色,它仅仅负责调用命令角色执行操作。publicclassRequestProcessor {
……protectedActionForward processActionPerform(HttpServletRequest request,
HttpServletResponse response,
Action action,
ActionForm form,
ActionMapping mapping)throwsIOException, 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)需要支持事务操作。