在现实生活中,常常会出现这样的事例:一个请求有多个对象可以处理,但每个对象的处理条件或权限不同。例如,公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据自己要请假的天数去找不同的领导签名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这增加了难度。这样的例子还有很多,如找领导出差报销。
在计算机软硬件中也有相关例子,如总线网中数据报传送,每台计算机根据目标地址是否同自己的地址相同来决定是否接收;还有异常处理中,处理程序根据异常的类型决定自己是否处理该异常;还有 Struts2 的拦截器、JSP 和 Servlet 的 Filter 等,所有这些,如果用责任链模式都能很好解决。
责任链模式的定义
责任链(Chain of Responsibility
)模式的定义
:是将链中每一个节点看做是一个对象,每个节点处理的请求都不同,且内部自动维护下一个节点对象。当一个请求从链式的 首端发出,会沿着链的路径依次传递个下一个节点对象,直至有对象处理这个请求为止。属于行为型模式
注意:责任链模式也叫职责链模式。
在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,所以责任链将请求的发送者和请求的处理者解耦了。
责任链模式的结构
通常情况下,可以通过数据链表来实现职责链模式的数据结构。
职责链模式主要包含以下角色:
抽象处理者(Handler
)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
具体处理者(Concrete Handler
)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
客户类(Client
)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
责任链模式的实现
责任链模式常于建造者模式一起使用,达到链式编程的效果
反例:登录操作时,获取用户名和密码后,会进行一步步判断
/**
* 不使用责任链模式时,代码中大量的if语句
*/
public class MemberService {
public void login(String loginName,String loginPass){
if(StringUtils.isEmpty(loginName) ||
StringUtils.isEmpty(loginPass)){
System.out.println("用户名和密码为空");
return;
}
System.out.println("用户名和密码不为空,可以往下执行");
Member member = checkExists(loginName,loginPass);
if(null == member){
System.out.println("用户不存在");
return;
}
System.out.println("登录成功!");
if(!"管理员".equals(member.getRoleName())){
System.out.println("您不是管理员,没有操作权限");
return;
}
System.out.println("允许操作");
}
private Member checkExists(String loginName,String loginPass){
Member member = new Member(loginName,loginPass);
member.setRoleName("管理员");
return member;
}
}
使用责任链模式:
/**
* 用户登录实体类
*/
public class Member {
private String loginName;//用户名
private String loginPass;//密码
private String roleName;//角色
public Member(String loginName, String loginPass,String roleName) {
this.loginName = loginName;
this.loginPass = loginPass;
this.roleName = roleName;
}
public String getLoginName() {return loginName;}
public void setLoginName(String loginName) {this.loginName = loginName;}
public String getLoginPass() {return loginPass;}
public void setLoginPass(String loginPass) {this.loginPass = loginPass;}
public String getRoleName() {return roleName;}
public void setRoleName(String roleName) {this.roleName = roleName;}
@Override
public String toString() {
return "Member{" +
"loginName='" + loginName + '\'' +
", loginPass='" + loginPass + '\'' +
", roleName='" + roleName + '\'' +
'}';
}
}
/**
* 抽象处理者(`Handler`):
*/
public abstract class Handler<T> {
protected Handler next;//下一个节点对象
public void next(Handler next){ this.next = next;}//初始化下一个节点
public abstract void doHandler(Member member);//执行具体逻辑的方法
public static class Builder<T>{//静态内部类:建造者
private Handler<T> head;//头节点
private Handler<T> tail;//尾节点
//添加节点的方法
public Builder<T> addHandler(Handler handler){
if (this.head == null) {//如果头节点为null:第一次添加
this.head = this.tail = handler;//头和尾都是该节点
return this;
}
//非第一次添加
this.tail.next(handler);//初始化当前尾节点的下一个节点
this.tail = handler;//此时尾节点就是添加过来的节点
return this;
}
public Handler<T> build(){
return this.head;//返回头节点
}
}
}
/**
* 具体处理者(`Concrete Handler`):处理验证用户名和密码的逻辑
*/
public class ValidateHandler extends Handler {
public void doHandler(Member member) {
if(StringUtils.isEmpty(member.getLoginName()) ||
StringUtils.isEmpty(member.getLoginPass())){
System.out.println("用户名和密码为空");
return;
}
System.out.println("用户名和密码不为空,可以往下执行");
if(null != next) { //如果下一个节点不为空,则执行下一个节点
next.doHandler(member);
}
}
}
/**
* 具体处理者(`Concrete Handler`):登录成功后执行的逻辑
*/
public class LoginHandler extends Handler {
public void doHandler(Member member) {
System.out.println("登录成功!");
if(null != next) {//如果下一个节点不为空,则执行下一个节点
next.doHandler(member);
}
}
}
/**
*具体处理者(`Concrete Handler`):处理角色权限的逻辑
*/
public class AuthHandler extends Handler {
public void doHandler(Member member) {
if(!"管理员".equals(member.getRoleName())){
System.out.println("您不是管理员,没有操作权限");
return;
}
System.out.println("允许操作");
if(null != next) {//如果下一个节点不为空,则执行下一个节点
next.doHandler(member);
}
}
}
/**
* 客户类(`Client`):创建处理链。对外调用
*/
public class MemberService {
//对外暴露的登录方法
public void login(String loginName,String loginPass,String roleName){
Handler.Builder builder = new Handler.Builder();//创建建造者
builder.addHandler(new ValidateHandler())//建造者开始建造责任链
.addHandler(new LoginHandler())
.addHandler(new AuthHandler());
builder.build().doHandler(new Member(loginName,loginPass,roleName));//获取头节点开始依次执行
}
}
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
MemberService memberService = new MemberService();
memberService.login("tom","666","管理员");
}
}
程序运行结果如下:
用户名和密码不为空,可以往下执行
登录成功!
允许操作
责任链模式的应用场景
在生活中责任链模式还是很常见的,比如,我们工作中处理一些失事务,往往是各个部门协同合作的完成一个任务,而各个部门都有自己的职责,因此,一个部门完成自己人职责任务后就会把任务转交给下一个部门,直到所有部门都过一遍后事情才完成。
在程序中,责任链模式主要是解耦了请求和处理,客户只需请求发送到链上即可,无需关心请求的具体内容和处理细节,请求会自动进行传递直至有节点对象处理。适用于以下场景:
1.有多个对象可以处理一个请求,哪个对象处理该请求由运行时刻自动确定。
2.可动态指定一组对象处理请求,或添加新的处理者。
3.在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
责任链模式的优缺点
责任链模式是一种对象行为型模式,其主要优点如下:
1.降低了对象之间的耦合度
。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
2.增强了系统的可扩展性
。可以根据需要增加新的请求处理类,满足开闭原则。
3.增强了给对象指派职责的灵活性
。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
4.责任链简化了对象之间的连接
。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 语句。
5.责任分担
。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
其主要缺点如下:
1.不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
2.对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
3.职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。