O padrão Chain of Responsibility é um padrão de design comportamental que tem como objetivo principal desacoplar o remetente de uma solicitação de seus receptores, permitindo que mais de um objeto possa tratar a solicitação.
Esse padrão estabelece uma cadeia de objetos receptores nos quais a solicitação passa sequencialmente até ser tratada. Ele resolve vários problemas:
-
Desacoplamento de Remetente e Receptores: Em sistemas onde um remetente precisa interagir com vários possíveis receptores, mas não deve saber qual deles irá efetivamente tratar a solicitação, o Chain of Responsibility permite essa flexibilidade.
-
Flexibilidade na Tratativa de Solicitações: Permite que diferentes objetos em uma cadeia tenham a chance de tratar a solicitação, o que é útil quando o remetente não sabe exatamente quem deve processá-la, ou quando há múltiplos objetos capazes de realizar a tarefa.
-
Dinamismo no Encadeamento: A cadeia de responsabilidades pode ser alterada dinamicamente em tempo de execução, adicionando ou removendo handlers, o que proporciona grande flexibilidade.
-
Redução de Acoplamento: Ao invés de manter uma série de condicionais e acoplamentos rígidos, o padrão permite a criação de uma estrutura onde cada objeto na cadeia decide se pode ou não tratar a solicitação e quem seria o próximo candidato.
Como Funciona
O padrão Chain of Responsibility geralmente envolve os seguintes componentes:
- Handler (Interface ou Classe Abstrata): Define uma interface para tratar solicitações. Pode implementar a passagem da solicitação ao longo da cadeia.
- Concrete Handlers: Classes que estendem a classe Handler ou implementam a interface. Cada uma trata a solicitação de maneira específica e passa a solicitação ao longo da cadeia se não puder tratá-la.
- Client: Inicia a solicitação para um objeto Handler na cadeia.
Exemplo
Um exemplo clássico do uso do Chain of Responsibility é em sistemas de processamento de pagamentos ou aprovações de requisições, onde uma solicitação passa por vários verificadores/validadores, cada um realizando sua verificação e decidindo se pode tratar a solicitação ou passá-la adiante na cadeia.
Exemplo de Implementação
Vamos criar um exemplo do padrão Chain of Responsibility em Java. Vamos implementar um sistema de aprovação de despesas onde diferentes gerentes têm limites variados de aprovação.
Interface Handler (Aprovador)
Primeiro, definimos a interface Aprovador, que será implementada por todos os aprovadores na cadeia:
public interface Aprovador {
void setProximo(Aprovador proximo);
void aprovarDespesa(Despesa despesa);
}Classe Concrete Handler (Gerente, Diretor, CEO)
Agora, implementamos as classes concretas Gerente, Diretor e CEO:
public class Gerente implements Aprovador {
private Aprovador proximo;
@Override
public void setProximo(Aprovador proximo) {
this.proximo = proximo;
}
@Override
public void aprovarDespesa(Despesa despesa) {
if (despesa.getValor() <= 1000) {
System.out.println("Despesa aprovada pelo Gerente");
} else {
proximo.aprovarDespesa(despesa);
}
}
}
public class Diretor implements Aprovador {
private Aprovador proximo;
@Override
public void setProximo(Aprovador proximo) {
this.proximo = proximo;
}
@Override
public void aprovarDespesa(Despesa despesa) {
if (despesa.getValor() <= 5000) {
System.out.println("Despesa aprovada pelo Diretor");
} else {
proximo.aprovarDespesa(despesa);
}
}
}
public class CEO implements Aprovador {
@Override
public void setProximo(Aprovador proximo) {
// Não é necessário neste nível
}
@Override
public void aprovarDespesa(Despesa despesa) {
System.out.println("Despesa aprovada pelo CEO");
}
}Classe Despesa
Vamos criar uma classe simples para representar a despesa:
public class Despesa {
private int valor;
public Despesa(int valor) {
this.valor = valor;
}
public int getValor() {
return valor;
}
}Testando o Padrão Chain of Responsibility
Vamos montar a cadeia e testar a aprovação de despesas:
public class Main {
public static void main(String[] args) {
Aprovador gerente = new Gerente();
Aprovador diretor = new Diretor();
Aprovador ceo = new CEO();
gerente.setProximo(diretor);
diretor.setProximo(ceo);
Despesa despesa = new Despesa(4500);
gerente.aprovarDespesa(despesa);
}
}Neste exemplo, cada aprovador (Gerente, Diretor e CEO) pode aprovar despesas até um certo valor. Se a despesa exceder esse valor, ela é passada para o próximo aprovador na cadeia. Isso demonstra como o padrão Chain of Responsibility permite que diferentes objetos tratem a solicitação sequencialmente e de maneira desacoplada.