专栏首页关忆北.设计模式之责任链模式

设计模式之责任链模式

责任链模式

责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。如Spring Secuurity的处理。

责任链的角色

Handler(抽象处理者)

​ 定义请求处理类,一般为接口或抽象类,也可以理解为定义一个处理责任链。

ConcreteHandler(具体处理者)

​ 具体处理者实现或继承自抽象处理者,实现抽象处理者的方法,在方法中定义不同的处理逻辑,并将处理结果传递给下一个处理者。

有些像策略模式,但不同的是:策略模式根据不同的上下文类型选择不同的策略对象进行不同的逻辑处理,而责任链模式如何执行逻辑、执行什么逻辑是程序员选择的。 请注意下文中的 责任链的定义(Demo.class) Middleware middleware = new ThrottlingMiddleware(2); middleware.linkWith(new UserExistsMiddleware(server)) .linkWith(new RoleCheckMiddleware()); 责任链会将特定行为转换为被称作处理者的独立对象。 在上述示例中, 每个检查步骤都可被抽取为仅有单个方法的类, 并执行检查操作。 请求及其数据则会被作为参数传递给该方法。

如果后期需要加入安全校验、IP重复请求进行密码暴力破解、将用户信息放入缓存等步骤才能满足需求,那么每加入一个步骤,代码就会更加冗余,显然这不是最好的解决方案。

责任链解决方案

你将这些处理者连成一条链。 链上的每个处理者都有一个成员变量来保存对于下一处理者的引用。 如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。除了处理请求外, 处理者还负责沿着链传递请求。 请求会在链上移动, 直至所有处理者都有机会对其进行处理。 处理者可以决定不再沿着链传递请求, 这可高效地取消所有后续处理步骤。

这种需求就类似于Spring Security中的过滤器,一个请求到达后,被过滤链中的每个过滤器逐个进行处理。

在SpringSecrity的VirtualFilterChain源码中:

doFilter接口实现

过滤器在doFilter方法中执行过滤。 每个Filter都可以访问FilterConfig对象,可以从中获取其初始化参数,它是对ServletContext的引用,可以使用该ServletContext加载例如过滤任务所需的资源。 在Web应用程序的部署描述符中配置过滤器 为此设计确定的示例是 1)认证过滤器 2)记录和审核过滤器 3)图像转换滤镜 4)数据压缩过滤器 5)加密过滤器 6)标记化过滤器 7)触发资源访问事件的过滤器 8)XSL / T过滤器 9)Mime型链式过滤器

点击进去Filter的任意一个接口实现:

这里doFilter就是责任链中的其中一个链条(责任链中的每一个链条都实现自同一接口,表示同一个对象,这样才能将2请求传递给下一个),使用责任链模式可以避免请求和响应耦合在一起。

talk is cheaper,show me your code.

使用责任链模式完成认证、授权、验证

Middleware.class

/**
 * Base middleware class.
 * @author Liu-PC
 * @Description 基础验证接口
 */
public abstract class Middleware {
    private Middleware next;

    /**
     * Builds chains of middleware objects.
     *
     * @param next
     * @return
     */
    public Middleware linkWith(Middleware next) {
        this.next = next;
        return next;
    }

    /**
     * Subclasses will implement this method with concrete checks.
     *
     * @param email
     * @param password
     * @return
     */
    public abstract boolean check(String email, String password);

    protected boolean checkNext(String email, String password) {
        if (next == null) {
            return true;
        }
        return next.checkNext(email, password);
    }
    
}

UserExistsMiddleware.class

/**
 * @author Liutx
 * @date 2021/1/17 16:51
 * @Description 检查用户登录信息
 * ConcreteHandler. Checks whether a user with the given credentials exists.
 */
public class UserExistsMiddleware extends Middleware {

    private Server server;

    public UserExistsMiddleware(Server server) {
        this.server = server;
    }


    /**
     *
     * @param email
     * @param password
     * @return 返回true/false  通过条件来控制责任链是否向下进行
     */
    @Override
    public boolean check(String email, String password) {
        if (!server.hasEmail(email)) {
            System.out.println("This email is not registered!");
            return false;
        }

        if (!server.isValidPassword(email, password)) {
            System.out.println("Wrong password!");
            return false;
        }

        return checkNext(email, password);
    }
}

ThrottlingMiddleware.class

/**
 * @author Liutx
 * @date 2021/1/17 16:30
 * @Description 请求数量(次数)限制
 * ConcreteHandler. Checks whether there are too many failed login requests.
 */

public class ThrottlingMiddleware extends Middleware {
    public static final long A_MINUTE = 60_000;

    /**
     * 请求间隔
     */
    private int requestPerMinute;
    private int request;
    private long currentTime;

    public ThrottlingMiddleware(int requestPerMinute) {
        this.requestPerMinute = requestPerMinute;
        this.currentTime = System.currentTimeMillis();
    }

    @Override
    public boolean check(String email, String password) {
        if (System.currentTimeMillis() > currentTime + A_MINUTE) {
            request = 0;
            currentTime = System.currentTimeMillis();
        }
        request++;
        if (request > requestPerMinute) {
            System.out.println("Request limit exceeded!");
            Thread.currentThread().interrupt();
        }
        return checkNext(email, password);
    }
}

RoleCheckMiddleware.class

/**
 * @author Liutx
 * @date 2021/1/17 17:20
 * @Description 检查用户角色
 * ConcreteHandler. Checks a user's role.
 */
public class RoleCheckMiddleware extends Middleware {

    @Override
    public boolean check(String email, String password) {
        if (email.equals("admin@example.com")) {
            System.out.println("Hello, admin!");
            return true;
        }
        System.out.println("Hello, user!");
        return checkNext(email, password);
    }
}

Server.class

/**
 * @author Liutx
 * @date 2021/1/17 16:55
 * @Description Server
 */
public class Server {
    private Map<String, String> users = new HashMap<>();
    private Middleware middleware;

    /**
     * Client passes a chain of object to server. This improves flexibility and
     * makes testing the server class easier.
     */
    public void setMiddleware(Middleware middleware) {
        this.middleware = middleware;
    }

    /**
     * Server gets email and password from client and sends the authorization
     * request to the chain.
     */
    public boolean logIn(String email, String password) {
        if (middleware.check(email, password)) {
            System.out.println("Authorization have been successful!");

            // Do something useful here for authorized users.

            return true;
        }
        return false;
    }

    public void register(String email, String password) {
        users.put(email, password);
    }

    public boolean hasEmail(String email) {
        return users.containsKey(email);
    }

    public boolean isValidPassword(String email, String password) {
        return users.get(email).equals(password);
    }
}

Demo.class

/**
 * @author Liutx
 * @date 2021/1/17 17:21
 * @Description
 */
public class Demo {
    private static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    private static Server server;

    private static void init() {
        server = new Server();
        server.register("admin@example.com", "admin_pass");
        server.register("user@example.com", "user_pass");

        //All checks are linked. Client can build various chains using the same component.
        Middleware middleware = new ThrottlingMiddleware(2);
        middleware.linkWith(new UserExistsMiddleware(server))
                .linkWith(new RoleCheckMiddleware());

        // Server gets a chain from client code
        server.setMiddleware(middleware);
    }

    public static void main(String[] args) throws IOException {
        init();

        boolean success;
        do {
            System.out.print("Enter email: ");
            String email = reader.readLine();
            System.out.print("Input password: ");
            String password = reader.readLine();
            success = server.logIn(email, password);
        } while (!success);
    }
}

职责链模式的主要优点

  • 对象仅需知道该请求会被处理即可,且链中的对象不需要知道链的结构,由客户端负责链的创建,降低了系统的耦合度
  • 请求处理对象仅需维持一个指向其后继者的引用,而不需要维持它对所有的候选处理者的引用,可简化对象的相互连接
  • 新增接收者处理也只需要增加链中的一个节点,不需要改动太多.

责任链模式应用

  • Spring Security 使用责任链模式,可以动态地添加或删除责任(处理 request 请求)
  • Spring AOP 通过责任链模式来管理 Advisor
  • Mybatis 中的 Plugin 机制使用了责任链模式,配置各种官方或者自定义的 Plugin,与 Filter 类似,可以在执行 Sql 语句的时候做一些操作
本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!
本文分享自作者个人站点/博客:https://blog.csdn.net/weixin_42313773复制
如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

相关文章

  • 【设计模式之责任链模式】

    1、定义:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系, 将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。

    用户5640963
  • 设计模式之责任链模式

    责任链模式(Chain of Responsibility Pattern)属于设计模式的行为型模式。责任链模式与多米诺骨牌有点类似,请求在链中从前向后传递,一...

    Dylan Liu
  • 设计模式之责任链模式

    在现实生活中,有很多请求并不是一个人说了就算的,例如面试时的工资,低于1万的薪水可能技术经理就可以决定了,但是1万~1万5的薪水可能技术经理就没这个权利批准,可...

    用户1205080
  • 设计模式之责任链模式

    定义:给多个对象处理请求的机会,减少请求的发送者与接受者之间的耦合。将接受对象链接起来,在链中传递请求,直到有一个对象处理这个请求。

    没有故事的陈师傅
  • 设计模式之责任链模式

    本文通过图书馆管理系统中,用户名校验、密码校验、需要增加问题,每次都要增加if判断语句,将其改用责任链模式进行链式调用,为了让代码更加的优雅,我们使用之前学过的...

    程序员田同学
  • java设计模式之责任链模式

    有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象决定处理该请求。但是发出者并不清楚到底最终那个对象会处理该请求,...

    小小鱼儿小小林
  • PHP设计模式之责任链模式

    GoF定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

    硬核项目经理
  • JAVA设计模式之责任链模式

    在阎宏博士的《JAVA与模式》一书中开头是这样描述责任链(Chain of Responsibility)模式的:

    zhangheng
  • 白话设计模式之责任链模式

    责任链模式,顾名思义,就是一系列链式操作,就说明是由很多节点组成的链条,当有请求过来时,会顺着这条链一直往下执行,直到遇到符合条件的节点,下面我将以一个自己在项...

    小四的技术之旅
  • 设计模式之责任链模式(Chain of Responsibility)引入责任链模式责任链模式的实例责任链模式的分析责任链模式的应用

    责任链模式描述的就是如何推卸责任,说的简洁点,就是踢皮球哈哈。举个例子,有时候,出了某件事,我们去解决,找到A,结果A踢皮球,说这不关我的事,去找B解决,然后我...

    desperate633
  • 前端设计模式之责任链模式

    责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象...

    Cookieboty
  • 设计模式10之责任链模式

    我们有这样一个场景,假如你出差需要报销。你的报销单审批人依次是:项目经理、科室主任、部门负责人、财务负责人。当然你不需要知道每个审批人的姓名、电话号码、办公地址...

    Lvshen
  • 大话设计模式之---责任链模式

    责任链模式,又称职责链模式,Chain Of Responsibility,使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦...

    疯狂的KK
  • 设计模式-责任链模式

    看下具体的代码: 先把抽象处理者写出来,他是一个抽象类或者接口,这里使用抽象类,每个处理者给一个名字 name 属性

    breezedancer
  • 设计模式----责任链模式

    一个事件需要经过多个对象处理是一个挺常见的场景,譬如采购审批流程,请假流程,软件开发中的异常处理流程,web请求处理流程等各种各样的流程,可以考虑使用责任链模式...

    大忽悠爱学习
  • 设计模式- 责任链模式

    即将所有处理某种请求的对象一个接一个的排成序列,当某个任务来临时,按照次序列依次执行下去,直至有对象处理。

    开源519
  • 设计模式-责任链模式

    基本每个在职人员,都经历过请假的事情。正常情况下,请假需要经过上级部门领导审批,而公司有总经理、部门经理、组长 有的甚至更多职位。请假也分为很多种情况,比如,事...

    逍遥壮士
  • 设计模式-责任链模式

    责任链模式(Chain of Responsibility Pattern):避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条...

    码哥字节
  • 设计模式~责任链模式

    责任链模式(Chain of Responsibility)是一种对象的行为模式。

    Vincent-yuan

扫码关注腾讯云开发者

领取腾讯云代金券