Índice do grimório

Padrão 10 · Estruturais

Composite

aplicar uma ordem a folhas e formações numa árvore parte-todo.

Começar pelo código
Colosso de raízes carbonizadas avança pela floresta em chamas enquanto Leda e moradores retiram Otília numa esteira.
Cena do capítulo 10 · Composite

01 · Entenda a escolha

Os fundamentos do padrão.

README.md

10 — Composite: Formações de unidades recursivas

Unidade.executarOrdem() é o contrato comum de Soldado e Formacao. A folha devolve seu evento; a formação registra sua própria entrada e percorre seus membros recursivamente. A operação devolve dados verificáveis, além de permitir formatar uma saída de console.

Executar

Node 26.10.0, npm 11.19.1, TypeScript 5.9.3, @types/node 26.0.0, lock independente. Dentro deste diretório:

npm ci --ignore-scripts
npm test
npm start
npm run solution

Da raiz Books:

node book-23/scripts/projetos.mjs 10 install
node book-23/scripts/projetos.mjs 10 check
node book-23/scripts/projetos.mjs 10 capture
node book-23/scripts/projetos.mjs 10 verify

O build limpa dist, compila ES2022/ESM com TypeScript estrito e executa testes Node sobre JavaScript emitido. Nenhum projeto vizinho é importado. exercise/ fica separado da solução e fora da compilação oficial.

Fontes e papéis

  • src/contratos.ts: Component Unidade e evento de relatório.
  • src/arvore.ts: Leaf Soldado, Composite Formacao e integridade da árvore.
  • src/main.ts: dois níveis de formações e três folhas, com relatório completo.
  • src/direto.ts: alternativa de lista plana executada em teste.
  • test/arvore.test.ts: ordem exata, folha, identidade, duplicidade, ciclos, cópias e limites.
  • exercise/ e solution/: variante editável com remoção e reutilização de subárvore.
  • diagram.mmd: fonte lógica editável; renderização e QA visual na etapa de arte.
  • evidence/: stdout real, testes, manifesto e execução em cópia isolada.

Percurso e resultado

O percurso é pré-ordem: primeiro o evento do grupo, depois cada membro pela ordem de inserção. Na demonstração, a sequência é Formação de Mirralis → Pelotão do Norte → Alaric → Elora → Lorn. Há duas entradas completas de formação no stdout, correspondentes aos dois objetos de grupo. Essa é a correção do erro de saída do artigo original, que omitia uma das entradas de grupo efetivamente executadas.

Cada evento contém tipo, nome e ordem. Nome e ordem são normalizados e não podem ficar vazios. O relatório e seus eventos são congelados; cada execução produz novos valores e não altera os relatórios anteriores. Formação vazia ainda gera seu próprio evento. Os dados registram a visita da operação a cada nó, não a execução física de uma retirada, o recebimento de um sinal remoto ou o resultado de um resgate.

Política de árvore e identidade

Formacao.adicionar() aceita os nós fornecidos pelo projeto (Soldado ou Formacao, incluindo a variante do exercício). Cada objeto pode ter no máximo um pai. Auto inclusão, duplicação do mesmo objeto no grupo, inclusão de ancestral e compartilhamento de um objeto entre grupos são recusados antes de modificar os vínculos. O controle de pai fica em um WeakMap interno do módulo e a lista de membros em um campo # da formação.

Identidade é a referência do objeto, não seu nome: duas instâncias chamadas Homônimo são dois nós. Essa convenção não deduplica pessoas reais. Para representar a mesma unidade no domínio, o chamador precisa conservar sua referência em vez de criar outro objeto com nome repetido. Não existe catálogo global de pessoas ou garantia de identidade distribuída.

listarMembros() devolve uma cópia defensiva do array. Os próprios nós são referências intencionais da árvore, não clones profundos: uma formação continua sendo a mesma quando retirada e reutilizada. Alterar a lista consultada não altera a estrutura nem solta seus vínculos. Soldado não oferece filhos, adicionar() ou remover(); gestão é responsabilidade do Composite.

O modelo é uma árvore, não um grafo geral: compartilhamento de subárvore entre dois pais não é permitido. As garantias valem para as operações e classes fornecidas neste módulo; interfaces TypeScript, protected e verificações instanceof não impedem toda manipulação arbitrária em JavaScript ou a escrita de subclasses que violem contratos. Não é um mecanismo de segurança.

Remoção e escala

A primitiva protegida desvincularMembro() mantém lista e pai consistentes. A solução FormacaoEditavel expõe remover() publicamente. Remoção ausente devolve false e não libera a unidade de outro grupo. Remoção presente libera somente o vínculo externo; os descendentes permanecem ligados à subárvore. Incluir a subárvore retirada em outro grupo preserva sua identidade, e os testes mostram que isso não permite recriar um ciclo.

Remover e incluir são operações separadas, sem transação ou transferência automática. O cliente escolhe a nova estrutura explicitamente. O percurso é síncrono e recursivo; uma árvore extremamente profunda pode atingir o limite da pilha. Não se afirma escala ilimitada, segurança contra mutação externa durante o percurso ou suporte a grafos cíclicos. O relatório é um traço em memória, sem persistência.

Custos, alternativas e comparações

Composite acrescenta estrutura e uma política de gestão que se justificam quando há relação parte-todo e operações recursivas úteis. Se só existem unidades independentes, executarListaPlana() aplica o contrato em uma coleção sem inventar um grupo; o teste demonstra essa alternativa. Uma lista plana não acrescenta hierarquia ou política de pai automaticamente.

Decorator envolve um componente para acrescentar uma responsabilidade à sua operação. Composite reúne zero ou mais componentes em uma estrutura parte-todo e aplica a operação recursivamente. Ter o mesmo contrato ou conter outros objetos não basta para tornar as duas intenções equivalentes.

Testes e hashes vinculam o relatório à execução real; não são auditoria técnica independente ou aprovação do autor e não sustentam promessas de desempenho ou segurança física.

02 · Leia o mecanismo

O feitiço, linha por linha.

Comece por src/main.ts para ver o programa em uso. Explore os contratos, a implementação e os testes no seletor de arquivos.

src/main.tsTypeScript
import { Formacao, Soldado } from "./arvore.js";

const pelotao = new Formacao("Pelotão do Norte");
pelotao.adicionar(new Soldado("Alaric"));
pelotao.adicionar(new Soldado("Elora"));
const formacao = new Formacao("Formação de Mirralis");
formacao.adicionar(pelotao);
formacao.adicionar(new Soldado("Lorn"));

for (const evento of formacao.executarOrdem("recuar")) {
  console.log(`${evento.tipo === "formacao" ? "Formação" : "Soldado"}: ${evento.nome} | ordem ${evento.ordem}`);
}
A execução do programa é feita localmente.

03 · Revele as conexões

A estrutura por trás da magia.

Veja os papéis e suas relações. Amplie cada vista para ler atributos, métodos e conexões com calma.

Soldado realiza Unidade.
10 · a operação

Esta vista reúne Unidade, Soldado, EventoOrdem. Soldado realiza Unidade. Unidade depende de EventoOrdem. Soldado: Folha sem adicionar, remover ou filhos. EventoOrdem: Pré-ordem: grupo antes dos filhos; irmãos na inserção. Relatório congelado. Membros exibidos são um recorte; construtores e demais membros podem estar omitidos.

Formacao realiza Unidade.
10 · b agregação

Esta vista reúne Unidade, Formacao. Formacao realiza Unidade. Formacao agrega Unidade (filhos do modelo); multiplicidade no destino 0..*. Formacao: Gestão aceita Soldado ou Formacao. Cada objeto tem no máximo um pai. Auto, ancestral e duplicado recusados. Membros exibidos são um recorte; construtores e demais membros podem estar omitidos. e3 · filhos do modelo

04 · Faça sua escolha

Sua vez de conjurar.

exercise/README.md

Exercício — Retirar sem romper a árvore

Crie a variante FormacaoEditavel extends Formacao e acrescente remover(unidade): boolean. A infraestrutura de integridade já oferece a primitiva protegida desvincularMembro(), responsável por alterar a lista e liberar o vínculo do pai juntos. Use-a para não duplicar ou corromper a política de árvore.

Critérios verificáveis:

  • Remover um filho direto presente devolve true; remover ausente ou repetido devolve false.
  • Uma tentativa de remoção pela formação errada não libera a unidade da formação correta.
  • Remover uma subárvore solta apenas seu vínculo externo; os descendentes continuam vinculados dentro dela.
  • A subárvore retirada pode ser adicionada a outra formação, com identidade e comportamento preservados.
  • A ordem dos irmãos restantes é mantida.
  • Auto inclusão, inclusão de ancestral e duplicidade continuam rejeitadas depois da remoção e reutilização.
  • Soldado continua sem operações de gestão de filhos.

Compare com solution/formacao-editavel.ts e solution/remocao.test.ts; execute npm run solution. Tentativas em exercise/ não são compiladas junto da resposta oficial.

Resposta comentada

A política é uma árvore estrita por identidade de objeto: um nó tem no máximo um pai. A variante oferece a remoção pública sem colocar adicionar() ou remover() em uma folha. A primitiva fornecida localiza somente um filho direto e só apaga seu vínculo de pai quando realmente o encontra; uma remoção ausente não afeta outra árvore.

A retirada e a inclusão no destino são chamadas separadas. Não há transferência atômica nem rollback: o cliente precisa saber se deseja incluir e tratar um erro da inclusão. Um destino descendente não torna válida uma ligação cíclica; as regras de ancestralidade continuam aplicáveis.

protected é uma restrição TypeScript para a API de extensão, não uma barreira de segurança JavaScript. A manutenção das regras pelas operações fornecidas não certifica subclasses arbitrárias ou manipulação de protótipos pelo chamador.

Compare depois da sua tentativa.

A resposta e seus testes estão na pasta solution/. O sucesso dos testes fornecidos confirma essa resposta; sua tentativa precisa estar incluída na compilação para ser avaliada.

Do estudo à prática

Experimente localmente.

Node 26.10.0 · npm 11.19.1 · TypeScript 5.9.3

Execute os comandos dentro da pasta deste projeto. O lock instala o compilador local. Os programas não são executados no navegador.

cd book-23/code/10-composite
npm ci --ignore-scripts --no-audit --no-fund
npm test
npm start

# Depois da sua tentativa
npm run solution
Baixar este projeto completo

Diagrama ampliado