yunnick

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

 

      以前项目写过关于TR069协议报文处理的代码(主要是基于SOAP协议发送一些远程命令并处理响应),在设计的时候,想的是应用策略模式对报文进行解析处理,

下图是主要代码结构(和策略模式很像)

代码类似于:

         /**
         * 1、需要解析的XML
         */
        String xml = "<xml>...</xml>";
        /**
         * 2、获取xml类型
         */
        MessageType type = SoapMessageFactory.getRpcType(xml);
        /**
         * 3、初始化soapMessage(最好先判断下xml类型)
         */
        SoapMessage soap = SoapMessageFactory.initSoap(type);
        /**
         * 4、解析xml,可通过header和body变量获取结果
         */
        soap.parse(xml);

      但回过头来仔细看,这哪是什么策略模式呢,明明是更符合命令模式(message对应command,paser对应receiver),策略模式和命令模式这么相似吗,这么容易混淆?于是我就进行分析比较。

基本定义:

策略模式:定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。该模式使得算法可独立于它们的客户变化。
命令模式:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

     以SOAP报文解析为例,先从设计思路分析。

     1、最初想法:将所有报文都统一看做为SoapMessage,定义不同的算法进行封装,对不同的报文进行不同的解析。认为是策略模式。

     2、进一步:其实Soap报文根据不同的type相当于不同请求(命令),定义并封装不同的算法,是与相应的报文对应的。认为是命令模式。

     第一种思路与策略模式的定义对比后就可以发现明显是不对的,因为策略模式会要求不同算法相互替换,而不同报文解析是不能替换的。

      策略模式侧重同一个问题可用不同的可替换的算法去解决,而实际使用中,说的苛刻些,调用者只取其一,执行一次算法即可;比如对数组排序,用插入排序算法排好序后,你不会再用堆排序重新拍一遍,又比如你用支付宝支付后,不会再用微信支付一遍;而这些场景都可以应用策略模式。

策略模式网上的例子很多,我觉得这篇文章总结的特别好:http://www.cnblogs.com/justinw/archive/2007/02/06/641414.html

 

      ====================分割线===================================

      而对于命令模式更多的解释是这样:不同问题使用不同算法或者侧重将请求的发送者和接收者解耦,这难道真的抓住命令模式的本质了吗?

      首先看一下命令模式的UML图:

      有5个角色:Invoker(调用者)和Receiver(接收者)、command接口和ConcreteCommand实现类以及Client。

首先注明:如果对于跨进程或者跨机器的这种远程的方法调用,使用类似命令模式的这种构造方法相信没有人有异议,下面对命令模式的分析侧重单一进程。

      网上很多人都使用下面的例子进行解释:

 1 class Invoker {
 2     private Command command;
 3     public Invoker(Command command) {
 4         this.command = command;
 5     }
 6     public void action(){
 7         this.command.execute();
 8     }
 9 }
10 
11 abstract class Command {
12     public abstract void execute();
13 }
14 
15 class ConcreteCommand extends Command {
16     private Receiver receiver;
17     public ConcreteCommand(Receiver receiver){
18         this.receiver = receiver;
19     }
20     public void execute() {
21         this.receiver.doSomething();
22     }
23 }
24 
25 class Receiver {
26     public void doSomething(){
27         System.out.println("接受者-业务逻辑处理");
28     }
29 }
30 
31 public class Client {
32     public static void main(String[] args){
33         Receiver receiver = new Receiver();
34         Command command = new        ConcreteCommand(receiver);
35         Invoker invoker = new Invoker(command);
36         invoker.action();
37     }
38 }
View Code

      上面的代码看似和命令模式的类图很吻合,我觉得更像是为了模式而模式所拼凑出的一段代码,没有太多实际意义,甚至会误导人。这段代码最多体现了命令模式定义的前半部分:“将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化”,而即使用来说明这一部分,代码也有部分是多余的,那就是Receiver;main方法的执行过程更像一个跷跷板,Invoker翘一下Command,Command翘一下Receiver,而Receiver执不执行、什么时候执行,为什么由别的对象说了算呢?!或者直接去掉Receiver,留下Invoker,Command和ConcreteCommand,Invoker保持对Command的引用,而ConcreteCommand封装自己的特性,命令怎么执行由ConcreteCommand的逻辑确定,而这也体现了面向接口编程原则,Command依旧可独立于Invoker变化。根据上面的分析,我总结了命令模式的一种变形,称之为“简约型命令模式”。

一)简约型命令模式

UML图:

      这里去掉了Receiver,Invoker持有对Command的引用,每个Command都有唯一的方法Execute()。在Client中加入了命令队列(Queue为虚线,表示可有可无),Client可以一次生成一个命令,马上执行,或者一次生成若干命令放入队列,依次执行。

      这种方式真是像极了“策略模式”,这可能就是我把命令模式和策略模式混淆的原因,而这种模式不能称为策略模式,就是因为算法是不可相互替代的,称之为“简约型命令模式”。

可参考:http://www.jdon.com/designpatterns/command.htm

 

      既然有简约型,就对应会有较复杂的类型,当然主要还是参考定义,注重实现其后半部分:“对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。”,我称之为“异步事务性命令模式”。

二)异步事务型命令模式

当然还是先看UML图:

      结构与原始的命令模式基本一样,唯一修改的是Receiver对象,增加了一个pool属性,用来接收Command,Invoker每次调用命令,并不会直接执行而是先写入pool中,待Receiver等到合适时机去选择执行,这种结构把Invoker和Receiver彻底解耦,Receiver完全不受Invoker制约,也完全体现了一个Receiver的角色。入队的同时,还可以将命令持久化,以一种Write ahead log的形式对其进行记录,还有ConcreteCommand中有个state属性,很多介绍命令模式的文章对没有对其进行说明,我认为这是实现事务必不可少的(可以用来指示此命令与某些命令是一组的,要么都执行成功,要么异常回滚,或者可以结合Composite模式使用),Receiver中添加的undo()和redo()方法就是和事务相关的操作,如果服务异常退出,重启后,使用redo()方法结合wal日志进行恢复,如果事务执行中断就需要根据命令执行前的状态进行恢复。

      目前还没有看到相关的实例。

最后还说一句:“不同问题使用不同算法或者侧重将请求的发送者和接收者解耦”只是命令模式的一种表现形式,解耦、不同问题不同算法,太容易让人和其他的模式相混淆,还是应该回到最初的定义去理解。

PS:最后写的有点仓促,对于这种事务设计还要继续研究
posted on 2015-10-29 16:41  yunnick  阅读(3599)  评论(0编辑  收藏  举报