Padrão de Design de Cadeia de Responsabilidade em Java
1. Introdução
Neste artigo, vamos dar uma olhada embehavioral design pattern:Chain of Responsibility amplamente utilizado.
Podemos encontrar mais padrões de projeto em nossoprevious article.
2. Cadeia de Responsabilidade
Wikipedia define Cadeia de Responsabilidade como um padrão de design que consiste em “uma fonte de objetos de comando e uma série de objetos de processamento”.
Cada objeto de processamento na cadeia é responsável por um certo tipo de comando, e o processamento é concluído, ele encaminha o comando para o próximo processador na cadeia.
O padrão da Cadeia de Responsabilidade é útil para:
-
Desacoplar o emissor e o receptor de um comando
-
Escolha uma estratégia de processamento no tempo de processamento
Então, vamos ver um exemplo simples do padrão.
3. Exemplo
Usaremos Chain of Responsibility para criar uma cadeia para lidar com solicitações de autenticação.
Portanto, o provedor de autenticação de entrada será ocommand, e cada processador de autenticação será um objetoprocessor separado.
Vamos primeiro criar uma classe base abstrata para nossos processadores:
public abstract class AuthenticationProcessor {
public AuthenticationProcessor nextProcessor;
// standard constructors
public abstract boolean isAuthorized(AuthenticationProvider authProvider);
}
A seguir, vamos criar processadores concretos que estendemAuthenticationProcessor:
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;
}
}
Aqui, criamos dois processadores concretos para nossas solicitações de autorização de entrada:UsernamePasswordProcessoreOAuthProcessor.
Para cada um, substituímos o métodoisAuthorized.
Agora vamos criar alguns testes:
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()));
}
}
O exemplo acima cria uma cadeia de processadores de autenticação:UsernamePasswordProcessor → OAuthProcessor. No primeiro teste, a autorização é bem-sucedida e, no outro, falha.
Primeiro,UsernamePasswordProcessor verifica se o provedor de autenticação é uma instância deUsernamePasswordProvider.
Não sendo a entrada esperada,UsernamePasswordProcessor delega paraOAuthProcessor.
Por último, oOAuthProcessor processa o comando. No primeiro teste, há uma correspondência e o teste passa. No segundo, não há mais processadores na cadeia e, como resultado, o teste falha.
4. Princípios de Implementação
Precisamos manter alguns princípios importantes em mente ao implementar a Cadeia de Responsabilidade:
-
Cada processador da cadeia terá sua implementação para processar um comando
-
Em nosso exemplo acima, todos os processadores têm sua implementação deisAuthorized
-
-
Cada processador na cadeia deve ter referência ao próximo processador
-
Acima,UsernamePasswordProcessor delega paraOAuthProcessor
-
-
Cada processador é responsável por delegar ao próximo processador, portanto, cuidado com comandos descartados
-
Novamente em nosso exemplo, se o comando for uma instância deSamlProvider, a solicitação pode não ser processada e não será autorizada
-
-
Os processadores não devem formar um ciclo recursivo
-
Em nosso exemplo, não temos um ciclo em nossa cadeia:UsernamePasswordProcessor → OAuthProcessor. Mas, se definirmos explicitamenteUsernamePasswordProcessor como o próximo processador deOAuthProcessor,then we end up with a cycle in our chain:UsernamePasswordProcessor → OAuthProcessor → UsernamePasswordProcessor. Pegar o próximo processador no construtor pode ajudar com isso
-
-
Apenas um processador na cadeia lida com um determinado comando
-
Em nosso exemplo, se um comando de entrada contém uma instância deOAuthTokenProvider, então apenasOAuthProcessor irá lidar com o comando
-
5. Uso no mundo real
No mundo Java, nos beneficiamos da Cadeia de Responsabilidade todos os dias. One such classic example is Servlet Filters inJava que permitem que vários filtros processem uma solicitação HTTP. Embora, nesse caso,each filter invokes the chain instead of the next filter.
Vamos dar uma olhada no trecho de código abaixo para melhor compreensão desse padrão em Filtros de 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);
}
}
Conforme visto no trecho de código acima, precisamos invocar o métodoFilterChain'sdoFilter para passar a solicitação para o próximo processador na cadeia.
6. Desvantagens
E agora que vimos como a Cadeia de Responsabilidade é interessante, vamos ter em mente algumas desvantagens:
-
Principalmente, pode ser quebrado facilmente:
-
se um processador falhar em chamar o próximo processador, o comando será descartado
-
se um processador chama o processador errado, pode levar a um ciclo
-
-
Ele pode criar rastreamentos profundos de pilha, o que pode afetar o desempenho
-
Isso pode levar à duplicação de código nos processadores, aumentando a manutenção
7. Conclusão
Neste artigo, falamos sobre a cadeia de responsabilidade e seus pontos fortes e fracos com a ajuda de uma cadeia para autorizar solicitações de autenticação recebidas.
E, como sempre, o código-fonte pode ser encontradoover on GitHub.