一、定义
使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链。并沿着这条链传递该请求,直到有一个对象处理它为止。
责任链模式的重点在于”链”上,在责任链模式中,很多对象由每一个对象对其下家的引用而接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。
二、模式结构
2.1类图
2.2 角色
1. Handler
定义一个处理请求的接口。(可选)实现后继链。
2. ConcreteHandler
处理它所负责的请求。可访问它的后继者。如果可处理该请求,就处理;否则将该请求转发给它的后继者。3. 3 Client
向链上的具体处理者(ConcreteHandler)对象提交请求。
三、例子
案例:请假申请,如果请假一天,只需班长同意;请假一周则需要辅导员同意,请假一个月则需学校同意。
LeaveHandler 请假Handler基类
public abstract class LeaveHandler {
private LeaveHandler nextHandler;
//处理请假申请
public abstract void deal_Leave_request(int days);
//下一个处理者的getter 和setter
public void setNextHandler(LeaveHandler nextHandler) {
this.nextHandler = nextHandler;
}
public LeaveHandler getNextHandler() {
return nextHandler;
}
}
DayLeaveConcreteHandler 处理请一天假的情况
public class DayLeaveConcreteHandler extends LeaveHandler
{
@Override
public void deal_Leave_request(int days)
{
if (days <= 1)
{
System.out.println("请假1天只需要班长批准");
}
else
{
this.getNextHandler().deal_Leave_request(days);
}
}
}
WeekLeaveConcreteHandler处理请假一周以内的情况
public class WeekLeaveConcreteHandler extends LeaveHandler
{
@Override
public void deal_Leave_request(int days)
{
if (days > 1 && days <= 7)
{
System.out.println("请假"+days+"天需要辅导员批准");
}
else
{
this.getNextHandler().deal_Leave_request(days);
}
}
}
MonthLeaveConcreteHandler处理请假大于一周的情况
public class MonthLeaveConcreteHandler extends LeaveHandler
{
@Override
public void deal_Leave_request(int days)
{
if (days > 7)
{
System.out.println("请假"+days+"天需要学校批准");
}
}
}
四、优缺点及应用
4.1、适用场合
1.有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
2.你想在不明确指定接受者的情况下,向多个对象中的一个提交请求。
3.处理一个请求的对象集合应被动态指定。
4.2、优点
降低了耦合、提高了灵活性
4.3、缺点
- 如果职责链结构比较复杂,会产生很多的内存垃圾对象。
- 责任链模式可能会带来一些额外的性能损耗,因为它要从链子开头开始遍历。
- 责任链有一个缺点是大家在开发的时候要注意:调试不是很方便,特别是链条比较长,环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。
五、纯的与不纯的责任链模式
一个纯的责任链模式要求一个具体的处理者对象只能在两个行为中选择一个:一是承担责任,而是把责任推给下家。不允许出现某一个具体处理者对象在承担了一部分责任后又把责任向下传的情况。
在一个纯的责任链模式里面,一个请求必须被某一个处理者对象所接收;在一个不纯的责任链模式里面,一个请求可以最终不被任何接收端对象所接收。
纯的责任链模式的实际例子很难找到,一般看到的例子均是不纯的责任链模式的实现。有些人认为不纯的责任链根本不是责任链模式,这也许是有道理的。但是在实际的系统里,纯的责任链很难找到。如果坚持责任链不纯便不是责任链模式,那么责任链模式便不会有太大意义了。
六、过滤器例子
现在我们的需求是处理用户的请求,将用户提交的字符串信息进行层层处理,同时在处理完成之后返回结果时,也要对返回的字符串进行层层处理,而处理返回的情况时其处理的顺序和先前是正好相反的顺序。
首先建立用户的请求和接收对象Request和Response:
Request
public class Request {
String requestStr;
public String getRequestStr() {
return requestStr;
}
public void setRequestStr(String requestStr) {
this.requestStr = requestStr;
}
}
Response
public class Response {
String responseStr;
public String getResponseStr() {
return responseStr;
}
public void setResponseStr(String responseStr) {
this.responseStr = responseStr;
}
}
我们将处理用户信息的逻辑抽象成为一个个的过滤器,进一步抽象出过滤器接口Filter
public interface Filter {
public void doFilter(Request request, Response response,FilterChain chain);
}
注意在Filte接口中doFilter方法参数中有FilterChain的一个变量,我们再建立FilterChain类:
import java.util.ArrayList;
import java.util.List;
public class FilterChain implements Filter {
List<Filter> filters = new ArrayList<Filter>();
int index = 0;
public FilterChain addFilter(Filter f) {
this.filters.add(f);
return this;
}
@Override
public void doFilter(Request request, Response response, FilterChain chain) {
if (index == filters.size())
return;
Filter f = filters.get(index);
index++;
f.doFilter(request, response, chain);
}
}
在FilterChain中继承了Filter接口,从而实现了doFilter方法,在FilterChain中又有一个index变量,该变量是用来标记当前访问的是哪一个过滤器,这些过滤器是存放在ArrayList中的,这样用户在使用的时候就可以实现自己的过滤器,编写自己的处理逻辑,从而将自己的过滤器添加到ArrayList中,再调用FilterChain的doFilter方法遍历整个责任链。
下面我们编写三个过滤器:
HTMLFilter
/**
* 过滤HTML中的脚本元素
*/
public class HTMLFilter implements Filter {
@Override
public void doFilter(Request request, Response response,FilterChain chain) {
System.out.println("经过HTMLFilter,脚本元素被过滤");
request.requestStr = request.getRequestStr().replace("<", "[")
.replace(">", "]");
chain.doFilter(request, response, chain);
response.responseStr += "-------->HTMLFilter";
}
}
SenitiveFilter
public class SenitiveFilter implements Filter {
@Override
public void doFilter(Request request, Response response, FilterChain chain) {
System.out.println("经过SenitiveFilter,敏感词语被过滤");
request.requestStr = request.getRequestStr().replace("敏感", "和谐");
chain.doFilter(request, response, chain);
response.responseStr += "------>SesitiveFilter";
}
}
FaceFilter
public class FaceFilter implements Filter {
@Override
public void doFilter(Request request, Response response, FilterChain chain) {
System.out.println("经过FaceFilter,笑脸符号被替换");
request.requestStr = request.getRequestStr().replace(":)","^V^");
chain.doFilter(request, response, chain);
response.responseStr += "------->FaceFilter";
}
}
场景类
public class Client {
public static void main(String[] args) {
String message = "敏感词汇,<script> :)";
System.out.println("原始请求: "+message);
Request request = new Request();
request.setRequestStr(message);
Response response = new Response();
response.setResponseStr("response");
FilterChain fc = new FilterChain();
fc.addFilter(new HTMLFilter()).addFilter(new SenitiveFilter());
FilterChain fc2 = new FilterChain();
fc2.addFilter(new FaceFilter());
fc.addFilter(fc2);
fc.doFilter(request, response,fc);
System.out.println("request = " + request.getRequestStr());
System.out.println("response = " + response.getResponseStr());
}
}
运行结果
原始请求: 敏感词汇,<script> :) 经过HTMLFilter,脚本元素被过滤 经过SenitiveFilter,敏感词语被过滤 经过FaceFilter,笑脸符号被替换 request = 和谐词汇,[script] ^V^ response = response------->FaceFilter------>SesitiveFilter-------->HTMLFilter |
七、相关模式
观察者模式也可以实现请求的传递,比如一个事件发生了,通知了观察者,同时观察者又作为一个被观察者,通知了另外一个观察者,这也形成了一个事件广播链,这和我们今天讲的责任链是有区别的:
- 受众数量不同。观察者广播链式可以1:N的方式广播,而责任链则要求是的1:1的传递,必然有一个且只有一个类完成请求的处理;
- 请求内容不同。观察者广播链中的信息可以在传播中改变,但是责任链中的请求是不可改变的;
- 处理逻辑不同。观察者广播链主要用于触发联动动作,而责任链则是对一个类型的请求按照既定的规则进行处理。
八、Java中应用
责任链模式(Chain of Responsibility)是Web项目中最经常用到的一个设计模式,比如是Apache Tomcat 的对Encoding的处理,Struts2 的拦截器, jsp servlet 的Filter 等等,都是基于责任链模式设计的。
参考
http://www.cnblogs.com/itTeacher/archive/2012/12/05/2803618.html
http://blog.youkuaiyun.com/liuchangqing123/article/details/7386912