Pular para o conteúdo
Comportamentais

Chain of Responsibility

Evita acoplar o remetente de uma requisição ao seu receptor, dando a mais de um objeto a chance de tratar a requisição. Encadeia os objetos receptores e passa a requisição ao longo da cadeia até que um objeto a trate.

Intenção

Desacoplar o remetente de uma requisição do seu receptor, permitindo que múltiplos objetos tratem a requisição em sequência. Cada handler decide processar a requisição ou passá-la adiante para o próximo handler da cadeia.

Problema

Você tem uma requisição que poderia ser tratada por vários objetos diferentes, e não quer que o remetente saiba qual objeto específico vai tratá-la. Fixar a seleção de handler no código cria acoplamento forte e torna o sistema rígido quando novos handlers precisam ser adicionados ou a ordem precisa mudar.

Solução

Defina uma interface Handler com um método para processar requisições e uma referência ao próximo handler. Cada ConcreteHandler processa a requisição se puder, ou a encaminha para seu sucessor. O cliente envia a requisição ao primeiro handler da cadeia sem saber qual handler vai processá-la ao final. Handlers podem ser reordenados, adicionados, ou removidos sem afetar o cliente.

Participantes

  • Handler — define a interface para tratar requisições e opcionalmente mantém uma referência ao próximo handler
  • BaseHandler — classe abstrata opcional que implementa o comportamento padrão de encadeamento (encaminhar ao próximo handler)
  • ConcreteHandler — trata as requisições pelas quais é responsável; pode acessar seu sucessor para encaminhar requisições não tratadas
  • Client — inicia a requisição no primeiro handler da cadeia

Vantagens

  • Reduz acoplamento — o remetente não precisa saber qual handler processa a requisição
  • Composição flexível de cadeia — handlers podem ser adicionados, removidos, ou reordenados em tempo de execução
  • Cada handler tem uma única responsabilidade, seguindo o Princípio da Responsabilidade Única
  • Uma requisição pode ser tratada por múltiplos handlers em sequência (processamento estilo middleware)

Desvantagens

  • Não há garantia de que uma requisição será tratada — ela pode cair no fim da cadeia sem tratamento
  • A depuração pode ser difícil porque o fluxo passa por múltiplos objetos
  • A configuração da cadeia precisa estar correta; uma ordem incorreta pode produzir resultados errados
  • O desempenho pode sofrer com cadeias muito longas, já que cada handler adiciona overhead

Analogia do mundo real

Ligar para a central de atendimento de uma empresa é uma cadeia de responsabilidade. Sua ligação vai primeiro para um atendente de linha de frente. Se ele não conseguir resolver seu problema, escala para um especialista. Se o especialista também não conseguir ajudar, vai para um gerente. Você, o chamador, não escolhe quem trata seu problema — você só entra na cadeia e cada nível decide se trata ou escala.

Casos de uso

  • Pipelines de middleware HTTP — autenticação, logging, CORS, rate limiting, compressão
  • Cadeias de validação — cada validador checa uma regra e passa adiante se válida
  • Roteamento de tickets de suporte — escalar do nível 1 para o nível 2 e depois nível 3
  • Propagação de eventos no DOM — eventos se propagam de elementos filhos para elementos pais
  • Fluxos de aprovação — gerente, diretor, VP, cada um com diferentes níveis de autoridade
  • Tratamento de exceções — blocos catch formam uma cadeia que trata diferentes tipos de exceção

Exemplos de código

Handler.java
import java.util.HashMap;
import java.util.Map;

final class Request {
    final String method;
    final String path;
    final Map<String, String> headers;
    final String clientIp;

    Request(String method, String path, Map<String, String> headers, String clientIp) {
        this.method = method;
        this.path = path;
        this.headers = headers;
        this.clientIp = clientIp;
    }
}

interface Handler {
    Handler setNext(Handler handler);
    Request handle(Request request);
}

abstract class BaseHandler implements Handler {
    private Handler next;

    @Override
    public Handler setNext(Handler handler) {
        this.next = handler;
        return handler; // enables chaining: a.setNext(b).setNext(c)
    }

    @Override
    public Request handle(Request request) {
        if (next != null) {
            return next.handle(request);
        }
        return request; // end of chain -- request passed all checks
    }
}

final class AuthHandler extends BaseHandler {
    @Override
    public Request handle(Request request) {
        if (!request.headers.containsKey("Authorization")) {
            System.out.println("401: missing Authorization header");
            return null;
        }
        return super.handle(request);
    }
}

final class RateLimitHandler extends BaseHandler {
    private final Map<String, Integer> hitsByIp = new HashMap<>();

    @Override
    public Request handle(Request request) {
        int hits = hitsByIp.merge(request.clientIp, 1, Integer::sum);
        if (hits > 100) {
            System.out.println("429: rate limit exceeded for " + request.clientIp);
            return null;
        }
        return super.handle(request);
    }
}

final class LoggingHandler extends BaseHandler {
    @Override
    public Request handle(Request request) {
        System.out.println("[log] " + request.method + " " + request.path);
        return super.handle(request);
    }
}

public class ChainOfResponsibilityDemo {
    public static void main(String[] args) {
        Handler auth = new AuthHandler();
        auth.setNext(new RateLimitHandler()).setNext(new LoggingHandler());

        Map<String, String> headers = new HashMap<>();
        headers.put("Authorization", "Bearer token123");
        Request request = new Request("GET", "/api/users", headers, "10.0.0.1");

        Request result = auth.handle(request);
        System.out.println(result != null ? "Request passed the chain" : "Request rejected");
    }
}

Um pipeline de middleware HTTP onde AuthHandler, RateLimitHandler, e LoggingHandler cada um inspeciona a requisição e decide se passa ao próximo handler ou rejeita.