Atividade 07: Adapter
Exercício 1: Aplicações
Seção intitulada “Exercício 1: Aplicações”Para cada cenário abaixo, indique se o padrão Adapter é apropriado ou não e justifique em 2–3 frases.
- Um módulo de previsão do tempo que precisa consultar dois fornecedores externos (SDKs de terceiros) com APIs diferentes e não modificáveis, enquanto o restante da aplicação deve usar uma única interface de consulta.
- Uma aplicação que hoje usa uma biblioteca legada de envio de e-mail e pretende trocá-la por outra, sem alterar os serviços que já fazem os envios.
- Um time que decide “preparar o terreno” e já cria adaptadores para todas as classes do sistema, mesmo sem existir hoje nenhuma interface incompatível real.
- Um
Checkoutque precisa integrar um SDK de pagamento de terceiros para processar os pedidos.
Para cada item, responda:
- “Faz sentido usar Adapter” ou “Não faz sentido usar Adapter”.
- Explique rapidamente o porquê (interface incompatível, código de terceiros, acoplamento, overengineering, etc.).
Exercício 2: Analogia
Seção intitulada “Exercício 2: Analogia”Crie uma analogia própria para explicar o padrão Adapter para alguém que não é da área de TI.
Na aula, usamos a tomada em outro país: o laptop usa um plugue brasileiro, mas a tomada é europeia, e o adaptador encaixa os dois lados sem que ninguém precise “mudar o laptop” nem “mudar a tomada”. Agora crie uma outra analogia.
Descreva uma situação do mundo real em que:
- dois “lados” tenham formatos/padrões diferentes,
- um intermediário faça os dois funcionarem juntos sem alterar nenhum dos lados,
- e trocar um dos lados não exija mudanças no outro.
Exercício 3: Anti-pattern
Seção intitulada “Exercício 3: Anti-pattern”Considere o código Java abaixo, usado por um módulo de clima da aplicação:
public class PainelClima {
public void exibirTemperatura(String cidade, String fornecedor) { if (fornecedor.equals("accuweather")) { AccuWeatherApi api = new AccuWeatherApi(); int f = api.getTemperature(cidade); // retorna °F int c = (f - 32) * 5 / 9; // conversão manual System.out.println("Temperatura: " + c + "°C"); } else if (fornecedor.equals("openweather")) { OpenWeatherApi api = new OpenWeatherApi(); double c = api.getTempCelsius(cidade); // outra assinatura System.out.println("Temperatura: " + c + "°C"); } }}Além desse painel, outros dois módulos da aplicação fazem a mesma consulta repetindo o mesmo código de conversão.
Responda:
- Por que essa forma de chamar os fornecedores diretamente é um problema de design?
- Que tipo de bugs ou dificuldades aparecem quando um SDK de terceiros muda (por exemplo, passa a retornar °F) ou quando um novo fornecedor precisa ser adicionado?
- Proponha uma solução usando o padrão Adapter, explicando em linhas gerais: a interface do cliente, o(s) adaptador(es) por fornecedor e como o
PainelClimapassaria a depender apenas da interface.
Exercício 4: Exemplo real
Seção intitulada “Exercício 4: Exemplo real”Acesse o seguinte arquivo em um projeto open source:
- Projeto: OpenJDK
- Arquivo:
InputStreamReader.java
Em Java, um InputStream trabalha com bytes, enquanto um Reader trabalha com caracteres. Para ler texto de uma fonte que só fornece bytes (arquivo, socket, etc.), o JDK oferece o InputStreamReader: uma classe que estende Reader (a interface que o cliente usa) e encapsula um InputStream (recebido no construtor, junto com um charset), convertendo os bytes em caracteres. É exatamente a estrutura do adaptador de objeto vista em aula.
Responda:
- Quais são as duas “interfaces” incompatíveis que o
InputStreamReaderliga? O que a classe estende (qual o tipo dela) e o que ela recebe no construtor? - Observe os métodos
read(),ready()eclose(): para onde eles delegam? Por que o código cliente que lê texto enxerga apenas umReadere nunca precisa conhecer oInputStreampor baixo? - Onde acontece a conversão de bytes → caracteres (dica: observe o campo
StreamDecoder sd)? Que variação do padrão Adapter essa classe representa e por que ela funciona com qualquerInputStream(arquivo, socket, memória)?
Exercício 5: Implementação
Seção intitulada “Exercício 5: Implementação”Imagine que você foi contratado para integrar um módulo de previsão do tempo a dois sistemas externos diferentes.
A sua aplicação precisa consultar a temperatura em graus Celsius e não deve depender das classes dos fornecedores. Os fornecedores (SDKs de terceiros, que não podem ser alterados) são:
OpenWeatherApi— expõepublic double temperatura(String cidade)(retorna °C comodouble).AccuWeatherApi— expõepublic int getTemperature(String cidade)(retorna °F comoint).
Sua missão
Seção intitulada “Sua missão”Implemente, em Java, um sistema que adapte os dois fornecedores a uma interface comum, usando o padrão Adapter.
- Crie a interface do cliente
PrevisaoService:- Contrato:
int obterTemperatura(String cidade)— sempre em °C.
- Contrato:
- Crie as classes dos fornecedores externos (como se fossem SDKs de terceiros, sem modificar):
OpenWeatherApicompublic double temperatura(String cidade).AccuWeatherApicompublic int getTemperature(String cidade).
- Crie os adaptadores:
OpenWeatherAdapter implements PrevisaoService, que encapsula umOpenWeatherApi(o valor já vem em °C — convertadoubleparaintcom(int)).AccuWeatherAdapter implements PrevisaoService, que encapsula umAccuWeatherApie converte de °F para °C ((fahrenheit - 32) * 5 / 9).
- Crie a classe
PainelClima(o cliente):- Recebe uma
PrevisaoService(por exemplo, no construtor). - Método
exibir(String cidade)que imprime a temperatura usando apenas a interface.
- Recebe uma
- Crie uma classe de teste, por exemplo
Main, que:- Crie um
PainelClimacom cada fornecedor (passando o adaptador correspondente) e mostre o resultado para uma cidade. - Demonstre que o
PainelClimanão muda quando o fornecedor é trocado. - Explique, em um comentário ou README, como adicionar um terceiro fornecedor exigiria apenas uma nova classe de adaptador, sem alterar o
PainelClima.
- Crie um