首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

必备的责任链设计模式

概念

职责链模式(Chain of Responsibility)使多个对象都有机会处理同一个请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

责任链设计模式中的角色

Handler抽象处理者(定义一个处理请求的接口,接口可以定义出一个方法,以设定和返回对下家的引用。通常由一个抽象类或接口实现Concrete Handler具体处理者(具体请求者在接受到请求后,如果满足条件则自己处理请求,否则将请求传到下家。具体处理者持有下家对象的引用)Client客户端(调用者)责任链设计模式类图

链表VS责任链

责任链模式为某个请求创建一个对象链,每个对象依次检查此请求,并对其进行处理,或者将它传给链中的下一个对象。链表是很常见一种数据结构,链表中的每一个节点都是一个对象,并且该对象中存储着下一个节点的指针。链表的基本结构如下:责任链模式的结构其实和链表很类似,存在的区别就是责任链模式中所有的对象都有一个共同的父类( 或接口 ):

在责任链模式中,N 个 Handler 子类都处理同一个请求,只不过具体的职责有所差别。当有一个请求进入时,先经过 AHandler 的 handlerRequest 方法,然后再把请求传递给 BHandler ,B 处理完再把请求传递给 CHandler ,以此类推,形成一个链条。链条上的每一个对象所承担的责任各不相同,这就是责任链模式。实战案例现在我们模拟一个场景:论坛用户发表帖子,但是常常会有用户发一些不良的信息,如广告信息、涉黄信息、涉及政治的敏感词等。这时我们就可以使用责任链模式来过滤用户发表的信息。先定义所有责任链对象的父类:

/**

* 帖子处理器

*/

publicabstractclassPostHandler{

/**

* 后继者

*/

protectedPostHandlersuccessor;

publicvoidsetSuccessor(PostHandlerhandler){

this.successor=handler;

}

publicabstractvoidhandlerRequest(Postpost);

protectedfinalvoidnext(Postpost){

if(this.successor!=null){

this.successor.handlerRequest(post);

}

}

}

父类 Handler 主要封装了传递请求等方法,其中要注意的有以下几点:

successor ( 后继者 ),这个属性很重要,它保存了责任链中下一个处理器。

在 next() 方法中( 方法名自己随便取 ),当请求传递到最后一个责任对象时,已经没有后继者继续处理请求了,因此要对 successor 做判空处理,避免抛出空指针异常。

处理请求的 handlerRequest 的入参和返回类型可以根据实际情况修改,可以在该方法中抛出异常来中断请求。

广告处理器:

/**

* 广告处理器

*/

publicclassAdHandlerextendsPostHandler{

@Override

publicvoidhandlerRequest(Postpost) {

//屏蔽广告内容

Stringcontent=post.getContent();

//.....

content=content.replace("广告","**");

post.setContent(content);

System.out.println("过滤广告...");

//传递给下一个处理器

next(post);

}

}

涉黄处理器:

/**

* 涉黄处理器

*/

publicclassYellowHandlerextendsPostHandler{

@Override

publicvoidhandlerRequest(Postpost) {

//屏蔽涉黄内容

Stringcontent=post.getContent();

//.....

content=content.replace("涉黄","**");

post.setContent(content);

System.out.println("过滤涉黄内容...");

//传递给下一个处理器

next(post);

}

}

敏感词处理器:

/**

* 敏感词处理器

*/

publicclassSensitiveWordsHandlerextendsPostHandler{

@Override

publicvoidhandlerRequest(Postpost) {

//屏蔽敏感词

Stringcontent=post.getContent();

//.....

content=content.replace("敏感词","**");

post.setContent(content);

System.out.println("过滤敏感词...");

//传递给下一个处理器

next(post);

}

}

三个责任链对象的结构基本一致,只有具体的业务处理逻辑不同。上面代码中将所有不健康内容都用 “*” 号代替。调用:

//创建责任对象

PostHandleradHandler=newAdHandler();

PostHandleryellowHandler=newYellowHandler();

PostHandlerswHandler=newSensitiveWordsHandler();

//形成责任链

yellowHandler.setSuccessor(swHandler);

adHandler.setSuccessor(yellowHandler);

Postpost=newPost();

post.setContent("我是正常内容,我是广告,我是涉黄,我是敏感词,我是正常内容");

System.out.println("过滤前的内容为:"+post.getContent());

post=adHandler.handlerRequest(post);

System.out.println("过滤后的内容为:"+post.getContent());

调用结果:看到这里,相信你已经基本掌握了责任链模式。但问题来了,我直接将过滤不良信息写在一个方法里不行吗?比如:

publicclassPostUtil{

publicvoidfilterContent(Postpost){

Stringcontent=post.getContent();

content=content.replace("广告","**");

content=content.replace("涉黄","**");

content=content.replace("敏感词","**");

post.setContent(content);

}

}

相比之下,这种方式更简单,仅仅几行代码就搞定了。为什么还要用责任链模式呢?大家还记得开闭原则吗?如果后面要增加其他的功能,过滤其他类型的内容,我们还得修改上面的 filterContent 方法,违背了开闭原则。如果你是一个框架开发者,你希望别人修改你框架的源码吗?因此我们需要使用责任链模式,能够在不修改已有代码的情况下扩展新功能。经典案例1. Servlet 中的 FilterServlet 中的过滤器 Filter 就是典型的责任链模式,假如我们要给每一次 Http 请求都打印一个 log ,就可以使用 Filter 过滤器来实现:创建一个 Filter 实现 Filter 接口:

publicclassLogFilterimplementsFilter{

@Override

publicvoidinit(FilterConfigfilterConfig)throwsServletException{

}

@Override

publicvoiddoFilter(ServletRequestservletRequest,ServletResponseservletResponse,FilterChainfilterChain)throwsIOException,ServletException{

System.out.println("write log");

filterChain.doFilter(servletRequest,servletResponse);

}

@Override

publicvoiddestroy() {

}

}

然后将这个过滤器配置到 web.xml 中:

LogFilter

com.zhoujun.filter.LogFilter

LogFilter

/*

在上面 LogFilter 类中,我们可以看到 servlet 的责任链是通过 Filter 来实现的,这是一个接口,在 doFilter 中还用到了 FilterChain ,也是一个接口。通过查找源码,发现了 FilterChain 的其中一个实现类:

publicclassPassThroughFilterChainimplementsFilterChain{

@Nullable

privateFilterfilter;

@Nullable

privateFilterChainnextFilterChain;

@Nullable

privateServletservlet;

publicPassThroughFilterChain(Filterfilter,FilterChainnextFilterChain) {

Assert.notNull(filter,"Filter must not be null");

Assert.notNull(nextFilterChain,"'FilterChain must not be null");

this.filter=filter;

this.nextFilterChain=nextFilterChain;

}

publicPassThroughFilterChain(Servletservlet) {

Assert.notNull(servlet,"Servlet must not be null");

this.servlet=servlet;

}

publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse)throwsServletException,IOException{

if(this.filter!=null) {

this.filter.doFilter(request,response,this.nextFilterChain);

}else{

Assert.state(this.servlet!=null,"Neither a Filter not a Servlet set");

this.servlet.service(request,response);

}

}

}

请仔细看这个实现类,你会发现其结构和我们之前的 PostHandler 示例代码极其相似,该类中的 Private FilterChain nextFilterChain; 相当于 PostHandler 中的后继者 Successor 。将我们自定义的 Filter 配置到 web.xml 中的操作就是将该对象添加到责任链上,Servlet 开发者帮我们完成了 setSuccessor() 的操作。

责任链设计模式在Spring、Dubbo、Mybatis等框架中也有大量的应用。总结责任链模式经常用于过滤器,拦截器,事件( 鼠标键盘事件,冒泡事件等 )等场景。优点

请求者和接收者解耦;

可以动态地增加或减少责任链上的对象,或者修改顺序。

缺点

调用者不知道请求可能被哪些责任链对象处理,不利于排错;

用户请求可能被责任链中途拦截,最终未必被真正执行,这点既是优点也是缺点,我们可以利用它做权限控制拦截器。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200508A09CT300?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券