责任链模式是将链中的每一个节点看做是一个对象,每个节点处理的请求均不相同,且内部自动维护下一个节点对象,当一个请求从链式的首段发出时,会沿着链的路径依次传递给每一个节点对象,直至有对象处理这个请求位置,属于行为模式。
这里需要注意的是每个节点都能对对象进行一定的处理(也可以不处理),处理完成之后节点再进行判断还要进行后续处理还是说传递给下一个节点。
应用场景
首先举一个日常的例子,比如我们申请开发票,首先我们要写好报销单,首先要你的部门领导审批,部门领导审批不通过直接打回,审批通过再由公司的总经理审批这里审批通过才算成审批完成。这种情况就很适合使用责任链模式。
总结一下责任链主要适用一下几种情况:
- 多个对象可以处理同一个请求,但是具体由那个对象处理完成则在运行时决定。
- 不明确指定接收者的情况下,向多个对象中的一个提交一个请求
可以看一下责任链模式的通用UML类图:
通过类图可以看到总共包含以下角色:
- 抽象处理者:主要是定义处理请求的方法以及维护下一个处理结点的对象的引用
- 具体处理者:处理的具体实现
责任链的精髓在于将很多处理节点行成个链式结构,并允许结点自身决定是否进行处理或者转发。
实际代码案例
下面举一个我们在开发时经常会遇到的一种情况:登录 比如我们开发一个管理系统在登录的时候往往我们会先判断客户端传递的账号及密码是否为空但凡有一个是空肯定是不能继续往下走的,然后就是根据用户账号密码拿到用户的所有信息,如果能拿到继续周下一步,拿不到则是报错提示用户不存在,到下一步又会判断当前用户的权限。
无模式情况下的代码
private String login(String username,String password) {
if(username == null || password == null) {
return "账户或者密码为null";
}
User user = queryUserInfo(username, password);
if(user == null) {
return "找不到用户";
}
if(!Objects.equals(user.getRoleName(), "超管")){
return "没有权限";
}
return "登录成功";
}
private User queryUserInfo(String username,String password){
if(Objects.equals(username, "土豆") && Objects.equals(password, "666666")) {
return new User(username,password,"超管");
}else if(Objects.equals(username, "土豆2号") && Objects.equals(password, "666666")){
return new User(username,password,"普通员工");
}
return null;
}
发现判断代码都冗余在一个方法里面,后续改动修改都需要修改中这个方法不满足开闭原则。
采用责任链模式优化代码
首先创建抽象类规定抽象方法以及维护下一个节点
public abstract class Handler {
protected Handler next;
public void setNext(Handler next) {
this.next = next;
}
public abstract void doHandler(User user);
}
然后就是创建多个实现逻辑的节点对象:
public class ValidatedHandler extends Handler {
@Override
public void doHandler(User user) {
if(user.getUsername() == null || user.getPassword() == null) {
System.out.println("账户或者密码为null");
}else{
this.next.doHandler(user);
}
}
}
public class UserHandler extends Handler{
@Override
public void doHandler(User user) {
queryUserInfo(user);
if(user.getRoleName() == null){
System.out.println("没有找到用户");
}else{
this.next.doHandler(user);
}
}
private static void queryUserInfo(User user){
if(Objects.equals(user.getUsername(), "土豆") && Objects.equals(user.getPassword(), "666666")) {
user.setRoleName("超管");
}else if(Objects.equals(user.getUsername(), "土豆2号") && Objects.equals(user.getPassword(), "666666")){
user.setRoleName("普通员工");
}
}
}
public class AuthHandler extends Handler{
@Override
public void doHandler(User user) {
if(!Objects.equals(user.getRoleName(), "超管")){
System.out.println("没有权限");
}
System.out.println("登入成功");
}
}
最后调用:
public static void main(String[] args) {
User user = new User("土豆","666666");
Handler validatedHandler = new ValidatedHandler();
Handler userHandler = new UserHandler();
Handler authHandler = new AuthHandler();
validatedHandler.setNext(userHandler);
userHandler.setNext(authHandler);
validatedHandler.doHandler(user);
}
可以看一下UML类图:
采用建造者+责任链模式优化代码
上述的代码发现维护链表的操作在用户调用的那一层,链表的组装过于复杂,这个时候我们可以使用建造者模式, 自动维护链表的组装,调用者只需要指定链表的顺序即可 主要修改Handler内代码:
public abstract class Handler {
protected Handler next;
public void setNext(Handler next) {
this.next = next;
}
public abstract void doHandler(User user);
public static class Builder {
private Handler head;
private Handler tail;
public Builder addHandler(Handler handler) {
if(this.head == null){
this.head = this.tail = handler;
return this;
}
this.tail.setNext(handler);
this.tail = handler;
return this;
}
public Handler build() {
return this.head;
}
}
}
Handler.Builder builder = new Handler.Builder();
builder.addHandler(new ValidatedHandler())
.addHandler(new UserHandler())
.addHandler(new AuthHandler());
builder.build().doHandler(user);
可发现调用者只需要关心链表顺序写入就好
责任链模式优缺点
优点:
- 请求与处理解耦
- 处理者只需要关心自己的处理逻辑即可,如果不是自己的直接转发
- 具有链式传递功能,请求者不需要关系链路结构等待结果就好
- 易于维护,可以很灵活的修改链路的结构新增或者删除,符合开闭原则
缺点:
- 会出现循环引用的情况
- 责任链太长会影响性能