Padrão Adapter
Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy — lição 71 de 87.
O Adapter Pattern é um padrão de projeto estrutural que permite que interfaces incompatíveis trabalhem juntas. Ele atua como uma ponte entre duas classes, convertendo a interface de uma classe em uma interface que o cliente espera. Pense nele como um adaptador de tomada que permite conectar um dispositivo europeu em uma tomada americana.
Imagine que você tem uma interface MediaPlayer existente que reproduz arquivos de áudio, mas você precisa integrar uma biblioteca de terceiros que usa uma interface completamente diferente para reproduzir vídeos:
// Interface existente que seu código usa
interface MediaPlayer {
void play(String filename);
}
// Classe de terceiros com interface incompatível
class AdvancedVideoPlayer {
public void playVideo(String filename) {
System.out.println("Playing video: " + filename);
}
}O adaptador envolve a classe incompatível e implementa a interface esperada, traduzindo as chamadas entre elas:
class VideoPlayerAdapter implements MediaPlayer {
private AdvancedVideoPlayer videoPlayer;
public VideoPlayerAdapter() {
this.videoPlayer = new AdvancedVideoPlayer();
}
public void play(String filename) {
videoPlayer.playVideo(filename);
}
}Agora o seu código cliente pode usar o reprodutor de vídeo através da familiar interface MediaPlayer:
MediaPlayer player = new VideoPlayerAdapter();
player.play("movie.mp4"); // Saída: Reproduzindo vídeo: movie.mp4O Padrão Adapter é inestimável ao integrar código legado ou bibliotecas de terceiros sem modificar seu código-fonte. O cliente não percebe que está trabalhando com uma classe adaptada — ele simplesmente usa a interface que conhece.
Desafio
FácilVamos construir um sistema de conversão de temperatura usando o Padrão Adapter! Você tem uma aplicação meteorológica existente que trabalha com temperaturas em Celsius, mas precisa integrar um sensor de temperatura legado que apenas fornece leituras em Fahrenheit. Em vez de modificar qualquer um dos sistemas, você criará um adaptador que preenche essa lacuna.
Você organizará seu código em quatro arquivos:
TemperatureProvider.java: Defina a interface que sua aplicação meteorológica espera. Ela deve declarar um métodogetTemperatureCelsius()que retorna um double representando a temperatura em Celsius.FahrenheitSensor.java: Isso representa o sensor legado com uma interface incompatível. Crie uma classe com um campo double privado para a temperatura em Fahrenheit, definido através do construtor. Ela deve ter um métodoreadFahrenheit()que retorna o valor em Fahrenheit armazenado.SensorAdapter.java: Crie o adaptador que torna o sensor Fahrenheit compatível com sua aplicação meteorológica. Seu adaptador deve implementarTemperatureProvidere envolver umFahrenheitSensor. QuandogetTemperatureCelsius()for chamado, ele deve ler do sensor e converter o valor para Celsius usando a fórmula:(fahrenheit - 32) * 5 / 9.Main.java: Reúna tudo! Você receberá uma entrada: uma leitura de temperatura em Fahrenheit (double).Crie um
FahrenheitSensorcom o valor de entrada. Em seguida, crie umSensorAdapterque envolva este sensor. Armazene o adaptador em uma variável do tipoTemperatureProviderpara demonstrar que sua aplicação meteorológica pode usá-lo através da interface esperada.Imprima o resultado neste formato:
"Temperature: " + String.format("%.1f", celsius) + " C"onde celsius é formatado com uma casa decimal.
Você receberá uma entrada: a temperatura em Fahrenheit (double).
Por exemplo, com a entrada 98.6, sua saída seria:
Temperature: 37.0 CObserve como sua classe Main trabalha inteiramente com a interface TemperatureProvider — ela não tem ideia de que a temperatura real está vindo de um sensor Fahrenheit sendo adaptado nos bastidores. Esta é a elegância do Padrão Adapter!
Folha de consulta
O Adapter Pattern (Padrão Adaptador) é um padrão de design estrutural que permite que interfaces incompatíveis trabalhem juntas, agindo como uma ponte entre duas classes, convertendo uma interface em outra que o cliente espera.
Componentes Chave
- Target Interface (Interface Alvo): A interface que o cliente espera usar
- Adaptee (Adaptado): A classe existente com uma interface incompatível
- Adapter (Adaptador): A classe que implementa a interface alvo e envolve o adaptado, traduzindo as chamadas entre eles
Estrutura Básica
// Target interface
interface MediaPlayer {
void play(String filename);
}
// Adaptee (incompatible class)
class AdvancedVideoPlayer {
public void playVideo(String filename) {
System.out.println("Playing video: " + filename);
}
}
// Adapter
class VideoPlayerAdapter implements MediaPlayer {
private AdvancedVideoPlayer videoPlayer;
public VideoPlayerAdapter() {
this.videoPlayer = new AdvancedVideoPlayer();
}
public void play(String filename) {
videoPlayer.playVideo(filename);
}
}
// Client usage
MediaPlayer player = new VideoPlayerAdapter();
player.play("movie.mp4"); // Output: Playing video: movie.mp4Quando Usar
O Adapter Pattern é útil quando:
- Integrar código legado ou bibliotecas de terceiros sem modificar seu código-fonte
- Fazer com que interfaces incompatíveis trabalhem juntas
- O cliente precisa permanecer alheio à implementação adaptada
Experimente você mesmo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
double fahrenheit = scanner.nextDouble();
// TODO: Crie um FahrenheitSensor com o valor de entrada
// TODO: Crie um SensorAdapter que envolva o sensor
// TODO: Armazene o adaptador em uma variável TemperatureProvider
// TODO: Obtenha a temperatura em Celsius e a imprima
// Formato: "Temperature: [celsius] C" com uma casa decimal
// Dica: Use String.format("%.1f", value) para formatação
}
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos ExternosIntrodução à POOClasses vs ObjetosA Palavra-chave thisMétodosCampos (Atributos)Método ConstrutorSobrecarga de ConstrutorRecapitulação - Calculadora Simples4Herança
Herança Básica (extends)A Palavra-chave superSobrescrita de Método (@Override)Encadeamento de ConstrutoresA Classe ObjectHerança Simples e MultinívelPor que não há Herança Múltipla de ClassesRecapitulação - Hierarquia de Funcionários7Métodos Especiais e a Classe Object
Método toString()equals() e hashCode()Método clone()compareTo() e ComparableInterface ComparatorRecapitulação - Ordenação Personalizada2Modificadores de Acesso e Encapsulamento
Visão Geral dos Níveis de AcessoMétodos Getter e SetterOcultação de InformaçõesA Palavra-chave finalRecapitulação - Gerenciador de Conta Bancária5Polimorfismo
Fundamentos de Sobrecarga de MétodosSobrescrita de Métodos (Tempo de Execução)Upcasting e DowncastingO Operador instanceofClasses e Métodos AbstratosRecapitulação - Calculadora de Formas8Conceitos Avançados de OOP
Composição vs HerançaAgregação vs ComposiçãoClasses Internas, Aninhadas e AnônimasEnums e Métodos de EnumRecords (Java 16+)Classes Seladas (Java 17+)11Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão BuilderPadrão ObserverPadrão Strategy3Propriedades de Classe e Membros Estáticos
Variáveis de Instância vs EstáticasMétodos EstáticosBlocos EstáticosConstantes (static final)Recapitulação - Contador e Utilitário6Interfaces e Classes Abstratas
Introdução às InterfacesImplementando InterfacesImplementação de Múltiplas InterfacesDefault e Static em InterfacesClasses Abstratas vs InterfacesInterfaces FuncionaisRecapitulação - Sistema de Pagamento9Generics
Introdução a GenericsClasses GenéricasMétodos GenéricosParâmetros de Tipos DelimitadosWildcards (?, extends, super)Recapitulação - Container Genérico12Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão Iterator