写点什么

ChainOfResponsibilityPattern- 责任链模式

作者:梁歪歪 ♚
  • 2022 年 6 月 02 日
  • 本文字数:5025 字

    阅读完需:约 16 分钟

ChainOfResponsibilityPattern-责任链模式

责任链模式

责任链模式(Chain of Responsibility Pattern):是指将链中的每一个节点看作是一个对象,每个节点处理的请求均不同,且每个节点内部自动维护了一个又一个节点对象。当一个请求在链路的头部发出时,会沿着链的路径依次传递给每一个节点对象,直到有对象处理这个请求为止。


在我们一个系统中,登录及权限校验就可以作为一条“链路”来处理,普通写法就是在一个方法里面先校验账号密码是否正确,然后校验角色是否正确,再校验权限是否正确,但是如果利用责任链模式我们就可以把这三种校验封装成 3 个节点,并让这三个节点构成一条“链路”。示例我们就通过登录及其权限校验的示例来举例说明。


  • 登录用户信息类LoginUser.java


package cn.liangyy.chain;
/** * 登录用户信息 * 存储一些需要校验的信息 */public class LoginUser { //登录名 private String loginName; //密码 private String password; //角色 private String roleName; //权限 private String permission;
public String getLoginName() { return loginName; }
public void setLoginName(String loginName) { this.loginName = loginName; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getRoleName() { return roleName; }
public void setRoleName(String roleName) { this.roleName = roleName; }
public String getPermission() { return permission; }
public void setPermission(String permission) { this.permission = permission; }}
复制代码


  • 新建一个“链路”的抽象节点类MyHandler.java,这个类内部维护下一个节点


package cn.liangyy.chain;
/** * "链路"的抽象节点类 * 维护下一个节点,并定义执行业务逻辑 */public abstract class MyHandler { //持有下一个节点对象,也就是每个节点都需要知道自己的下一个节点是谁才能传递下去 protected MyHandler next;
public void next(MyHandler handler){ this.next = handler; }
//执行每个节点的处理逻辑 public abstract void doHandler(LoginUser loginUser);}
复制代码


  • 新建建造“链路”节点之一,校验账号密码节点类VerifyAccountHandler.java


package cn.liangyy.chain;
/** * 校验账户密码节点类 * "链路"节点之一,继承抽象节点类 */public class VerifyAccountHandler extends MyHandler { @Override public void doHandler(LoginUser loginUser) { if (null == loginUser.getLoginName()){ System.out.println("用户名不能为空!"); return; } if (null == loginUser.getPassword()){ System.out.println("密码不能为空!"); return; } if (!loginUser.getPassword().equals("123")){ System.out.println("密码不正确!"); return; } System.out.println("账户密码校验通过!"); //传递给下一个节点 next.doHandler(loginUser); }}
复制代码


  • 新建建造“链路”节点之一,校验角色信息节点类VerifyRolehandler.java


package cn.liangyy.chain;
/** * 校验角色信息节点类 * "链路"节点之一 */public class VerifyRolehandler extends MyHandler { @Override public void doHandler(LoginUser loginUser) { if (!"admin".equals(loginUser.getRoleName())){ //校验角色信息 System.out.println("角色信息有误!"); return; } System.out.println("角色信息校验通过"); //传递给下一个节点 next.doHandler(loginUser); }}
复制代码


  • 新建建造“链路”节点之一,校验权限信息节点类VerifyPermissionhandler.java


package cn.liangyy.chain;
/** * 校验权限信息节点类 * "链路"节点之一 */public class VerifyPermissionhandler extends MyHandler { @Override public void doHandler(LoginUser loginUser) { if (!"admin".equals(loginUser.getPermission())){ System.out.println("暂无权限!"); return; } System.out.println("权限校验通过,登录成功!"); }}
复制代码


  • 测试TestChain.java


package cn.liangyy.chain;
/** * 责任链模式-测试 */public class TestChain { public static void main(String[] args) { MyHandler accountHandler = new VerifyAccountHandler(); MyHandler roleHandler = new VerifyRolehandler(); MyHandler permissionHandler = new VerifyPermissionhandler();
accountHandler.next(roleHandler); roleHandler.next(permissionHandler);
LoginUser loginUser = new LoginUser(); loginUser.setLoginName("liangsir"); loginUser.setPassword("123"); loginUser.setRoleName("admin"); loginUser.setPermission("admin"); //从起点开始调用 accountHandler.doHandler(loginUser); }}
复制代码


以上写法为责任链模式的常规写法,下面我们将用建造者模式进行改造,使其更加优雅...


  • 登录用户信息类LoginUser.java


package cn.liangyy.chain;
/** * 登录用户信息 * 存储一些需要校验的信息 */public class LoginUser { //登录名 private String loginName; //密码 private String password; //角色 private String roleName; //权限 private String permission;
public String getLoginName() { return loginName; }
public void setLoginName(String loginName) { this.loginName = loginName; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getRoleName() { return roleName; }
public void setRoleName(String roleName) { this.roleName = roleName; }
public String getPermission() { return permission; }
public void setPermission(String permission) { this.permission = permission; }}
复制代码


  • “链路”的抽象节点BuildHandler.java


package cn.liangyy.chain.two;
import cn.liangyy.chain.one.LoginUser;import cn.liangyy.chain.one.MyHandler;
/** * "链路"的抽象节点 */public abstract class BuildHandler<T> { //持有下一个节点对象,也就是每个节点都需要知道自己的下一个节点是谁才能传递下去 protected MyHandler next;
public void next(MyHandler handler){ this.next = handler; }
//执行每个节点的处理逻辑 public abstract void doHandler(LoginUser loginUser);}
复制代码


  • "链路"对象的建造者类Builder.java


package cn.liangyy.chain.two;
/** * "链路"对象的建造者类 */public class Builder<T> { //链路的头节点 private BuildHandler<T> head; //链路的尾节点 private BuildHandler<T> tail;
public Builder<T> addHandler(BuildHandler handler){ //如果头节点为空,则说明正在构建第一个节点,此时头尾节点都相等 if (null == head){ head = this.tail = handler; return this; } //如果节点不为空 //把当前"链路"的tail节点的下一个节点指向刚加进来的节点 this.tail.next(handler); //然后把当前加进来的节点设置为tail节点 this.tail = handler; return this; }
public BuildHandler<T> build(){ //调用build之后就会开始调用流程了,从头节点开始,所以返回头节点 return this.head; }}
复制代码


  • “链路”节点之一,校验账号密码节点类VerifyAccountHandler.java


package cn.liangyy.chain.two;
/** * 校验账号密码节点类 */public class VerifyAccountHandler extends BuildHandler { @Override public void doHandler(LoginUser loginUser) { if (null == loginUser.getLoginName()){ System.out.println("用户名不能为空!"); return; } if (null == loginUser.getPassword()){ System.out.println("密码不能为空!"); return; } if (!loginUser.getPassword().equals("123")){ System.out.println("密码不正确!"); return; } System.out.println("账户密码校验通过!"); //传递给下一个节点 next.doHandler(loginUser); }}
复制代码


  • “链路”节点之一,校验角色信息节点类 VerifyRolehandler.java


package cn.liangyy.chain.two;
/** * 校验角色信息节点类 * "链路"节点之一 */public class VerifyRolehandler extends BuildHandler { @Override public void doHandler(LoginUser loginUser) { if (!"admin".equals(loginUser.getRoleName())){ //校验角色信息 System.out.println("角色信息有误!"); return; } System.out.println("角色信息校验通过"); //传递给下一个节点 next.doHandler(loginUser); }}
复制代码


  • “链路”节点之一,校验权限信息节点类VerifyPermissionhandler.java


package cn.liangyy.chain.two;
/** * 校验权限信息节点类 * "链路"节点之一 */public class VerifyPermissionhandler extends BuildHandler { @Override public void doHandler(LoginUser loginUser) { if (!"admin".equals(loginUser.getPermission())){ System.out.println("暂无权限!"); return; } System.out.println("权限校验通过,登录成功!"); }}
复制代码


  • 测试TestBuildChain.java


package cn.liangyy.chain.two;
/** * 责任链模式-测试 */public class TestBuildChain { public static void main(String[] args) { LoginUser loginUser = new LoginUser(); loginUser.setLoginName("liangsir"); loginUser.setPassword("123"); loginUser.setRoleName("admin"); loginUser.setPermission("admin");
Builder<BuildHandler> build = new Builder<>(); //利用建造者模式构建"链路对象" build.addHandler(new VerifyAccountHandler()) .addHandler(new VerifyRolehandler()) .addHandler(new VerifyPermissionhandler()); //build方法返回头节点,所以从头节点开始执行,后面就会自动传递 build.build().doHandler(loginUser); }}
复制代码


可以看到,通过建造者模式和责任链模式结合的代码,在构造链路对象的时候比常规写法优雅多了...


责任链模式使用场景责任链模式主要是解耦了请求与处理,用户只需要将请求发送到链上即可,无需关心请求的具体内容和处理细节,请求会自动进行传递直至有节点进行处理。主要适用于如下场景:


  • 多个对象可以处理同一请求,但具体由哪个对象处理则在运行时动态决定。

  • 可以动态指定一组对象的处理请求。


责任链模式优点


  • 请求处理者(链路中的节点)只需关注自己感兴趣的请求进行处理,对于不感兴趣或者无法处理的请求直接转发给下一个处理者。

  • 具备链式传递请求的功能,请求发送者无需知晓链路结构,只需等待请求处理结果。

  • 链路结构灵活,可以通过改变链路结构动态的新增或者删减责任。

  • 易于扩展新的请求处理类,符合开闭原则。


责任链模式缺点


  • 如果责任链的链路太长或者处理时间过长,会影响性能。

  • 如果节点对象存在循环引用时,会造成死循环,导致系统崩溃。

发布于: 刚刚阅读数: 3
用户头像

梁歪歪 ♚

关注

还未添加个人签名 2021.07.22 加入

还未添加个人简介

评论

发布
暂无评论
ChainOfResponsibilityPattern-责任链模式_设计模式_梁歪歪 ♚_InfoQ写作社区