Índice do grimório

Padrão 03 · Criacionais

Builder

construir por etapas uma ficha ou um relatório usando receitas de expedição.

Começar pelo código
Uma gárgula de pedra investe contra a janela da torre enquanto Tessa e Ivo recolhem o operário ferido para a escada.
Cena do capítulo 03 · Builder

01 · Entenda a escolha

Os fundamentos do padrão.

README.md

03 — Builder: Construtor de personagens e equipamentos

O produto é uma ficha de configuração de um participante de resgate, com nome, classe, vida, missão e equipamento. O programa não cria uma pessoa. Duas receitas constroem perfis de torre e galeria. O mesmo contrato de etapas produz uma ficha estruturada (PersonagemBuilder) ou um relatório textual (RelatorioBuilder).

Executar

Versões de reprodução: Node 26.10.0, npm 11.19.1, TypeScript 5.9.3, @types/node 26.0.0, dependências fixadas no lock. Dentro deste diretório:

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

Da raiz Books:

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

Compilação estrita para ES2022/ESM com tsc local; testes Node sobre JavaScript emitido. Cada build limpa dist. Nenhum projeto vizinho é importado. exercise/ fica separado da solução e fora da compilação oficial.

Papéis e fontes

  • src/contratos.ts: produto, equipamento, missão e interface Builder EtapasDeExpedicao.
  • src/builders.ts: builders concretos com retornos próprios; base interna compartilha rascunho e etapas.
  • src/validacao.ts: invariantes e cópia do produto, sem entrega de alias mutável.
  • src/diretor.ts: Director opcional e duas receitas independentes da representação.
  • src/direto.ts: alternativa executável com objeto de opções, sem Builder.
  • src/main.ts: duas fichas, preservação da primeira, relatório e recusa de ficha incompleta.
  • test/: contratos de construção, reuso, entradas mutáveis, imutabilidade e alternativa direta.
  • exercise/ e solution/: proteção especial e comparação com objeto de opções.
  • diagram.mmd: relações editáveis do código-base; renderização e QA visual ficam na etapa própria.
  • evidence/: stdout real, testes e manifesto SHA-256; isolation.json registra execução numa cópia sem o livro.

Construção e política de reuso

Os builders aceitam etapas em chamadas separadas. Seus métodos retornam this para permitir encadeamento, mas o contrato visto pelo Director não depende disso. build() pertence ao builder concreto, com retorno FichaDePersonagem ou string; o Director só coordena etapas. Assim o exemplo demonstra construção por etapas com representações distintas, além de uma API fluent com setters.

build() valida e retém o rascunho. Cada chamada bem-sucedida devolve um novo snapshot; uma falha permite corrigir o rascunho e tentar novamente. reiniciar() descarta o rascunho explicitamente. Cada receita do Director chama reiniciar() antes das outras etapas, evitando a herança acidental de equipamentos. Sem Director, o chamador escolhe quando reiniciar.

Nome e classe são obrigatórios e normalizados com trim. Vida é um número positivo finito; não modela saúde real. Missão é torre ou galeria. Torre exige ao menos um item com proteção vento, galeria exige agua; todos os equipamentos precisam de nome não vazio. A validação ocorre ao construir o resultado, não a cada etapa: um rascunho parcial é permitido e não é um produto válido.

O equipamento é copiado na entrada, incluindo sua lista de proteções. No resultado há novas cópias da lista de equipamentos, de cada objeto e de suas listas aninhadas; esses níveis e a ficha são congelados em execução com Object.freeze. É uma cópia profunda destes dados declarados, sem função genérica de clonagem de objetos, recursos externos ou ciclos. O relatório é string imutável. A base usa #dados para não expor o rascunho mutável.

Limites e alternativas

Ficha válida significa que as invariantes do modelo foram atendidas. Não certifica sobrevivência, treinamento, qualidade de cordas, adequação física nem disponibilidade de estoque. As etiquetas de proteção são convenções locais. As funções recebem entradas tipadas; não são um parser ou validador de JSON arbitrário, e readonly sozinho não congela objetos.

Não há persistência, rede, consumo de equipamentos ou custo de expedição simulado. O produto não inclui mana, montaria ou habilidades sem função no problema. Dados completos podem ser passados diretamente a criarFichaDireta(), usando a mesma validação e cópia. Isso costuma ser mais simples quando não há etapas distribuídas, receitas reutilizadas ou representações distintas.

Builder custa classes adicionais, rascunho e regras explícitas de finalização. Abstract Factory escolhe uma família de produtos prontos e relacionados; Builder coordena a construção de um resultado por etapas. Não é necessário usar ambos para configurar esta ficha.

As saídas vêm da execução real e a captura não substitui auditoria técnica independente nem aprovação autoral. Uma alteração de código invalida hashes anteriores e exige nova execução e conferência dos trechos publicados.

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 { PersonagemBuilder, RelatorioBuilder } from "./builders.js";
import { DiretorDeResgate } from "./diretor.js";

const diretor = new DiretorDeResgate();
const builder = new PersonagemBuilder();
diretor.prepararTorre(builder, "Ivo");
const torre = builder.build();
diretor.prepararGaleria(builder, "Bia");
const galeria = builder.build();

console.log(`Ficha torre: ${torre.nome} | ${torre.equipamentos.map((item) => item.nome).join(", ")}`);
console.log(`Ficha galeria: ${galeria.nome} | ${galeria.equipamentos.map((item) => item.nome).join(", ")}`);
console.log(`Ficha anterior preservada: ${torre.nome} | missão ${torre.missao}`);

const relatorio = new RelatorioBuilder();
diretor.prepararTorre(relatorio, "Ivo");
console.log(`Relatório: ${relatorio.build()}`);

try {
  new PersonagemBuilder().definirIdentidade("Ivo", "resgatista")
    .definirVida(100).definirMissao("torre").build();
} catch (erro: unknown) {
  if (!(erro instanceof Error)) throw erro;
  console.log(`Ficha incompleta rejeitada: ${erro.message}`);
}
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.

DiretorDeResgate depende de EtapasDeExpedicao (mesmas etapas por receita).
03 · a etapas

Esta vista reúne EtapasDeExpedicao, DiretorDeResgate, Equipamento. DiretorDeResgate depende de EtapasDeExpedicao (mesmas etapas por receita). EtapasDeExpedicao depende de Equipamento. Membros exibidos são um recorte; construtores e demais membros podem estar omitidos. e4 · mesmas etapas por receita

ConstrutorBase realiza EtapasDeExpedicao.
03 · b builders

Esta vista reúne EtapasDeExpedicao, ConstrutorBase, PersonagemBuilder, RelatorioBuilder. ConstrutorBase realiza EtapasDeExpedicao. PersonagemBuilder herda de ConstrutorBase. RelatorioBuilder herda de ConstrutorBase. ConstrutorBase: Copia entrada. Valida ao concluir. Build não reseta; reiniciar é explícito. RelatorioBuilder: Mesma receita; resultado textual de conferência. Membros exibidos são um recorte; construtores e demais membros podem estar omitidos.

PersonagemBuilder depende de FichaDePersonagem (build devolve snapshot).
03 · c produto

Esta vista reúne PersonagemBuilder, FichaDePersonagem, Equipamento. PersonagemBuilder depende de FichaDePersonagem (build devolve snapshot). FichaDePersonagem compõe Equipamento. FichaDePersonagem: Ficha, itens e listas congelados em execução. Configuração, não pessoa. Membros exibidos são um recorte; construtores e demais membros podem estar omitidos. e5 · build devolve snapshot

04 · Faça sua escolha

Sua vez de conjurar.

exercise/README.md

Exercício — A galeria com proteção especial

Usando as receitas existentes de torre e galeria como referência, escreva uma receita prepararGaleriaComFiltro(builder, nome). Preserve a proteção contra água e acrescente um equipamento com nome Máscara com filtro e proteção fumaca, sem alterar EtapasDeExpedicao nem os builders.

Critérios:

  • A mesma receita deve atender PersonagemBuilder e RelatorioBuilder.
  • A ficha contém proteção contra água e a proteção especial; o relatório inclui a máscara.
  • Reutilizar o builder com a receita de galeria original remove a máscara, pois a receita começa com reiniciar().
  • O resultado anterior permanece independente e imutável.
  • Produza a mesma ficha usando criarFichaDireta() com um objeto de opções e compare os valores.
  • Explique quando os dados já disponíveis tornam a função direta preferível ao processo por etapas.

Compare com solution/protecao-especial.ts e seus testes. exercise/ fica fora do build oficial para não compilar a tentativa junto da resposta. A solução executável pode ser rodada com npm run solution.

Resposta comentada

A nova receita chama DiretorDeResgate.prepararGaleria() e acrescenta um equipamento antes de pedir o resultado ao builder concreto. O contrato já aceita listas de proteções; nenhuma etapa nova é necessária. Isso mostra uma extensão dentro das categorias existentes, sem alegar que qualquer nova regra de domínio dispensará mudança no contrato.

Os testes comparam a ficha com a função direta e observam a máscara no relatório. A expressão fumaca é uma etiqueta do modelo: não valida filtro, composição do ar ou resistência humana. A validação original continua exigindo somente agua na missão galeria; tornar o filtro obrigatório em uma nova missão exigiria ampliar e testar essa regra explicitamente.

A função direta precisa de menos objetos e chamadas quando todos os dados chegam juntos e não há receitas nem representações alternativas. O Builder se justifica quando a construção por etapas e sua reutilização oferecem clareza que compense o rascunho e as classes adicionais.

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/03-builder
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