Design Patterns no Dia a Dia: Adapter
Data de publicação

Design Patterns no Dia a Dia: Adapter
Tem uma situação que provavelmente você já viveu: você está no meio de uma feature, precisa integrar uma biblioteca externa ou um serviço legado, e as interfaces simplesmente não conversam. O contrato que o seu código espera é diferente do que aquele componente entrega.
Durante um tempo eu resolvia isso na força bruta — jogava conversões no meio do código de negócio, criava funções utilitárias espalhadas, ou pior, modificava o componente legado e torcia para nada quebrar. Funcionava. Mas sempre voltava para me assombrar.
Foi mergulhando em design patterns que entendi que esse problema tem nome, tem solução e tem estrutura. O padrão chama Adapter.
O que é o Adapter
O Adapter é um padrão estrutural que permite que objetos com interfaces incompatíveis trabalhem juntos. Ele age como um intermediário: traduz a interface que o cliente conhece para a interface que o serviço oferece.
A analogia mais direta: o adaptador de tomada. Sua fonte de notebook tem um plug americano, a parede tem uma tomada europeia. Você não refaz a instalação elétrica do prédio nem compra um notebook novo — você coloca um adaptador no meio.
Quando aplicar
- Você precisa usar uma classe existente (legada ou de terceiros) mas a interface dela não bate com o que seu código espera.
- Você quer reaproveitar subclasses existentes sem duplicar código, adicionando funcionalidade via composição.
- A interface do serviço externo pode mudar — e você quer isolar esse impacto num único ponto.
O sinal de alerta mais claro: você está escrevendo conversões de dados ou chamadas de adaptação espalhadas por vários lugares. Esse código deveria estar centralizado num Adapter.
Estrutura
O padrão tem quatro participantes principais:
- Client — o código que usa a funcionalidade. Só conhece a interface Target.
- Target — a interface que o Client espera. Em Go, será uma interface com o método desejado.
- Adaptee — a classe/struct existente com a interface incompatível. Pode ser legada ou de terceiros.
- Adapter — implementa Target e internamente usa o Adaptee. É aqui que a tradução acontece.
Exemplo em Go
Imagine que seu sistema usa um Logger com a interface:
type Logger interface { Log(message string)}Mas você precisa integrar uma biblioteca de observabilidade legada que expõe:
type LegacyLogger struct{}
func (l *LegacyLogger) WriteLog(level, msg string) { fmt.Printf("[%s] %s\n", level, msg)}O Client espera Log(message string). O LegacyLogger oferece WriteLog(level, msg string). Interfaces incompatíveis.
O Adapter resolve isso:
// Adapter: implementa Logger, usa LegacyLogger internamentetype LegacyLoggerAdapter struct { legacy *LegacyLogger}
func NewLegacyLoggerAdapter(l *LegacyLogger) *LegacyLoggerAdapter { return &LegacyLoggerAdapter{legacy: l}}
func (a *LegacyLoggerAdapter) Log(message string) { a.legacy.WriteLog("INFO", message)}Uso no Client:
func main() { legacy := &LegacyLogger{} logger := NewLegacyLoggerAdapter(legacy)
// O client só conhece a interface Logger logger.Log("Sistema iniciado")}O Client não sabe nada do LegacyLogger. Se futuramente a biblioteca mudar, você ajusta apenas o Adapter.
⚠️ Ponto de atenção:
- Aumenta o número de classes/structs. Se a interface do serviço for simples e estável, talvez uma conversão direta seja suficiente. Não force o padrão onde ele não agrega.
Adapter vs padrões próximos
É comum confundir o Adapter com outros padrões estruturais:
- Facade também simplifica acesso a código complexo, mas ele define uma interface nova para um subsistema inteiro. O Adapter faz uma interface existente funcionar onde outra é esperada — ele não define, ele traduz.
- Decorator mantém ou estende a interface original. O Adapter substitui uma interface por outra.
- Proxy mantém a mesma interface e adiciona comportamento (cache, controle de acesso). O Adapter muda a interface.
Exercício intermediário
Contexto: Você está construindo um sistema de pagamentos. Seu código de checkout usa a seguinte interface:
type PaymentGateway interface { Charge(amount float64, currency string) error}Uma nova parceira de pagamentos foi contratada e a SDK dela expõe:
Sua tarefa:
- Implemente um
ExternalPaymentAdapterque satisfaça a interfacePaymentGateway. - O adapter deve converter
float64(reais/dólares) paraint(centavos) antes de repassar ao SDK. - Se o SDK retornar
false, o adapter deve retornar umerrorcom a mensagem recebida. - Escreva um
mainque usa o adapter via interfacePaymentGatewaypara cobrar R$ 49,90 em BRL.
.
.
.
package main
import ( "errors" "fmt" "math")
// Interface esperada pelo sistematype PaymentGateway interface { Charge(amount float64, currency string) error}
// SDK externo — não podemos alterartype ExternalPaymentSDK struct{}
func (e *ExternalPaymentSDK) ProcessPayment(cents int, currencyCode string) (bool, string) { if cents <= 0 { return false, "invalid amount" } return true, "txn_" + currencyCode}
// Adaptertype ExternalPaymentAdapter struct { sdk *ExternalPaymentSDK}
func NewExternalPaymentAdapter(sdk *ExternalPaymentSDK) *ExternalPaymentAdapter { return &ExternalPaymentAdapter{sdk: sdk}}
func (a *ExternalPaymentAdapter) Charge(amount float64, currency string) error { // Converte float64 para centavos (int) cents := int(math.Round(amount * 100))
success, msg := a.sdk.ProcessPayment(cents, currency) if !success { return errors.New(msg) }
fmt.Printf("Pagamento aprovado: %s (txn: %s)\n", currency, msg) return nil}
// Client — só conhece PaymentGatewayfunc checkout(gateway PaymentGateway, amount float64, currency string) { err := gateway.Charge(amount, currency) if err != nil { fmt.Printf("Falha no pagamento: %v\n", err) return } fmt.Println("Checkout concluído com sucesso.")}
func main() { sdk := &ExternalPaymentSDK{} gateway := NewExternalPaymentAdapter(sdk)
checkout(gateway, 49.90, "BRL")}O ponto central: checkout não sabe que existe um ExternalPaymentSDK. Se a parceira mudar a SDK, você atualiza só o ExternalPaymentAdapter. O resto do sistema não precisa ser alterado.
Conclusão
Design patterns não são receitas para seguir cegamente. São vocabulário para nomear problemas que você já encontrou e ferramentas para resolvê-los com clareza. O Adapter foi um dos primeiros que me fez pensar: "ah, então é isso que eu estava tentando fazer nas últimas semanas".