你的位置:首页 > ASP.net教程

[ASP.net教程]命令模式与策略模式之己见


 

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

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

代码类似于:

     /**     * 1、需要解析的*/    String = "<;    /**     * 2、获取*/    MessageType type = SoapMessageFactory.getRpcType(/**     * 3、初始化soapMessage(最好先判断下*/    SoapMessage soap = SoapMessageFactory.initSoap(type);    /**     * 4、解析*/    soap.parse(

      但回过头来仔细看,这哪是什么策略模式呢,明明是更符合命令模式(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:最后写的有点仓促,对于这种事务设计还要继续研究