责任链模式

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

假如你正在开发一个在线订购系统。你希望对系统访问进行限制,只允许认证用户创建订单。此外,拥有管理权限的用户也拥有所有订单的完全访问权限。简单规划后,你会意识到这些检查必须依次进行。只要接收到包含用户凭据的请求,应用程序就可尝试对进入系统的用户进行认证。但如果由于用户凭据不正确而导致认证失败,那就没有必要进行后续检查了。

在接下来的几个月里, 你实现了后续的几个检查步骤:

  • 一位同事认为直接将原始数据传递给订购系统存在安全隐患。因此你新增了额外的验证步骤来清理请求中的数据。
  • 过了一段时间,有人注意到系统无法抵御暴力密码破解方式的攻击。为了防范这种情况,你立刻添加了一个检查步骤来过滤来自同一 IP 地址的重复错误请求。
  • 又有人提议你可以对包含同样数据的重复请求返回缓存中的结果,从而提高系统响应速度。因此,你新增了一个检查步骤,确保只有没有满足条件的缓存结果时请求才能通过并被发送给系统。

检查代码本来就已经混乱不堪,而每次新增功能都会使其更加臃肿。修改某个检查步骤有时会影响其他的检查步骤。最糟糕的是,当你希望复用这些检查步骤来保护其他系统组件时,你只能复制部分代码,因为这些组件只需部分而非全部的检查步骤。

系统会变得让人非常费解,而且其维护成本也会激增。你在艰难地和这些代码共处一段时间后,有一天终于决定对整个系统进行重构。

与许多其他行为设计模式一样,责任链会将特定行为转换为被称作处理者的独立对象。在上述示例中,每个检查步骤都可被抽取为仅有单个方法的类,并执行检查操作。请求及其数据则会被作为参数传递给该方法。责任链模式建议你将这些处理者连成一条链,链上的每个处理者都有一个成员变量来保存对于下一处理者的引用。除了处理请求外,处理者还负责沿着链传递请求。请求会在链上移动,直至所有处理者都有机会对其进行处理。最重要的是,处理者可以决定不再沿着链传递请求,这可高效地取消所有后续处理步骤。

不过还有一种稍微不同的方式(也是更经典的一种),那就是处理者接收到请求后自行决定是否能够对其进行处理。如果自己能够处理,处理者就不再继续传递请求。因此在这种情况下,每个请求要么最多有一个处理者对其进行处理,要么没有任何处理者对其进行处理。在处理图形用户界面元素栈中的事件时,这种方式非常常见。这时,责任链就有点像策略模式了。

import java.util.ArrayList;
import java.util.List;

// 请求类,包含用户凭据和请求数据
class Request {
    private String credentials;
    private String ipAddress;
    private String data;
    private boolean authenticated;
    private boolean isAdmin;

    public Request(String credentials, String ipAddress, String data) {
        this.credentials = credentials;
        this.ipAddress = ipAddress;
        this.data = data;
    }

    // Getters and Setters
}

// 抽象处理器
abstract class Handler {
    protected Handler next;

    public Handler setNext(Handler next) {
        this.next = next;
        return next;
    }

    public abstract boolean handle(Request request);
}

// 认证处理器
class AuthenticationHandler extends Handler {
    @Override
    public boolean handle(Request request) {
        // 模拟认证逻辑
        if (!isValidCredentials(request.getCredentials())) {
            System.out.println("认证失败:无效的用户凭据");
            return false;
        }
        
        request.setAuthenticated(true);
        // 模拟设置管理员权限
        if ("admin_token".equals(request.getCredentials())) {
            request.setAdmin(true);
        }
        
        System.out.println("认证成功");
        return next == null || next.handle(request);
    }

    private boolean isValidCredentials(String credentials) {
        // 模拟凭证验证
        return credentials != null && 
              (credentials.equals("user_token") || credentials.equals("admin_token"));
    }
}

// 数据清理处理器
class DataSanitizationHandler extends Handler {
    @Override
    public boolean handle(Request request) {
        if (!request.isAuthenticated()) {
            return false;
        }

        // 模拟数据清理逻辑
        String sanitizedData = sanitizeData(request.getData());
        System.out.println("数据清理完成");
        
        return next == null || next.handle(request);
    }

    private String sanitizeData(String data) {
        // 模拟数据清理过程
        return data.replaceAll("<script>", "");
    }
}

// IP过滤处理器
class IPFilterHandler extends Handler {
    private List<String> blockedIPs = new ArrayList<>();
    
    @Override
    public boolean handle(Request request) {
        if (!request.isAuthenticated()) {
            return false;
        }

        // 模拟IP过滤逻辑
        if (isBlockedIP(request.getIpAddress())) {
            System.out.println("访问被拒绝:IP地址已被屏蔽");
            return false;
        }
        
        System.out.println("IP检查通过");
        return next == null || next.handle(request);
    }

    private boolean isBlockedIP(String ipAddress) {
        // 模拟IP黑名单检查
        return blockedIPs.contains(ipAddress);
    }
}

// 缓存检查处理器
class CacheHandler extends Handler {
    @Override
    public boolean handle(Request request) {
        if (!request.isAuthenticated()) {
            return false;
        }

        // 模拟缓存检查逻辑
        if (hasCachedResult(request)) {
            System.out.println("返回缓存结果");
            return true; // 有缓存结果,直接返回,不继续传递
        }
        
        System.out.println("无缓存结果,继续处理");
        return next == null || next.handle(request);
    }

    private boolean hasCachedResult(Request request) {
        // 模拟缓存检查
        return false; // 默认无缓存
    }
}

// 订单处理处理器(责任链的终点)
class OrderProcessingHandler extends Handler {
    @Override
    public boolean handle(Request request) {
        if (!request.isAuthenticated()) {
            return false;
        }

        // 模拟订单处理逻辑
        System.out.println("订单处理完成");
        return true;
    }
}

// 客户端代码
public class ChainOfResponsibilityDemo {
    public static void main(String[] args) {
        // 构建责任链
        Handler authentication = new AuthenticationHandler();
        Handler sanitization = new DataSanitizationHandler();
        Handler ipFilter = new IPFilterHandler();
        Handler cache = new CacheHandler();
        Handler orderProcessing = new OrderProcessingHandler();

        authentication.setNext(sanitization)
                     .setNext(ipFilter)
                     .setNext(cache)
                     .setNext(orderProcessing);

        // 测试请求
        System.out.println("=== 测试1:正常用户请求 ===");
        Request userRequest = new Request("user_token", "192.168.1.1", "订单数据");
        authentication.handle(userRequest);

        System.out.println("\n=== 测试2:管理员请求 ===");
        Request adminRequest = new Request("admin_token", "192.168.1.2", "订单数据");
        authentication.handle(adminRequest);

        System.out.println("\n=== 测试3:无效凭证请求 ===");
        Request invalidRequest = new Request("invalid_token", "192.168.1.3", "订单数据");
        authentication.handle(invalidRequest);
    }
}

责任链模式和策略模式有点像,他们的区别是:

  • 策略模式: 一个请求只能由一个策略类处理,客户端选择使用哪个策略
  • 责任链模式: 一个请求可以由多个处理器依次处理,每个处理器决定是否处理请求和是否传递