概述
责任链模式(Chain of Responsebility Pattern)为请求创建一个接收者对象的链,该设计模式属于行为型模式。
责任链模式是一种对象行为型模式,避免请求发送者与接收者的耦合。在该模式中,请求接收者的多个对象对其后继进行引用而连接起来形成一条链。请求在该链上传递,直到请求被该链上的某个对象处理。若接收者对象有权限处理该请求,则对该请求进行处理,否则,将该请求转发到该对象引用的后继对象来请求,以此类推。
发出请求的客户端并不知道请求会被链上的哪一个对象处理,这就使系统可以在不影响客户端的情况下动态地重新组织分配责任。
责任链模式涉及到的角色
抽象处理(Handler)角色–核心:通常为一个抽象类,由于不同的具体处理者对象处理请求的方式不同,所以在该类中定义一个处理请求的抽象方法,具体处理者继承自该抽象类。每个具体处理者的后继引用也是一个具体处理者,所以在抽象处理者类中定义一个抽象处理者类型的对象,作为其对后继对象的引用。通过该引用,具体处理者可形成一条链。
具体处理者角色:为抽象处理者的子类,在该类中实现了抽象处理者类中定义的处理方法,用来实际处理客户端的请求。通常在处理前进行判断,看是否有相应的权限处理某个客户端请求,若有权限则进行处理,否则就将请求转发至后继对象来处理。具体处理者中可访问下一个对象,以便请求的转发。
链
所谓的"链"指的是多个节点的有序集合,链的各个节点可灵活拆分重组。
实例
模拟一个请假流程,当请假天数在3天以内时可向项目经理申请,超过3天7天以内可向总监申请,超过7天的就要向老板申请。
创建一个抽象处理类
在该类中创建抽象的请假处理方法
public abstract class LeaveHandler {
//定义抽象处理者类型的对象,并设置为protected类型,以便其子类访问
protected LeaveHandler successor;
public void setSuccessor(LeaveHandler successor) {
this.successor = successor;
}
//定义抽象请假处理方法
public abstract void disposeLeave(int day);
}
抽象处理类中定义了抽象处理者类型的对象作为后继对象的引用,并在该类中定义抽象处理方法,具体实现由其子类完成
具体处理类
项目经理类
public class ProjectLeader extends LeaveHandler {
@Override
public void disposeLeave(int day) {
//权限判断
if(day <= 3) {
//有权限,执行具体处理方法
System.out.println(day + "天的假期,项目经理审批通过。。");
} else {
//没有权限,转发给后继处理者(上级)
successor.disposeLeave(day);
}
}
}
总监类
public class MajorDomo extends LeaveHandler {
@Override
public void disposeLeave(int day) {
//权限判断
if(day <= 7) {
//有权限,执行具体处理方法
System.out.println(day + "天的假期,总监审批通过。。");
} else {
//没有权限,转发给后继处理者(上级)
successor.disposeLeave(day);
}
}
}
老板类
public class Boss extends LeaveHandler {
@Override
public void disposeLeave(int day) {
//无需进行权限判断(只要老板同意,请一年假也行)
System.out.println(day + "天的假期,老板审批通过。。");
}
}
具体处理者是抽象处理者的子类,若当前具体处理者有权限处理当前的请求,则对请求进行处理,反之,则将该请求转发给后继对象(上级)来处理。
工厂类
public class LeaveHandlerFactory {
public static LeaveHandler createHandler() {
LeaveHandler projectLeader = new ProjectLeader();
LeaveHandler majorDomo = new MajorDomo();
LeaveHandler boss = new Boss();
//定义后继对象的引用
projectLeader.setSuccessor(majorDomo);
majorDomo.setSuccessor(boss);
return projectLeader;
}
}
测试方法(客户端)
public class ChainPatternDemo {
public static void main(String[] args) {
LeaveHandler handler = LeaveHandlerFactory.createHandler();
handler.disposeLeave(8);
}
}
输出结果
若要在系统中增加一个新的具体处理者,如增加一个人事部经理角色可处理10天以内的请假申请,需编写一个新的具体处理者类:
public class PersonLeader extends LeaveHandler {
@Override
public void disposeLeave(int day) {
//权限判断
if(day <= 10) {
//有权限,执行具体处理方法
System.out.println(day + "天的假期,人事部经理审批通过。。");
} else {
//没有权限,转发给后继处理者(上级)
successor.disposeLeave(day);
}
}
}
工厂类改造为
LeaveHandler personLeader = new PersonLeader();
//修改责任链
majorDomo.setSuccessor(personLeader);
personLeader.setSuccessor(boss);
客户端测试类不变,此时的输出结果为
新增具体处理者类对原有类库无任何影响,无需修改已有类的源码,符合"开闭原则"。
纯与不纯的责任链模式
责任链模式可分为纯的责任链模式和不纯的责任链模式
纯的责任链模式
一个纯的责任链模式要求一个具体的处理者要么对请求全部处理完,要么将请求转发给后继处理者,不能出现一个具体处理者对请求处理部分又将该请求转发给后继处理者的情况。
并且要求对于所接收到的请求必须被一个具体处理者处理,不能出现请求被转发到最后一个处理者仍然未被处理的情况。
上面的例子就是使用的纯的责任链模式。
不纯的责任链模式
在一个不纯的责任链模式中允许一个请求被一个处理者处理部分后继续转发给后继处理者,或只处理部分。
也可以在该责任链中对一个请求完全不做处理。
Java AWT 1.0及以前的版本中的事件处理模型用的就是不纯的责任链模式,这种事件处理机制又叫事件浮升(Event Bubbing)机制。从Java 1.1之后,JDK使用观察者模式代替责任链模式来处理事件。目前,JavaScript中仍然可使用这种事件浮升机制来进行事件处理。
注:不纯的责任链模式要比纯的责任链模式的应用更加广泛。
总结
优点
实现了解耦,符合"开闭原则"。客户端不需知道具体传递过程,只需知道最终结果是否被处理了。
链表的结构可以被灵活地更改重组,增强给具体对象指派职责的灵活性,以及具体对象之间的链接顺序。
缺点
调用时间的问题,若请求在链表的开始几个具体对象中被处理了还好,若被传递到最后一个具体对象才被处理,并且当前链表较长的时候,其效率必定会比不使用责任链模式的方式要低一些。
内存问题,若构造出过多的链表节点对象,但有些对象在应用场景不会被经常用到,就会造成内存的浪费。
可能不容易观察运行时的特征(请求被转发到了链表的哪一个节点),不利于调试。
使用实例
Web应用开发中创建一个过滤器(Filter)链来对请求数据进行过滤;
在工作流系统中实现公文的分级审批;
Java Web中Tomcat对Encoding的处理;
Struts2的拦截器等。
使用责任链模式可很好地解决此类问题。
使用场景
有多个对象可处理同一个请求,具体哪一个对象处理该请求待运行时刻再确定,客户端只需将请求提交到链上,无需关系具体处理请求的对象。
责任链模式中的链表可以是一条链,可以是一棵树,也可以是一个环。最常见的是直线型,请求在该链上传递,直到链上的某个具体对象可处理该请求。在同一时刻,命令只允许由一个对象传递到另一个对象,不能传给多个对象。
责任链模式并不创建责任链,具体的创建工作由系统的其他部分完成,一般是在使用该责任链的客户端中创建责任链。
注:慎用责任链模式,若有其他更好的模式可代替责任链模式的话,不要用责任链模式。