GoF, p. 223 também chamado de CoR, Chain of Command
Cadeia de Responsabilidade
Chain of Responsibility
Passe o pedido por uma fila de handlers até que um deles resolva.
Evitar o acoplamento entre o remetente de uma solicitação e seu receptor, dando a mais de um objeto a chance de tratar a solicitação. Encadear os objetos receptores e passar a solicitação ao longo da cadeia até que um a trate.
O suporte técnico em níveis
Você liga para o suporte. O robô tenta resolver; não conseguiu, passa para o atendente nível 1; não conseguiu, nível 2; não conseguiu, engenharia. Cada nível conhece apenas o próximo. Você, do outro lado da linha, não faz ideia de quantos níveis existem — só fez um pedido.
Um pedido precisa passar por várias verificações sequenciais (autenticação, limite de taxa, cache, validação, sanitização). Empilhar tudo numa função gera um bloco monstruoso de `if`s aninhados, impossível de reordenar ou reaproveitar em outra rota.
Transforme cada verificação em um objeto handler com um único método `tratar()`. Cada handler guarda uma referência ao próximo e decide: trata e para, trata e passa adiante, ou passa adiante direto. A cadeia é montada dinamicamente pelo cliente.
- Uma função com dez
if (…) return erro;em sequência. - A mesma sequência de validações copiada entre controllers.
Estrutura
- Handler
- Interface comum; normalmente guarda o próximo elo.
- BaseHandler
- (opcional) Implementa o encadeamento e o repasse padrão.
- ConcreteHandler
- Trata o que sabe tratar; decide se interrompe ou continua.
- Client
- Monta a cadeia e dispara o pedido no primeiro elo.
Implementação
class Handler {
#proximo = null;
// Encadeamento fluente: a.encadear(b).encadear(c) devolve a ponta.
encadear(proximo) { this.#proximo = proximo; return proximo; }
tratar(req) {
// Comportamento padrão: repassar. Se não há próximo, ninguém barrou → OK.
return this.#proximo ? this.#proximo.tratar(req) : { ok: true, req };
}
}
class Autenticacao extends Handler {
tratar(req) {
if (!req.token) return { ok: false, erro: '401 sem token' }; // ⛔ interrompe
req.usuario = { id: 7, papel: 'editor' };
return super.tratar(req); // ✅ segue
}
}
class Autorizacao extends Handler {
constructor(papel) { super(); this.papel = papel; }
tratar(req) {
if (req.usuario.papel !== this.papel) return { ok: false, erro: '403 sem permissão' };
return super.tratar(req);
}
}
class LimiteDeTaxa extends Handler {
#contagem = new Map();
tratar(req) {
const n = (this.#contagem.get(req.ip) ?? 0) + 1;
this.#contagem.set(req.ip, n);
if (n > 3) return { ok: false, erro: '429 muitas requisições' };
return super.tratar(req);
}
}
class Sanitizacao extends Handler {
tratar(req) {
req.corpo = String(req.corpo ?? '').replace(/<[^>]*>/g, ''); // trata e SEGUE
return super.tratar(req);
}
}
// ── O cliente monta a cadeia — a ORDEM é uma decisão dele ──
const inicio = new LimiteDeTaxa();
inicio.encadear(new Autenticacao())
.encadear(new Autorizacao('editor'))
.encadear(new Sanitizacao());
console.log(inicio.tratar({ ip: '1.1.1.1', token: 'abc', corpo: '<script>x</script>Oi' }));
// { ok: true, req: { …, corpo: 'Oi' } }
console.log(inicio.tratar({ ip: '1.1.1.1' })); // { ok: false, erro: '401 sem token' } - javax.servlet.Filter / FilterChain
- Middlewares de Express, Koa, ASP.NET Core
- Propagação de eventos no DOM (bubbling)
- java.util.logging.Logger — handlers em cadeia
Consequências
- Controla a ordem de tratamento a partir do cliente.
- Cada handler faz uma coisa só (Responsabilidade Única).
- Novos handlers entram sem alterar os existentes (Aberto/Fechado).
- Desacopla quem pede de quem trata.
- Um pedido pode chegar ao fim sem ser tratado por ninguém.
- Depuração fica mais difícil: o fluxo é dinâmico e não aparece no código.
- Vários objetos podem tratar um pedido e o handler certo só é conhecido em tempo de execução.
- A ordem de execução dos tratamentos precisa ser configurável.
- O conjunto de handlers muda dinamicamente.
- Sempre existe exatamente um tratador conhecido — uma chamada direta é mais clara.
Relações com outros padrões
- Decorator
- Estruturas muito parecidas; na Chain qualquer elo pode ABORTAR o fluxo, no Decorator todos executam.
- Mediator
- Outra forma de desacoplar remetente e receptor, mas centralizada.
Verificação
Qual a diferença prática entre Chain of Responsibility e Decorator?
- Na Chain, um elo pode interromper e não repassar; no Decorator todas as camadas participam da chamada
- A Chain só funciona com no máximo três elos
- O Decorator não pode alterar os argumentos
- A Chain exige herança e o Decorator, composição
Ambos formam uma pilha de objetos que se delegam. A intenção difere: a Chain busca QUEM trata (e para ali); o Decorator ENRIQUECE, e todos contribuem.