跳转至

责任链#

约 864 个字 6 行代码 预计阅读时间 17 分钟

意图#

允许你将请求沿着处理者链进行发送。收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下个处理者。

结构#

责任链
  1. 处理者(Handler)声明了所有具体处理者的通用接口。该接口通常仅包含单个方法用于请求处理,但有时其还会包含一个设置链上下个处理者的方法。
  2. 基础处理者(Base Handler) 是一个可选的类,你可以将所有处理者共用的样本代码放置在其中。在通常情况下,该类中定义了一个保存对于下个处理者引用的成员变量。客户端可通过将处理者传递给上个处理者的构造函数或设定方法来创建链。该类还可以实现默认的处理行为:确定下个处理者存在后再将请求传递给它。
  3. 具体处理者(Concrete Handlers)包含处理请求的实际代码。每个处理者接收到请求后,都必须决定是否进行处理,以及是否沿着链传递请求。处理者通常是独立且不可变的,需要通过构造函数一次性地获得所有必要地数据。
  4. 客户端(Client)可根据程序逻辑一次性或者动态地生成链。值得注意的是,请求可发送给链上的任意一个处理者,而非必须是第一个处理者。

场景分析#

需求描述#

开发一个在线订购系统。你希望对系统访问进行限制,只允许认证用户创建订单。此外,拥有管理权限的用户也拥有所有订单的完全访问权限。

这些检查需要依次进行。

请求必须经过一系列检查后才能由订购系统来处理。

问题描述#

过了一段时间,需求增加:

  • 接将原始数据传递给订购系统存在安全隐患。需要新增了额外的验证步骤来清理请求中的数据。
  • 无法抵御暴力密码破解方式的攻击。需要添加了一个检查步骤来过滤来自同一 IP 地址的重复错误请求。
  • 可以对包含同样数据的重复请求返回缓存中的结果,从而提高系统响应速度。需要新增一个检查步骤,确保只有没有满足条件的缓存结果时请求才能通过并被发送给系统。
代码变得越来越多,也越来越混乱。

解决方案#

责任链模式会将特定行为转换为被称作处理者的独立对象。在上述示例中,每个检查步骤都可被抽取为仅有单个方法的类 ,并执行检查操作。将这些处理者连成一条上的每个处理者都有一个成员变量来保存对于下一处理者的引用

处理者可以决定不再沿着链传递请求,这可高效地取消所有后续处理步骤。

处理者依次排列,组成一条链。

代码实现#

基础处理者#

Middleware.java
package com.luguosong.behavioral.chain_of_responsibility.middleware;

/**
 * 基础处理者
 *
 * @author luguosong
 */
public abstract class Middleware {

    //下一个处理者
    private Middleware next;

    /*
    * 构建责任链
    * */
    public static Middleware link(Middleware first, Middleware... chain) {
        Middleware head = first;
        for (Middleware nextInChain: chain) {
            head.next = nextInChain;
            head = nextInChain;
        }
        return first;
    }

    /*
     * 抽象处理方法,用于处理请求
     * */
    public abstract boolean check(String email, String password);

    /*
     * 处理者公共代码
     * 对链中的下一个对象进行检查,如果已到达链中的最后一个对象,则结束遍历。
     * */
    protected boolean checkNext(String email, String password) {
        if (next == null) {
            return true;
        }
        return next.check(email, password);
    }
}

具体处理者#

检查请求数量限制
package com.luguosong.behavioral.chain_of_responsibility.middleware.impl;

import com.luguosong.behavioral.chain_of_responsibility.middleware.Middleware;

/**
 * 具体处理程序。检查是否有过多的登录失败请求。
 *
 * @author luguosong
 */
public class ThrottlingMiddleware extends Middleware {
    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 + 60_000) {
            request = 0;
            currentTime = System.currentTimeMillis();
        }

        request++;

        if (request > requestPerMinute) {
            System.out.println("请求次数超出限制!");
            Thread.currentThread().stop();
        }
        return checkNext(email, password);
    }
}
检查用户登录信息
package com.luguosong.behavioral.chain_of_responsibility.middleware.impl;

import com.luguosong.behavioral.chain_of_responsibility.middleware.Middleware;
import com.luguosong.behavioral.chain_of_responsibility.server.Server;

/**
 * 具体处理程序。检查是否存在具有给定凭据的用户。
 *
 * @author luguosong
 */
public class UserExistsMiddleware extends Middleware {
    private Server server;

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

    @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);
    }
}
检查用户角色
package com.luguosong.behavioral.chain_of_responsibility.middleware.impl;

import com.luguosong.behavioral.chain_of_responsibility.middleware.Middleware;

/**
 * 具体处理程序:检查用户角色。
 *
 * @author luguosong
 */
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);
    }
}

客户端#

客户端代码
package com.luguosong.behavioral.chain_of_responsibility;

import com.luguosong.behavioral.chain_of_responsibility.middleware.Middleware;
import com.luguosong.behavioral.chain_of_responsibility.middleware.impl.RoleCheckMiddleware;
import com.luguosong.behavioral.chain_of_responsibility.middleware.impl.ThrottlingMiddleware;
import com.luguosong.behavioral.chain_of_responsibility.middleware.impl.UserExistsMiddleware;
import com.luguosong.behavioral.chain_of_responsibility.server.Server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * @author luguosong
 */
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", "12345678");
        server.register("user@example.com", "123456");

        // 所有检查都已连接。客户可以使用相同的组件构建各种链。
        Middleware middleware = Middleware.link(
                new ThrottlingMiddleware(2),
                new UserExistsMiddleware(server),
                new RoleCheckMiddleware()
        );

        // 服务器从客户端代码获取链。
        server.setMiddleware(middleware);
    }

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

        boolean success;
        do {
            System.out.print("输入邮箱: ");
            String email = reader.readLine();
            System.out.print("输入密码: ");
            String password = reader.readLine();
            success = server.logIn(email, password);
        } while (!success);
    }
}
授权目标
package com.luguosong.behavioral.chain_of_responsibility.server;

import com.luguosong.behavioral.chain_of_responsibility.middleware.Middleware;

import java.util.HashMap;
import java.util.Map;

/**
 * 模拟服务
 *
 * @author luguosong
 */
public class Server {
    private Map<String, String> users = new HashMap<>();
    private Middleware middleware;

    /**
     * 客户端将对象链传递给服务器。这提高了灵活性并使测试服务器类更容易。
     */
    public void setMiddleware(Middleware middleware) {
        this.middleware = middleware;
    }

    /**
     * 服务器从客户端获取电子邮件和密码并向链发送授权请求。
     */
    public boolean logIn(String email, String password) {
        if (middleware.check(email, password)) {
            System.out.println("授权成功!");

            // 在此为授权用户做些有用的事情。

            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);
    }
}

评论