标签:
以前项目写过关于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 }
上面的代码看似和命令模式的类图很吻合,我觉得更像是为了模式而模式所拼凑出的一段代码,没有太多实际意义,甚至会误导人。这段代码最多体现了命令模式定义的前半部分:“将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化”,而即使用来说明这一部分,代码也有部分是多余的,那就是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:最后写的有点仓促,对于这种事务设计还要继续研究
标签:
原文地址:http://www.cnblogs.com/yunnick/p/4920950.html