GoF, p. 163 também chamado de Object Tree
Composto
Composite
Trate objetos individuais e grupos de objetos exatamente da mesma forma.
Compor objetos em estruturas de árvore para representar hierarquias todo-parte. Composite permite que os clientes tratem objetos individuais e composições de objetos uniformemente.
Caixas dentro de caixas
Um pedido chega em uma caixa grande. Dentro há caixas menores e produtos soltos; dentro das menores, mais caixas e mais produtos. Para calcular o preço total você não precisa de duas regras: pergunta o preço à caixa de fora, e ela pergunta a cada item dentro dela — produto responde seu próprio preço, caixa repete o processo recursivamente.
Você tem uma estrutura em árvore e precisa executar uma operação sobre ela toda. Sem o padrão, o cliente vive checando `if (é folha) … else percorre filhos …`, e essa verificação se espalha por todo lugar.
Declare uma interface comum a folhas e a nós compostos. O nó composto implementa a operação delegando aos filhos e agregando os resultados. O cliente chama o método na raiz e a recursão faz o resto.
if (item instanceof Grupo) { ...percorre... } else { ...usa... }repetido.- Duas funções quase iguais, uma para item e outra para grupo.
Estrutura
- Component
- Interface comum a folhas e composites.
- Leaf
- Elemento simples, sem filhos; faz o trabalho de verdade.
- Composite
- Contém filhos Component e delega o trabalho a eles.
- Client
- Trabalha com todos os elementos apenas via Component.
Implementação
// ── Component: o contrato único ───────────────────────
class NoDoSistema {
constructor(nome) { this.nome = nome; }
get tamanho() { throw new Error('abstrato'); }
imprimir(nivel = 0) { throw new Error('abstrato'); }
}
// ── Leaf: faz o trabalho de verdade ───────────────────
class Arquivo extends NoDoSistema {
constructor(nome, bytes) { super(nome); this.bytes = bytes; }
get tamanho() { return this.bytes; }
imprimir(n = 0) { return `${' '.repeat(n)}📄 ${this.nome} (${this.bytes} B)`; }
}
// ── Composite: delega aos filhos e agrega ─────────────
class Pasta extends NoDoSistema {
constructor(nome, filhos = []) { super(nome); this.filhos = filhos; }
add(no) { this.filhos.push(no); return this; }
// A MESMA operação, resolvida recursivamente. Sem `if (é pasta)`.
get tamanho() { return this.filhos.reduce((soma, f) => soma + f.tamanho, 0); }
imprimir(n = 0) {
return [`${' '.repeat(n)}📁 ${this.nome}/ (${this.tamanho} B)`,
...this.filhos.map(f => f.imprimir(n + 1))].join('\n');
}
}
const projeto = new Pasta('projeto')
.add(new Arquivo('README.md', 1200))
.add(new Pasta('src')
.add(new Arquivo('index.js', 4800))
.add(new Pasta('utils').add(new Arquivo('data.js', 900))));
console.log(projeto.imprimir());
console.log('total:', projeto.tamanho, 'bytes'); // 6900 — recursão transparente - O DOM do navegador — Node, Element, TextNode
- java.awt.Container / javax.swing.JComponent
- Árvore de componentes React: um componente pode ser folha ou conter outros
Consequências
- Trabalha com árvores complexas de forma simples, via polimorfismo e recursão.
- Adiciona novos tipos de elemento sem quebrar o código existente (Aberto/Fechado).
- O cliente fica trivialmente simples: uma chamada na raiz.
- Pode ser difícil dar uma interface comum a classes muito diferentes — o risco é uma interface genérica demais.
- Métodos como `add`/`remove` na interface comum não fazem sentido para folhas (dilema segurança × transparência).
- Seu modelo de dados tem forma de árvore (menus, organogramas, DOM, categorias).
- Você quer que o cliente ignore a diferença entre item e grupo.
- A estrutura não é hierárquica — forçar Composite aqui só complica.
Relações com outros padrões
- Builder
- Builder monta árvores Composite passo a passo.
- Iterator
- Iterator percorre a árvore sem expor sua estrutura interna.
- Visitor
- Visitor executa operações sobre a árvore inteira sem inchar as classes dos nós.
- Chain of Responsibility
- O elo seguinte da cadeia pode ser o nó pai na árvore.
- Flyweight
- Folhas repetidas podem ser compartilhadas como flyweights.
- Decorator
- Ambos são estruturas recursivas; Decorator tem exatamente UM filho e adiciona responsabilidade.
Verificação
O benefício central do Composite para o código CLIENTE é:
- Poder tratar um objeto único e um grupo inteiro com a mesma chamada, sem verificar o tipo
- Reduzir o consumo de memória em árvores grandes
- Permitir adicionar comportamento em tempo de execução
- Garantir que a árvore nunca tenha mais de um nível
A uniformidade é o ponto. Reduzir memória por compartilhamento é Flyweight; adicionar comportamento dinamicamente é Decorator.