Chain of Responsibility Design Pattern in Java – Java中的责任链设计模式

最后修改: 2018年 3月 15日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

In this article, we’re going to take a look at a widely used behavioral design pattern: Chain of Responsibility.

在这篇文章中,我们要看一下一个广泛使用的行为设计模式责任链

We can find more design patterns in our previous article.

我们可以在我们的前文中找到更多的设计模式

2. Chain of Responsibility

2.责任链

Wikipedia defines Chain of Responsibility as a design pattern consisting of “a source of command objects and a series of processing objects”.

维基百科将责任链定义为一种设计模式,由 “一个命令对象的来源和一系列的处理对象 “组成。

Each processing object in the chain is responsible for a certain type of command, and the processing is done, it forwards the command to the next processor in the chain.

链上的每个处理对象负责某种类型的命令,处理完成后,它将命令转发给链上的下一个处理器。

The Chain of Responsibility pattern is handy for:

责任链模式很方便。

  • Decoupling a sender and receiver of a command
  • Picking a processing strategy at processing-time

So, let’s see a simple example of the pattern.

那么,让我们看看这个模式的一个简单例子。

3. Example

3.实例

We’re going to use Chain of Responsibility to create a chain for handling authentication requests.

我们将使用责任链来创建一个处理认证请求的链条。

So, the input authentication provider will be the command, and each authentication processor will be a separate processor object.

因此,输入认证提供者将是command,而每个认证处理器将是一个单独的processor对象。

Let’s first create an abstract base class for our processors:

让我们首先为我们的处理器创建一个抽象的基类。

public abstract class AuthenticationProcessor {

    public AuthenticationProcessor nextProcessor;
    
    // standard constructors

    public abstract boolean isAuthorized(AuthenticationProvider authProvider);
}

Next, let’s create concrete processors which extend AuthenticationProcessor:

接下来,让我们创建扩展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;
    }
}

Here, we created two concrete processors for our incoming authorization requests: UsernamePasswordProcessor and OAuthProcessor.

在这里,我们为传入的授权请求创建了两个具体的处理程序。UsernamePasswordProcessorOAuthProcessor

For each one, we overrode the isAuthorized method.

对于每一个人,我们都覆盖了isAuthorized方法。

Now let’s create a couple of tests:

现在让我们创建几个测试。

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

The example above creates a chain of authentication processors: UsernamePasswordProcessor -> OAuthProcessor. In the first test, the authorization succeeds, and in the other, it fails.

上面的例子创建了一个认证处理程序链。UsernamePasswordProcessor -> OAuthProcessor。在第一个测试中,授权成功,而在另一个测试中,授权失败。

First, UsernamePasswordProcessor checks to see if the authentication provider is an instance of UsernamePasswordProvider.

首先,UsernamePasswordProcessor检查认证提供者是否是UsernamePasswordProvider的一个实例。

Not being the expected input, UsernamePasswordProcessor delegates to OAuthProcessor.

不是预期的输入,UsernamePasswordProcessor委托给OAuthProcessor

Last, the OAuthProcessor processes the command. In the first test, there is a match and the test passes. In the second, there are no more processors in the chain, and, as a result, the test fails.

最后,OAuthProcessor处理该命令。在第一个测试中,有一个匹配,测试通过。在第二个测试中,链中没有更多的处理器,因此,测试失败。

4. Implementation Principles

4.实施原则

We need to keep few important principles in mind while implementing Chain of Responsibility:

在实施责任链时,我们需要牢记几个重要的原则。

  • Each processor in the chain will have its implementation for processing a command
    • In our example above, all processors have their implementation of isAuthorized
  • Every processor in the chain should have reference to the next processor
    • Above, UsernamePasswordProcessor delegates to OAuthProcessor
  • Each processor is responsible for delegating to the next processor so beware of dropped commands
    • Again in our example, if the command is an instance of SamlProvider then the request may not get processed and will be unauthorized
  • Processors should not form a recursive cycle
    • In our example, we don’t have a cycle in our chain: UsernamePasswordProcessor -> OAuthProcessor. But, if we explicitly set UsernamePasswordProcessor as next processor of OAuthProcessor, then we end up with a cycle in our chain: UsernamePasswordProcessor -> OAuthProcessor -> UsernamePasswordProcessor. Taking the next processor in the constructor can help with this
  • Only one processor in the chain handles a given command
    • In our example, if an incoming command contains an instance of OAuthTokenProvider, then only OAuthProcessor will handle the command

5. Usage in the Real World

5.在现实世界中的使用情况

In the Java world, we benefit from Chain of Responsibility every day. One such classic example is Servlet Filters in Java that allow multiple filters to process an HTTP request. Though in that case, each filter invokes the chain instead of the next filter.

在Java世界中,我们每天都从责任链中受益。其中一个经典的例子是Java中的Servlet过滤器,它允许多个过滤器处理一个HTTP请求。尽管在这种情况下,每个过滤器都会调用链,而不是下一个过滤器。

Let’s take a look at the code snippet below for better understanding of this pattern in Servlet Filters:

让我们看看下面的代码片段,以便更好地理解Servlet过滤器中的这种模式

public class CustomFilter implements Filter {

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

        // process the request

        // pass the request (i.e. the command) along the filter chain
        chain.doFilter(request, response);
    }
}

As seen in the code snippet above, we need to invoke FilterChain‘s doFilter method in order to pass the request on to next processor in the chain.

正如上面的代码片段所见,我们需要调用FilterChaindoFilter方法,以便将请求传递给链上的下一个处理器。

6. Disadvantages

6.劣势

And now that we’ve seen how interesting Chain of Responsibility is, let’s keep in mind some drawbacks:

现在我们已经看到了责任链是多么有趣,让我们牢记一些缺点。

  • Mostly, it can get broken easily:
    • if a processor fails to call the next processor, the command gets dropped
    • if a processor calls the wrong processor, it can lead to a cycle
  • It can create deep stack traces, which can affect performance
  • It can lead to duplicate code across processors, increasing maintenance

7. Conclusion

7.结论

In this article, we talked about Chain of Responsibility and its strengths and weaknesses with the help of a chain to authorize incoming authentication requests.

在这篇文章中,我们谈到了责任链和它的优势和劣势,借助于责任链来授权传入的认证请求。

And, as always, the source code can be found over on GitHub.

而且,像往常一样,可以在GitHub上找到源代码