Padrão 06 · Estruturais
Adapter
adaptar um fornecedor legado ao contrato esperado, incluindo conversão de velocidade.
Começar pelo código
01 · Entenda a escolha
Os fundamentos do padrão.
06 — Adapter: Integração de dragões legados
O cliente usa DragaoNovo, com velocidade em m/s e ataque que devolve resultado. DragaoAdapter compõe DragaoAntigo, converte km/h para m/s, traduz o alvo e o resultado de fogo, sem editar a biblioteca legada. Uma implementação nativa atende ao mesmo contrato diretamente.
Executar
Node 26.10.0, npm 11.19.1, TypeScript 5.9.3, @types/node 26.0.0, lock próprio. Dentro deste diretório:
npm ci --ignore-scripts
npm test
npm start
npm run solution
Da raiz Books:
node book-23/scripts/projetos.mjs 06 install
node book-23/scripts/projetos.mjs 06 check
node book-23/scripts/projetos.mjs 06 capture
node book-23/scripts/projetos.mjs 06 verify
O build limpa dist, usa tsc local estrito para ES2022/ESM e testa o JavaScript emitido com node:test. Não importa capítulos vizinhos. Tentativas em exercise/ ficam fora da compilação da resposta.
Papéis e arquivos
src/contratos.ts: TargetDragaoNovo, pedido, resultado e erro de recurso indisponível.src/legado/dragao-antigo.ts: Adaptee, biblioteca simulada que só oferece velocidade em km/h e fogo.src/adapter.ts: Adapter por composição, com conversão numérica e delegação.src/nativo.ts: implementação nativa do Target, sem camada de tradução.src/cliente.ts:liberarRochaDoFarol(), cliente independente de fornecedor.src/validacao.ts: valores de velocidade e alvo não vazio.src/main.ts: mesmo cliente para nativo/adaptado e ordem sem equivalente recusada.test/adaptacao.test.ts: conversão, contrato comum, chamadas delegadas, erros e fidelidade do resultado.exercise/esolution/: segundo fornecedor em nós, outro adaptador e testes.diagram.mmd: fonte editável das relações do código-base; renderização e QA visual ficam na etapa própria.evidence/: stdout real, testes, manifesto e execução em cópia isolada.
Contrato do cenário novo
O arquivo legado é uma biblioteca simulada para este livro, com unidade declarada em seu contrato. Não é reprodução literal do snippet do Medium: aquele exemplo devolvia 100 sem declarar uma unidade. A convenção de km/h e o resultado observável são parte desta reescrita; não se afirma que aquele 100 originalmente significava km/h.
O adaptador recebe a biblioteca e só usa suas operações públicas. voarVelocidade() retorna km/h; o adaptador divide por 3.6. soltarFogo(alvo) retorna {rocha, desobstruida}; o adaptador traduz para {alvo, efeito: 'fogo', rochaLiberada}. O campo booleano é preservado, inclusive quando o fornecedor relata false. O resultado é copiado e congelado, sem manter alias mutável do retorno legado.
DragaoNovo.atacar() permite pedir fogo ou gelo, com possibilidade contratual de lançar RecursoSemEquivalente quando o pacto não oferecer aquele efeito. Os dois fornecedores-base oferecem fogo e recusam gelo; não se inventa um equivalente. A recusa acontece antes de delegar a operação. Alvos vazios são rejeitados antes de chamar o legado. Exceções do fornecedor são propagadas.
liberarRochaDoFarol() consulta a velocidade e solicita fogo sobre um identificador de rocha. Retorna dados locais do ensaio. Não transporta pessoas, não movimenta o contrapeso, não modela cabos, massa, forças ou dano térmico e não confirma que um farol físico foi liberado. A narrativa contém o resgate e o pacto; o código demonstra a tradução do contrato disponível. rochaLiberada é o resultado declarado pelo simulador, sem sensores ou comprovação física.
Velocidade negativa ou não finita é inválida; zero é aceito como valor numérico e não prova capacidade de transporte. Entradas são tipadas; o programa não valida JSON arbitrário nem decide se qualquer objeto nomeado é uma rocha. private nos parâmetros de construtor é checagem TypeScript, não barreira de segurança em execução.
Números, teste e alternativa
O caso 36 km/h → 10 m/s permite igualdade numérica no exemplo executado. Casos fracionários são testados com tolerância absoluta 1e-12 m/s nos valores pequenos usados. A solução converte nós pelo fator 1852 / 3600 e só arredonda a apresentação com toFixed(3). Não se afirma que todos os números flutuantes tenham equivalência decimal exata, nem se escolhe tolerância para medições reais.
Adapter acrescenta uma camada e regras de tradução que precisam acompanhar os contratos. Se você controla as duas APIs e pode mudar uma delas de modo compatível com seus consumidores, uma alteração direta pode eliminar a necessidade da camada. Uma função que devolve um objeto com velocidadeMps e atacar também pode ser um adaptador; a classe aqui explicita composição e papéis. Os testes usam fornecedores substitutos para observar dados e delegações sem modificar o legado.
Facade oferece entrada conveniente para subsistemas; Adapter atende a um contrato já esperado por um cliente. Bridge separa dimensões de variação planejadas para evoluir; Adapter reconcilia interfaces existentes. A presença de composição ou de uma classe chamada ponte não define essas intenções por si.
Referência e rastreabilidade
O fator do exercício é conferido no NIST Guide to the SI — unidades fora do SI: um nó corresponde a uma milha náutica por hora, com 1852 / 3600 m/s. Consulta em 22/10/2026. A conversão é verificada também pela execução dos testes no runtime fixado.
Saídas e hashes vinculam a execução às fontes. Não representam auditoria técnica independente, aprovação do autor, benchmark ou garantia de integração com uma biblioteca externa real.
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 { DragaoAdapter } from "./adapter.js";
import { liberarRochaDoFarol } from "./cliente.js";
import { DragaoAntigo } from "./legado/dragao-antigo.js";
import { DragaoNativo } from "./nativo.js";
for (const [nome, dragao] of [
["Nativo", new DragaoNativo()],
["Adaptado", new DragaoAdapter(new DragaoAntigo())],
] as const) {
const ensaio = liberarRochaDoFarol(dragao, "Trava do farol");
console.log(`${nome}: ${ensaio.velocidadeMps.toFixed(3)} m/s | ${ensaio.resultado.alvo} | liberada ${ensaio.resultado.rochaLiberada}`);
}
try {
new DragaoAdapter(new DragaoAntigo()).atacar({ alvo: "Trava do farol", efeito: "gelo" });
} catch (erro: unknown) {
if (!(erro instanceof Error)) throw erro;
console.log(`Ordem recusada: ${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.
DragaoNovo oferece velocidadeMps(): number e atacar(pedido: PedidoDeAtaque): ResultadoDeAtaque. DragaoNativo atende ao contrato diretamente e conserva metrosPorSegundo como campo privado somente de leitura. DragaoAdapter atende ao mesmo contrato; seus membros selecionados aparecem na vista de adaptação ao legado. A relação de realização usa linha tracejada e triângulo vazio. Membros exibidos são um recorte; construtores e demais membros podem estar omitidos. DragaoNovo: liberarRochaDoFarol recebe este contrato e solicita fogo. Efeito indisponível lança RecursoSemEquivalente.
DragaoAdapter guarda antigo: DragaoAntigo como referência privada somente de leitura. A seta é associação direcionada; não declara ownership ou composição UML. velocidadeMps divide o valor em km/h por 3,6. atacar traduz o pedido e o objeto devolvido por soltarFogo(alvo: string), com rocha: string e desobstruida: boolean. O legado é uma biblioteca simulada e permanece inalterado. Membros exibidos são um recorte; construtores e demais membros podem estar omitidos. DragaoAdapter: Divide km/h por 3.6; traduz desobstruida para rochaLiberada. DragaoAntigo: Biblioteca simulada: km/h e fogo. Adapter não altera o legado. e3 · referência
PedidoDeAtaque declara alvo: string e efeito: "fogo" | "gelo". ResultadoDeAtaque conserva esses campos e acrescenta rochaLiberada: boolean. Os atributos dos dois contratos são somente de leitura. As setas tracejadas são dependências da interface DragaoNovo; o booleano é o resultado declarado pelo simulador, sem comprovação física. Membros exibidos são um recorte; construtores e demais membros podem estar omitidos.
04 · Faça sua escolha
Sua vez de conjurar.
Exercício — O fornecedor marítimo
Integre um segundo fornecedor que informa velocidade em nós e expõe aquecerRocha(identificador) em vez de soltarFogo(alvo). Ele também oferece somente fogo.
Critérios verificáveis:
- Um novo adaptador por composição atende
DragaoNovo;liberarRochaDoFarol()não muda. - 10 nós produzem aproximadamente
5.144444444444444m/s; compare com tolerância, não como igualdade decimal exata geral. - O fator usado é
1852 / 3600, sem arredondamento antecipado. - O alvo chega ao fornecedor normalizado e a indicação de liberação é preservada, inclusive quando for
false. - Pedido de gelo é recusado antes de acionar o fornecedor.
- Velocidade não finita é diagnosticada.
- Nenhuma edição é necessária na biblioteca antiga ou no fornecedor marítimo.
Compare com solution/fornecedor-maritimo.ts, solution/adapter-maritimo.ts e solution/maritimo.test.ts. Execute npm run solution. exercise/ fica fora do build oficial para não compilar a tentativa junto da resposta.
Resposta comentada
O adaptador converte os dados e a chamada, não apenas os nomes: nós viram m/s, identificador vira alvo e liberada vira rochaLiberada. O erro do efeito sem equivalente mantém o limite do pacto. O cliente não precisa saber de qual fornecedor veio o resultado.
O programa formata a velocidade com três casas decimais só na saída humana. O teste mantém o resultado numérico e usa tolerância absoluta de 1e-12 m/s para esse caso pequeno. Isso não declara uma tolerância física universal nem valida velocidade de um animal real.
Se ambos os contratos forem controlados pela mesma equipe, alterar a API diretamente pode ser mais claro que manter duas superfícies e um adaptador. Uma função ou um objeto literal também poderia cumprir DragaoNovo; uma classe não é exigência do mecanismo de adaptação por composição.
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/06-adapter npm ci --ignore-scripts --no-audit --no-fund npm test npm start # Depois da sua tentativa npm run solutionBaixar este projeto completo