责任链模式实例讲解

来源:互联网 发布:php开源erp系统 编辑:程序博客网 时间:2024/06/13 01:05

 老狗可能是国内最早接触到设计模式的程序员之一,却从来没想到过要把自己的心得和理论总结一下,写成文章与众狗狗们分享。等到想起来这么做时,发现关于设计模式的书籍已经汗牛充栋了,老狗就是这样,吃屎都抢不到尖尖!如今只好捡一些比较冷僻的,别人较少提及的模式来细说一下了,庶几能为众狗狗们拾遗补阙尔。
   责任链模式大约就属于这种冷僻的模式,大家在实际运用中一般较少用到。然而这是老狗偏爱的模式之一,为何呢?请看对责任链模式的描述:
责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。
   ---以上描述摘自阎宏博士的《JAVA与模式》一书,按宇宙惯例在此向阎博士致谢。
   拿我大天朝神秘的有关部门来比方,责任链上的每个对象就像我天朝的某个有关部门,每个部门都只管自己职责内的事。比我天朝有关部门好的一点是,超出职责的事情,责任链上的对象不会把案子踢回给调用者,让调用者自己去想办法,而是依次传递下去,直到某个部门把案子处理完再返回给调用者。这样对于调用者来说,处理的过程是完全透明的。
   老狗偏爱责任链模式,是因为这个模式至少体现了设计原则中的三条:
   1. 接口隔离原则:责任链上的每个对象不多不少,该干的事都要干,不该干的事坚决不干;
   2. 迪米特原则:最少知道,每个对象只知道自己能不能处理这个案子,不能处理,就丢给下一个对象,至于下一个对象能不能处理、怎么处理,不知道也不用去知道;
   3. 开闭原则:责任链模式完美的体现了开闭原则,当要处理的业务发生了改变,特别是增加了新的业务,无需改写原有的处理类,增加一个新类并添加到责任链上即可。要修改的地方仅仅是组装责任链的部分,如果把组装责任链写成可配置式的,连这部分代码都不需要修改。
   实际上责任链模式众狗狗们不应该陌生,J2EE框架的Filter类就是典型的责任链模式,大家可以去研究一下。
   责任链模式可以应用在你想不到的地方,老狗用实例来说明,老狗的微信快速开发框架项目中就用到了责任链模式,解决什么问题呢?你想都想不到,老狗用来解析Xml!请看下面的例子:

package com.halo.wechat.xml.utils.converters;

import org.w3c.dom.Node;

import com.halo.wechat.xml.utils.XmlConvertException;

/**
* @file NodeConverter.java
* @author Junior
* @date 2015年8月5日 下午8:30:19
* @version 1.0
*/
public abstract class NodeConverter {
   private NodeConverter nextConverter;

   protected String quotedCDATA(String data) {
       return "<![CDATA[" + data + "]]>";
   }

   /**
    * @return the nextConverter
    */
   public NodeConverter getNextConverter() {
       return nextConverter;
   }

   /**
    * @param nextConverter
    *            the nextConverter to set
    */
   public void setNextConverter(NodeConverter nextConverter) {
       this.nextConverter = nextConverter;
   }

   public abstract <T> T convert(Class<T> clazz, Node node) throws XmlConvertException;

   public abstract String reverse(Object obj) throws XmlConvertException;
}

   这是责任链上所有转换Xml对象的抽象基类,这里没有严格遵循责任链模式,实际上责任链模式的所有对象都应该实现一个接口,我改成继承同一个基类了,因为要在基类里实现一些公用方法,比如:protected String quotedCDATA(String data)

private NodeConverter nextConverter
   这个成员就是责任链对象持有的下一个对象,如果任务自己不能处理,就调用该成员的处理方法,直到最后一个责任链对象。

public abstract <T> T convert(Class<T> clazz, Node node) throws XmlConvertException;
public abstract String reverse(Object obj) throws XmlConvertException;
   以上两个方法是处理任务的,convert方法把Xml的节点转换成clazz指定的数据类型,reverse方法正相反,分析obj对象的数据类型,然后转换成相应的Xml节点。


   再来看其中一个子类,这个子类专门负责解析String类型:

package com.halo.wechat.xml.utils.converters;

import org.w3c.dom.Node;

import com.halo.wechat.xml.utils.XmlConvertException;

/**
* @file StringConverter.java
* @author Junior
* @date 2015年8月5日 下午8:56:48
* @version 1.0
*/
public class StringConverter extends NodeConverter {

   @SuppressWarnings("unchecked")
   @Override
   public <T> T convert(Class<T> clazz, Node node) throws XmlConvertException {
       // TODO 如果clazz是String,直接返回node的值
       if (clazz.getName().equals("java.lang.String")) {
           return (T) node.getFirstChild().getNodeValue();
       } else {
           if (null != this.getNextConverter()) {
return this.getNextConverter().convert(clazz, node);
           } else {
return null;
           }
       }
   }

   @Override
   public String reverse(Object obj) throws XmlConvertException {
       if (obj.getClass().getName().equals("java.lang.String")) {
           return quotedCDATA(String.valueOf(obj));
       } else {
           if (null != this.getNextConverter()) {
return this.getNextConverter().reverse(obj);
           } else {
return "";
           }
       }
   }

}

   如果不是String类型就直接丢给下一个NodeConverter,直到某个能处理该类型的NodeConverter接手。看出来了吗,责任链模式一个显而易见的好处就是,代码非常简洁。如果不用责任链模式,程序可能会是这样:

if (className.equals("byte")) {
   return (T) (new Byte(node.getFirstChild().getNodeValue()));
} else if (className.equals("short")) {
   return (T) (new Short(node.getFirstChild().getNodeValue()));
} else if (className.equals("int")) {
   return (T) (new Integer(node.getFirstChild().getNodeValue()));
} else if (className.equals("long")) {
   return (T) (new Long(node.getFirstChild().getNodeValue()));
} else if (className.equals("float")) {
   return (T) (new Float(node.getFirstChild().getNodeValue()));
} else if (className.equals("double")) {
   return (T) (new Double(node.getFirstChild().getNodeValue()));
} else if (className.equals("char")) {
   return (T) (new Character(node.getFirstChild().getNodeValue().charAt(0)));
} else if (className.equals("boolean")) {
   return (T) (new Boolean(node.getFirstChild().getNodeValue()));
} else if (className.equals("java.lang.String")) {
   return (T) node.getFirstChild().getNodeValue();
} else if...  
// 我还没完!还有几万种数据类型要处理!

   这还仅仅处理了Java的基础数据类型,要把所有可能出现的数据类型都处理完,光写else if就能让程序狗狗们流血漂橹、积尸成山!有狗狗说:“那你用责任链模式不也要创建无数个转换各种数据类型的子类吗?”“too naive!”,实际上我只实现了PrimitiveConverter、StringConverter、TimestampConverter三个子类,因为我这批NodeConverter是针对微信公众平台接口的,目前只用处理这么几种数据类型。如果以后有更多的数据类型需要处理,也不用修改现有的代码,直接增加一个子类就是!
   有不服气的狗狗又说:“那我只增加一个else if分支也可以。”这时开闭原则站出来说:“那老子怎么办?”

 实际上老狗还耍了个滑头,我这个项目已经放到github上去开源了,有兴趣的朋友可以去

https://github.com/junior9919/WTF

上clone我的项目,帮我补完这批子类啊!设计模式的魅力就在这里,好的模式使得合作开发变得容易。


欢迎扫码关注我的微信公众号


junior9919


打赏吧!我的勇士! ---- 大检察官怀特迈恩


0 0
原创粉丝点击