Padrão 02 · Criacionais
Abstract Factory
fornecer armas e proteções de famílias compatíveis.
Começar pelo código
01 · Entenda a escolha
Os fundamentos do padrão.
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.tsesrc/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.
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}`);
}
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.
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.
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
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
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.
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
ventopor 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
FabricaElementalpermanecem 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 solutionBaixar este projeto completo