Padrão Adaptador: Estrutura para Reconciliação de Interfaces

Visão Geral do Padrão Adaptador

O Padrão Adaptador é um padrão de design estrutural que facilita a colaboração entre objetos com interfaces incompatíveis. Ele encapsula a adaptação necessária, permitindo que cmoponentes distintos funcionem em conjunto sem modificar suas estruturas originais. Um exemplo prático é um leitor de cartões que atua como intermediário entre um cartão de memória e um notebook, traduzindo as requisições entre os dois dispositivos.

Este padrão é frequentemente denominado Wrapper, pois envolve a classe-alvo em uma camada adicional. Ele resolve o problema de utilizar classes existentes cujas enterfaces não se alinham com as necessidades do sistema, sem a possibilidade de alterar o código legado.

Implementação Básica do Padrão Adaptador

Considere a definição de uma interface alvo:

public interface Alvo {
    void executar();
}

Uma classe concreta que implementa esta interface:

public class AlvoConcreto implements Alvo {
    @Override
    public void executar() {
        System.out.println("Execução da classe AlvoConcreto");
    }
}

A classe que precisa ser adaptada, com sua própria interface:

public interface Adaptavel {
    void operacaoEspecifica();
}

A implementação concreta da classe a ser adaptada:

public class AdaptavelConcreto implements Adaptavel {
    @Override
    public void operacaoEspecifica() {
        System.out.println("Operação específica da classe AdaptavelConcreto");
    }
}

O adaptador que integra as duas interfaces:

public class Adaptador implements Alvo {
    private final Adaptavel componente;

    public Adaptador(Adaptavel componente) {
        this.componente = componente;
    }

    @Override
    public void executar() {
        System.out.println("Delegando ao adaptador");
        componente.operacaoEspecifica();
    }
}

Utilização no código cliente:

public class Cliente {
    public static void main(String[] args) {
        Adaptavel original = new AdaptavelConcreto();
        Alvo adaptado = new Adaptador(original);
        adaptado.executar();
    }
}

Saída esperada:

Delegando ao adaptador
Operação específica da classe AdaptavelConcreto

Considerações de Uso

  • O padrão é aplicável quando interfaces existentes não podem ser alteradas, sendo uma solução de refatoração em sistemas em operação.
  • Em linguagens com herança única, como Java, a adaptação por herança pode limitar a flexibilidade, exigindo que a classe-alvo seja uma interface.
  • Métodos da classe adaptada podem ser expostos, aumentando a complexidade de uso.
  • O uso excessivo pode tornar o sistema mais difícil de manter, sendo crucial uma análise de custo-benefício.
  • A conversão adequada de parâmetros e o direcionamento de chamadas são essenciais para a robustez do adaptador.

Aplicações Práticas

  • Integração de bibliotecas de terceiros com interfaces incompatíveis, evitando modiifcações no código externo.
  • Desenvolvimento de módulos genéricos que processam diversas fontes de dados, como bancos de dados ou arquivos, através de adaptadores específicos.
  • Unificação de interfaces de saída em sistemas com tipos de entrada variáveis, garantindo uma API consistente.
  • Inserção de uma camada de abstração para controlar o acesso a classes complexas, simplificando a interação do cliente.

Variantes de Implementação

Adaptação por Herança

Esta abordagem utiliza herança para sobrescrever métodos, adequada para cenários onde a classe base pode ser modificada:

public class AdaptavelBase {
    public void operacaoEspecifica() {
        System.out.println("Método da classe base");
    }
}

public class AdaptadorHeranca extends AdaptavelBase {
    @Override
    public void executar() {
        operacaoEspecifica();
    }
}

Adaptação por Composição

Baseada em composição, promove maior flexibilidade e desacoplamento:

public interface Destino {
    void acao();
}

public class Fonte {
    public void acaoOriginal() {
        System.out.println("Ação da fonte");
    }
}

public class AdaptadorComposicao implements Destino {
    private final Fonte fonte;

    public AdaptadorComposicao(Fonte fonte) {
        this.fonte = fonte;
    }

    @Override
    public void acao() {
        fonte.acaoOriginal();
    }
}

Adaptação com Decorador

Combina adaptação e funcionalidades adicionais, estendendo o comportamento da classe adaptada:

public interface Componente {
    void processar();
}

public class ComponenteConcreto implements Componente {
    @Override
    public void processar() {
        System.out.println("Processamento padrão");
    }
}

public class AdaptadorDecorador implements Componente {
    private final Componente componente;

    public AdaptadorDecorador(Componente componente) {
        this.componente = componente;
    }

    @Override
    public void processar() {
        System.out.println("Pré-processamento");
        componente.processar();
        System.out.println("Pós-processamento");
    }
}

Adaptação via Proxy

Atua como intermediário para controle de acesso, mantendo a mesma interface:

public interface Servico {
    void executarServico();
}

public class ServicoReal implements Servico {
    @Override
    public void executarServico() {
        System.out.println("Executando serviço real");
    }
}

public class AdaptadorProxy implements Servico {
    private final Servico servico;

    public AdaptadorProxy(Servico servico) {
        this.servico = servico;
    }

    @Override
    public void executarServico() {
        System.out.println("Verificando permissões");
        servico.executarServico();
    }
}

Tags: padrões-de-design padrão-adaptador java engenharia-de-software interfaces-incompatíveis

Publicado em 5-31 18:59 por Thomas