Índice do grimório

Padrão 02 · Criacionais

Abstract Factory

fornecer armas e proteções de famílias compatíveis.

Começar pelo código
A quimera de escória ergue sua cabeça triangular sobre a fenda enquanto Saira sustenta as nervuras de gelo para Dena.
Cena do capítulo 02 · Abstract Factory

01 · Entenda a escolha

Os fundamentos do padrão.

README.md

02 — Abstract Factory: Kits elementais compatíveis

Duas escolas oferecem arma e proteção. O cliente recebe uma FabricaElemental, cria cada categoria separadamente, emite um pulso da arma e o submete à proteção. Brasa coopera com brasa, geada com geada; a mistura é rejeitada com erro explícito.

Ambiente e comandos

Node 26.10.0, npm 11.19.1, TypeScript 5.9.3, @types/node 26.0.0. O lock fixa as dependências. Dentro deste diretório:

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

Da raiz Books, o runner existente seleciona apenas este projeto:

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

O build limpa dist, compila com checagem estrita e executa testes Node sobre o JavaScript emitido. Não importa o projeto 01 ou qualquer capítulo vizinho. exercise/ não é compilado com a resposta.

Arquivos

  • src/contratos.ts: Abstract Factory, categorias de produtos e valores de entrada/saída.
  • src/brasa.ts e src/geada.ts: fábricas concretas e seus produtos concretos.
  • src/ensaio.ts: regras locais comuns de intensidade e compatibilidade.
  • src/cliente.ts: ensaiarKit, dependente só dos contratos.
  • src/main.ts: seleção de fábricas, duas famílias válidas e mistura rejeitada.
  • test/familias.test.ts: cooperação, substituição, erros e limites.
  • exercise/README.md: proposta e resposta comentada sobre o custo de nova categoria.
  • solution/: terceira família de vento e seus testes.
  • diagram.mmd: fonte editável das relações do projeto-base.
  • evidence/: stdout real, testes, versões e hashes.

Garantias e limites

O simulador aceita intensidade positiva finita, inclusive fracionária. A arma valida antes de criar o pulso; a proteção valida também pulsos criados externamente. Em cada ensaio, a proteção compara sua afinidade com a do pulso e devolve um resultado imutável com nomes de arma/proteção e intensidade preservada. A intensidade é uma unidade abstrata de teste, sem modelo de energia, desgaste ou resistência física.

As interfaces não usam genéricos para prender uma família no sistema de tipos: a mistura entre categorias compila e é rejeitada em execução. FabricaElemental também permite implementar uma fábrica incoerente; o teste negativo demonstra essa fronteira. A mesma etiqueta de afinidade não comprova qualidade de equipamento, funcionamento mágico real nem autenticidade de produtos maliciosos. Não é validação de JSON arbitrário. readonly é restrição estática; somente os pulsos/resultados têm Object.freeze em execução. Não há estado persistente, rede ou consumo de material escondido nos testes.

Intenção, alternativa e custo

Abstract Factory organiza famílias de produtos relacionados, e cada método pode criar somente um produto por chamada. A quantidade de objetos não define o padrão. Aqui o cliente troca uma fábrica por outra para obter arma e proteção da mesma escola. Criar o par diretamente é mais simples quando há uma única família estável.

Adicionar uma família requer seus dois produtos e a fábrica, preservando o cliente. Adicionar uma categoria exige ampliar a interface das fábricas e atualizar todas as implementações. A solução de vento demonstra o primeiro caso; o exercício explica o segundo sem introduzir equipamento desnecessário.

Factory Method varia a criação por sobrescrita num Creator com operação comum. As fábricas deste projeto implementam uma interface por composição no cliente; não há operação herdada canônica. Uma Abstract Factory pode ser implementada usando Factory Methods, mas o objetivo continua sendo a família. Builder trata da construção por etapas; este exemplo fornece produtos prontos.

Execução bem-sucedida e hashes não equivalem a auditoria técnica independente nem aprovação do autor. A renderização e inspeção visual do Mermaid pertencem à etapa própria de arte.

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 { FabricaDeBrasa } from "./brasa.js";
import { ensaiarKit } from "./cliente.js";
import { FabricaDeGeada } from "./geada.js";

// A seleção concreta fica no ponto de composição, fora do cliente.
for (const fabrica of [new FabricaDeBrasa(), new FabricaDeGeada()]) {
  const resultado = ensaiarKit(fabrica, 3);
  console.log(`${resultado.afinidade} | ${resultado.arma} + ${resultado.protecao} | intensidade ${resultado.intensidade} | compatível`);
}

const arma = new FabricaDeBrasa().criarArma();
const protecao = new FabricaDeGeada().criarProtecao();
try {
  protecao.ensaiar(arma.emitirPulso(3));
} catch (erro: unknown) {
  if (!(erro instanceof Error)) throw erro;
  console.log(`Mistura 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.

FabricaDeBrasa realiza FabricaElemental.
02 · a fábricas

Esta vista reúne FabricaElemental, FabricaDeBrasa, FabricaDeGeada. FabricaDeBrasa realiza FabricaElemental. FabricaDeGeada realiza FabricaElemental. FabricaElemental: ensaiarKit recebe esta interface; cria arma e proteção separadamente. Membros exibidos são um recorte; construtores e demais membros podem estar omitidos.

ArmaDeBrasa realiza Arma.
02 · b brasa

Esta vista reúne FabricaDeBrasa, Arma, Protecao, ArmaDeBrasa, ProtecaoDeBrasa. ArmaDeBrasa realiza Arma. ProtecaoDeBrasa realiza Protecao. FabricaDeBrasa depende de ArmaDeBrasa (cria). FabricaDeBrasa depende de ProtecaoDeBrasa (cria). Protecao: Mistura rejeitada em execução. Resultado é simulação de afinidade. Membros exibidos são um recorte; construtores e demais membros podem estar omitidos. e7 · cria e8 · cria

ArmaDeGeada realiza Arma.
02 · c geada

Esta vista reúne FabricaDeGeada, Arma, Protecao, ArmaDeGeada, ProtecaoDeGeada. ArmaDeGeada realiza Arma. ProtecaoDeGeada realiza Protecao. FabricaDeGeada depende de ArmaDeGeada (cria). FabricaDeGeada depende de ProtecaoDeGeada (cria). Membros exibidos são um recorte; construtores e demais membros podem estar omitidos. e9 · cria e10 · cria

Arma depende de Pulso (emite).
02 · d afinidade

Esta vista reúne Arma, Protecao, Pulso, ResultadoDeEnsaio. Arma depende de Pulso (emite). Protecao depende de Pulso (verifica afinidade). Protecao depende de ResultadoDeEnsaio (devolve). Membros exibidos são um recorte; construtores e demais membros podem estar omitidos. e11 · emite e12 · verifica afinidade e13 · devolve

04 · Faça sua escolha

Sua vez de conjurar.

exercise/README.md

Exercício — A terceira escola

Acrescente uma família de vento em arquivos novos, com ArmaDeVento, ProtecaoDeVento e FabricaDeVento.

Critérios verificáveis:

  • A fábrica cria arma e proteção de afinidade vento por operações individuais.
  • ensaiarKit() recebe a nova fábrica sem alteração e retorna a família correta.
  • Intensidade positiva finita é preservada; zero, negativo e não finito são rejeitados.
  • Proteção de vento rejeita um pulso de brasa ou geada.
  • As duas fábricas anteriores e o contrato FabricaElemental permanecem idênticos.

Teste sua solução e compare com solution/vento.ts e solution/vento.test.ts. exercise/ fica fora do build oficial, para não compilar sua versão e a resposta juntas.

Resposta comentada

A terceira escola adiciona implementações das categorias já existentes, sem ampliar o contrato da fábrica. afinidade é string para admitir novas famílias; por isso a compatibilidade é verificada durante o ensaio, sem promessa de rejeição estática da mistura.

Uma nova categoria, por exemplo Amuleto, é outro eixo de mudança: exige definir seu contrato, ampliar FabricaElemental com criarAmuleto(), implementar a operação nas três fábricas, criar os produtos correspondentes e acrescentar testes de interação. O cliente só precisa mudar se passar a usar o amuleto. Não basta acrescentar uma quarta fábrica.

Este exercício pede registrar esse custo, não implementar uma categoria que o caso atual não utiliza. Um construtor direto de par continua preferível quando só há uma família estável e nenhuma necessidade de variação entre famílias.

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/02-abstract-factory
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