1. 引言

本文将探讨一种广泛应用于Java开发中的行为型设计模式责任链模式

更多设计模式可参考我们的设计模式系列文章

2. 责任链模式

根据Wikipedia的定义,责任链模式由"命令对象源和一系列处理对象"组成。

链中的每个处理对象负责特定类型的命令,处理完成后将命令转发给链中的下一个处理器。

责任链模式特别适用于以下场景:

  • 解耦命令的发送者和接收者
  • 在运行时动态选择处理策略

下面我们通过一个简单示例来理解这个模式。

3. 示例代码

我们将使用责任链模式创建一个处理认证请求的链。其中,认证提供者作为命令,每个认证处理器作为独立的处理对象

首先创建处理器的抽象基类:

public abstract class AuthenticationProcessor {

    public AuthenticationProcessor nextProcessor;
    
    // 标准构造函数

    public abstract boolean isAuthorized(AuthenticationProvider authProvider);
}

接下来创建两个具体处理器,继承自AuthenticationProcessor

public class OAuthProcessor extends AuthenticationProcessor {

    public OAuthProcessor(AuthenticationProcessor nextProcessor) {
        super(nextProcessor);
    }

    @Override
    public boolean isAuthorized(AuthenticationProvider authProvider) {
        if (authProvider instanceof OAuthTokenProvider) {
            return true;
        } else if (nextProcessor != null) {
            return nextProcessor.isAuthorized(authProvider);
        }
        
        return false;
    }
}
public class UsernamePasswordProcessor extends AuthenticationProcessor {

    public UsernamePasswordProcessor(AuthenticationProcessor nextProcessor) {
        super(nextProcessor);
    }

    @Override
    public boolean isAuthorized(AuthenticationProvider authProvider) {
        if (authProvider instanceof UsernamePasswordProvider) {
            return true;
        } else if (nextProcessor != null) {
            return nextProcessor.isAuthorized(authProvider);
        }
    return false;
    }
}

这里我们创建了两个具体处理器:UsernamePasswordProcessorOAuthProcessor,每个都重写了isAuthorized方法。

现在编写测试用例:

public class ChainOfResponsibilityTest {

    private static AuthenticationProcessor getChainOfAuthProcessor() {
        AuthenticationProcessor oAuthProcessor = new OAuthProcessor(null);
        return new UsernamePasswordProcessor(oAuthProcessor);
    }

    @Test
    public void givenOAuthProvider_whenCheckingAuthorized_thenSuccess() {
        AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor();
        assertTrue(authProcessorChain.isAuthorized(new OAuthTokenProvider()));
    }

    @Test
    public void givenSamlProvider_whenCheckingAuthorized_thenSuccess() {
        AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor();
 
        assertFalse(authProcessorChain.isAuthorized(new SamlTokenProvider()));
    }
}

上述示例构建了处理器链:UsernamePasswordProcessor -> OAuthProcessor。第一个测试通过,第二个测试失败。

处理流程:

  1. UsernamePasswordProcessor首先检查认证提供者是否为UsernamePasswordProvider实例
  2. 如果不是,则委托给OAuthProcessor
  3. OAuthProcessor处理命令:
    • 第一个测试匹配成功
    • 第二个测试没有匹配的处理器,导致认证失败

4. 实现原则

实现责任链模式时需牢记以下关键原则:

  • 每个处理器需实现自己的命令处理逻辑
  • 示例中所有处理器都实现了isAuthorized方法
  • 每个处理器必须持有下一个处理器的引用
  • 示例中UsernamePasswordProcessor委托给OAuthProcessor
  • 处理器必须负责调用下一个处理器,避免命令丢失
  • ⚠️ 警告:当命令是SamlProvider实例时,请求可能未被处理导致认证失败
  • 处理器不能形成递归循环
  • 示例链为:UsernamePasswordProcessor -> OAuthProcessor
  • ❌ 如果将UsernamePasswordProcessor设置为OAuthProcessor的下一个处理器,会形成循环:UsernamePasswordProcessor -> OAuthProcessor -> UsernamePasswordProcessor
  • 每个命令只能由一个处理器处理
  • 示例中当命令包含OAuthTokenProvider实例时,仅由OAuthProcessor处理

5. 实际应用

在Java生态中,我们每天都在使用责任链模式。最经典的例子是Java中的Servlet过滤器,它允许多个过滤器处理HTTP请求。不过在该场景中,每个过滤器调用的是过滤器链而非直接调用下一个过滤器

看下面Servlet过滤器的代码片段:

public class CustomFilter implements Filter {

    public void doFilter(
      ServletRequest request,
      ServletResponse response,
      FilterChain chain)
      throws IOException, ServletException {

        // 处理请求

        // 将请求(即命令)传递给过滤器链中的下一个处理器
        chain.doFilter(request, response);
    }
}

如代码所示,必须调用FilterChaindoFilter方法才能将请求传递给链中的下一个处理器。

6. 缺点分析

尽管责任链模式很实用,但也要注意其缺点:

  • 容易破坏链结构
  • 处理器未调用下一个处理器会导致命令丢失
  • 处理器调用错误处理器可能造成循环
  • 可能产生深层堆栈,影响性能
  • 处理器间代码重复,增加维护成本

7. 总结

本文通过一个认证请求处理链的示例,深入探讨了责任链模式的实现原理、优缺点及实际应用场景。

完整源代码可在GitHub仓库获取。


原始标题:Chain of Responsibility Design Pattern in Java