O padrão de design Adapter (Adaptador) na forma de adaptação de objeto tem como objetivo principal permitir que duas interfaces incompatíveis trabalhem juntas.

Ele resolve o problema de incompatibilidade ao "embrulhar" uma interface existente (Adaptee) em uma nova interface (Target) que o cliente espera ou precisa. Este padrão é particularmente útil em situações como:

  1. Integração de Sistemas Externos: Quando se está trabalhando com código de terceiros ou sistemas legados cujas interfaces não são compatíveis com o sistema atual.

  2. Reutilização de Código Legado: Permite que o código antigo seja reutilizado em novos sistemas sem a necessidade de alterar o código original, simplesmente adaptando sua interface.

  3. Transição Suave Durante Refatoração: Facilita a refatoração gradual de um sistema ao permitir que partes antigas e novas do sistema se comuniquem por meio de adaptadores.

  4. Polimorfismo e Flexibilidade: Permite que objetos com interfaces diferentes sejam tratados de maneira polimórfica e podem ser substituídos por novas implementações com facilidade.

Como Funciona a Adaptação de Objeto

No padrão de adaptação de objeto, o Adapter contém uma instância do Adaptee. O

Adapter implementa a interface Target e traduz as chamadas recebidas de seus métodos para os métodos correspondentes na interface Adaptee. Essa tradução envolve chamar os métodos apropriados no objeto Adaptee e também adaptar os parâmetros e valores de retorno conforme necessário.

Diferenças entre Adaptação de Objeto e Classe

  • Adaptação de Objeto: Utiliza composição. O Adapter contém uma instância do Adaptee.
  • Adaptação de Classe: Utiliza herança múltipla (não suportada em Java). O Adapter herda tanto da interface Target quanto da classe Adaptee.

Exemplo de implementação

Vamos considerar um cenário mais realista onde o padrão Adapter (adaptação de objeto) pode ser aplicado em Java. Imagine que estamos desenvolvendo um sistema de notificações, onde precisamos integrar uma biblioteca de terceiros que oferece serviço de envio de mensagens SMS, mas a interface desta biblioteca é diferente da interface de notificações que nosso sistema utiliza. Vamos criar um Adapter para tornar a biblioteca de SMS compatível com a nossa interface de notificações.

Definindo a Interface do Sistema (Target)

public interface Notificador {
    void enviarMensagem(String mensagem);
}

Esta é a interface Notificador que o nosso sistema utiliza para enviar notificações.

Classe da Biblioteca de Terceiros (Adaptee)

public class ServicoSMS {
    public void enviarSMS(String numeroDestino, String conteudo) {
        System.out.println("Enviando SMS para " + numeroDestino + ": " + conteudo);
    }
}

ServicoSMS é a classe da biblioteca de terceiros com uma interface incompatível (enviarSMS ao invés de enviarMensagem).

Implementando o Adapter

public class SMSAdapter implements Notificador {
    private ServicoSMS servicoSMS;
    private String numeroDestino;
 
    public SMSAdapter(ServicoSMS servicoSMS, String numeroDestino) {
        this.servicoSMS = servicoSMS;
        this.numeroDestino = numeroDestino;
    }
 
    @Override
    public void enviarMensagem(String mensagem) {
        servicoSMS.enviarSMS(numeroDestino, mensagem);
    }
}

SMSAdapter implementa a interface Notificador e utiliza um objeto ServicoSMS para adaptar a chamada do método.

Utilizando o Adapter no Sistema

public class Main {
    public static void main(String[] args) {
        ServicoSMS servicoSMS = new ServicoSMS();
        Notificador notificador = new SMSAdapter(servicoSMS, "123456789");
        notificador.enviarMensagem("Olá, este é um teste de SMS!");
    }
}

No método main, criamos uma instância do ServicoSMS e a passamos para o SMSAdapter, juntamente com um número de destino. Agora, podemos usar o SMSAdapter como um Notificador em nosso sistema.