Patrón Adapter
Parte de la sección Programación Orientada a Objetos del Journey de Java de Coddy — lección 71 de 87.
El Patrón Adaptador es un patrón de diseño estructural que permite que interfaces incompatibles trabajen juntas. Actúa como un puente entre dos clases, convirtiendo la interfaz de una clase en una interfaz que el cliente espera. Piénselo como un adaptador de corriente que le permite enchufar un dispositivo europeo en un tomacorriente americano.
Imagina que tienes una interfaz MediaPlayer existente que reproduce archivos de audio, pero necesitas integrar una biblioteca de terceros que utiliza una interfaz completamente diferente para reproducir videos:
// Interfaz existente que utiliza su código
interface MediaPlayer {
void play(String filename);
}
// Clase de terceros con una interfaz incompatible
class AdvancedVideoPlayer {
public void playVideo(String filename) {
System.out.println("Playing video: " + filename);
}
}El adaptador envuelve la clase incompatible e implementa la interfaz esperada, traduciendo las llamadas entre ellas:
class VideoPlayerAdapter implements MediaPlayer {
private AdvancedVideoPlayer videoPlayer;
public VideoPlayerAdapter() {
this.videoPlayer = new AdvancedVideoPlayer();
}
public void play(String filename) {
videoPlayer.playVideo(filename);
}
}Ahora el código de su cliente puede utilizar el reproductor de vídeo a través de la conocida interfaz MediaPlayer:
MediaPlayer player = new VideoPlayerAdapter();
player.play("movie.mp4"); // Salida: Reproduciendo video: movie.mp4El patrón Adapter es inestimable al integrar código heredado o bibliotecas de terceros sin modificar su código fuente. El cliente permanece ajeno al hecho de que está trabajando con una clase adaptada; simplemente utiliza la interfaz que conoce.
Desafío
Fácil¡Vamos a construir un sistema de conversión de temperatura utilizando el Patrón Adapter! Tienes una aplicación meteorológica existente que funciona con temperaturas en Celsius, pero necesitas integrar un sensor de temperatura antiguo que solo ofrece lecturas en Fahrenheit. En lugar de modificar cualquiera de los dos sistemas, crearás un adaptador que cierre esa brecha.
Organizarás tu código en cuatro archivos:
TemperatureProvider.java: Define la interfaz que espera tu aplicación meteorológica. Debe declarar un métodogetTemperatureCelsius()que devuelva un double representando la temperatura en Celsius.FahrenheitSensor.java: Esto representa el sensor antiguo con una interfaz incompatible. Crea una clase con un campo privado de tipo double para la temperatura en Fahrenheit, establecido a través del constructor. Debe tener un métodoreadFahrenheit()que devuelva el valor Fahrenheit almacenado.SensorAdapter.java: Crea el adaptador que hace que el sensor Fahrenheit sea compatible con tu aplicación meteorológica. Tu adaptador debe implementarTemperatureProvidery envolver unFahrenheitSensor. Cuando se llame agetTemperatureCelsius(), debe leer del sensor y convertir el valor a Celsius usando la fórmula:(fahrenheit - 32) * 5 / 9.Main.java: ¡Une todo! Recibirás una entrada: una lectura de temperatura en Fahrenheit (double).Crea un
FahrenheitSensorcon el valor de entrada. Luego crea unSensorAdapterque envuelva este sensor. Almacena el adaptador en una variable de tipoTemperatureProviderpara demostrar que tu aplicación meteorológica puede usarlo a través de la interfaz esperada.Imprime el resultado en este formato:
"Temperature: " + String.format("%.1f", celsius) + " C"donde celsius está formateado con un decimal.
Recibirás una entrada: la temperatura en Fahrenheit (double).
Por ejemplo, con la entrada 98.6, tu salida sería:
Temperature: 37.0 CObserva cómo tu clase Main trabaja completamente con la interfaz TemperatureProvider; no tiene idea de que la temperatura real proviene de un sensor Fahrenheit que está siendo adaptado entre bastidores. ¡Esta es la elegancia del Patrón Adapter!
Hoja de referencia
El Adapter Pattern (Patrón Adaptador) es un patrón de diseño estructural que permite que interfaces incompatibles trabajen juntas actuando como un puente entre dos clases, convirtiendo una interfaz en otra que el cliente espera.
Componentes Clave
- Target Interface (Interfaz Objetivo): La interfaz que el cliente espera utilizar
- Adaptee (Adaptado): La clase existente con una interfaz incompatible
- Adapter (Adaptador): La clase que implementa la interfaz objetivo y envuelve al adaptado, traduciendo las llamadas entre ellos
Estructura 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.mp4Cuándo Utilizarlo
El Adapter Pattern es útil cuando:
- Se integra código heredado (legacy) o librerías de terceros sin modificar su código fuente
- Se necesita hacer que interfaces incompatibles trabajen juntas
- El cliente debe permanecer ajeno a la implementación adaptada
Pruébalo tú mismo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
double fahrenheit = scanner.nextDouble();
// TODO: Crea un FahrenheitSensor con el valor de entrada
// TODO: Crea un SensorAdapter que envuelva el sensor
// TODO: Almacena el adaptador en una variable TemperatureProvider
// TODO: Obtén la temperatura en Celsius e imprímela
// Formato: "Temperature: [celsius] C" con un decimal
// Pista: Usa String.format("%.1f", value) para el formato
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosIntroducción a la OOPClases vs. ObjetosLa palabra clave thisMétodosCampos (Atributos)Método constructorSobrecarga de constructoresResumen - Calculadora simple4Herencia
Herencia básica (extends)La palabra clave superSobrescritura de métodos (@Override)Encadenamiento de constructoresLa clase ObjectHerencia simple y multinivelPor qué no hay herencia múltiple de clasesResumen - Jerarquía de empleados7Métodos especiales y la clase Object
Método toString()equals() y hashCode()Método clone()compareTo() y ComparableInterfaz ComparatorResumen - Ordenamiento personalizado2Modificadores de acceso y encapsulamiento
Visión general de los niveles de accesoMétodos Getter y SetterOcultamiento de informaciónLa palabra clave finalResumen - Gestor de cuentas bancarias5Polimorfismo
Fundamentos de sobrecarga de métodosSobrescritura de métodos (tiempo de ejecución)Upcasting y DowncastingEl operador instanceofClases y métodos abstractosResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. HerenciaAgregación vs. ComposiciónClases internas, anidadas y anónimasEnums y métodos de EnumRecords (Java 16+)Clases selladas (Java 17+)11Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón BuilderPatrón ObserverPatrón Strategy3Propiedades de clase y miembros estáticos
Variables de instancia vs. estáticasMétodos estáticosBloques estáticosConstantes (static final)Repaso - Contador y utilidad6Interfaces y Clases Abstractas
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesMétodos Default y Static en InterfacesClases Abstractas vs InterfacesInterfaces FuncionalesResumen - Sistema de Pagos9Genéricos
Introducción a los genéricosClases genéricasMétodos genéricosParámetros de tipo delimitadosComodines (?, extends, super)Resumen - Contenedor genérico12Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Iterator