GoF, p. 139 também chamado de Wrapper
Adaptador
Adapter
Traduz a interface de uma classe para outra que o cliente já espera.
Converter a interface de uma classe em outra interface que os clientes esperam. Adapter permite que classes trabalhem juntas quando isso seria impossível por incompatibilidade de interfaces.
O adaptador de tomada
Seu notebook tem plugue americano; a tomada do hotel é europeia. Ninguém reprojeta o notebook nem a parede: você compra um adaptador. Ele não gera energia nem muda a voltagem — apenas traduz um formato de conexão para outro.
Você tem uma classe útil (uma biblioteca de terceiros, um sistema legado) cuja interface não bate com a que seu código usa. Você não pode alterá-la — é de terceiros, ou é usada por outros mil lugares.
Crie uma classe intermediária que implementa a interface esperada pelo cliente e, por dentro, delega para o objeto incompatível, convertendo dados e chamadas nos dois sentidos.
- Conversões de formato repetidas em vários pontos do código.
- Detalhes de uma lib de terceiros vazando para dentro do domínio.
Estrutura
- Target
- A interface que o cliente já usa e entende.
- Adaptee
- A classe existente e incompatível que se quer aproveitar.
- Adapter
- Implementa Target e traduz cada chamada para o Adaptee.
- Client
- Programa contra Target; não sabe que existe um Adaptee.
Implementação
// ── Target: a interface que TODO o seu app já usa ─────
class ProvedorDePagamento {
async cobrar(valorEmCentavos, cartao) { throw new Error('abstrato'); }
}
// ── Adaptee: a lib do gateway, com outra assinatura ───
class GatewayLegado {
// Trabalha com reais em string, campos com outros nomes e callback.
executeTransaction(payload, callback) {
const ok = payload.card_number?.length === 16;
callback(ok ? null : new Error('CARD_INVALID'), { transaction_id: 'tx_918', status: ok ? 'OK' : 'FAIL' });
}
}
// ── Adapter: implementa Target, fala com o Adaptee ────
class AdaptadorGatewayLegado extends ProvedorDePagamento {
constructor(legado = new GatewayLegado()) { super(); this.legado = legado; }
cobrar(valorEmCentavos, cartao) {
// 1. traduz os DADOS de entrada
const payload = {
amount_brl: (valorEmCentavos / 100).toFixed(2),
card_number: cartao.numero,
exp: `${cartao.mes}/${cartao.ano}`,
};
// 2. traduz o PROTOCOLO (callback → Promise)
return new Promise((resolve, reject) => {
this.legado.executeTransaction(payload, (erro, resposta) => {
if (erro) return reject(new Error(`Pagamento recusado: ${erro.message}`));
// 3. traduz os DADOS de saída
resolve({ id: resposta.transaction_id, aprovado: resposta.status === 'OK' });
});
});
}
}
// O cliente não faz ideia de que existe um sistema legado ali dentro.
async function checkout(provedor) {
const r = await provedor.cobrar(19900, { numero: '4111111111111111', mes: '12', ano: '30' });
console.log(r.aprovado ? `✅ pago (${r.id})` : '❌ recusado');
}
checkout(new AdaptadorGatewayLegado()); - java.io.InputStreamReader (InputStream → Reader)
- java.util.Arrays.asList()
- Ponte entre APIs REST e GraphQL num BFF
Consequências
- Separa a conversão de interface da lógica de negócio (Responsabilidade Única).
- Permite introduzir novos adaptadores sem tocar no cliente (Aberto/Fechado).
- Viabiliza reutilizar código que, de outra forma, seria incompatível.
- Aumenta a complexidade: mais uma camada de indireção.
- Às vezes é mais simples ajustar a própria classe de serviço — quando ela é sua.
- Você quer usar uma classe existente cuja interface não bate com a sua.
- Você precisa reutilizar várias subclasses que não têm uma funcionalidade comum e não pode alterá-las.
- Você está isolando seu domínio de bibliotecas externas (a camada anticorrupção do DDD).
- Você controla as duas pontas e pode simplesmente alinhar as interfaces.
Relações com outros padrões
- Bridge
- Bridge é projetada ANTES, para deixar as partes evoluírem; Adapter é aplicada DEPOIS, para consertar incompatibilidade.
- Decorator
- Decorator mantém a interface e adiciona comportamento; Adapter muda a interface.
- Proxy
- Proxy mantém a MESMA interface e controla o acesso; Adapter converte.
- Facade
- Facade cria uma interface NOVA e simplificada para um subsistema; Adapter reusa uma interface existente.
Verificação
Adapter, Decorator e Proxy envolvem outro objeto. O que distingue o Adapter?
- O Adapter MUDA a interface; Decorator e Proxy preservam a interface original
- O Adapter é o único que usa herança
- O Adapter é o único que pode ser aplicado em tempo de execução
- O Adapter é o único que pode envolver mais de um objeto
Decorator preserva a interface e acrescenta comportamento; Proxy preserva a interface e controla o acesso; Adapter existe justamente para trocar a interface.