🚨 ¡Nueva review! 🔇 Los mejores cascos con ANC del mercado: los Sony WH-1000XM4 . ¡Échale un ojo! 👀

Patrón Adapter: El Enchufe Universal de Código

Cómo hacer que dos clases incompatibles trabajen juntas con el Patrón Adaptador.

Escrito por domin el 12/10/2001

🔌 El Patrón de Diseño Adapter: El Traductor Universal

🧩 Patrón Adapter: El Lector de Tarjetas Universal

El patrón de diseño Adapter, podría ser de los patrones más usados y quizá de los más sencillos de entender.

Yo por si no lo sabías, soy europeo. Por estandarización, en Europa, en la mayoría de países usamos un enchufe concreto para conectar aparatos electricos a la red, pero si viajo a Londres (UK) voy a comprobar que ese enchufe es totalmente distinto y cuando vaya a cargar el smartphone no voy a poder conectarlo a la red eléctrica por incompatibilidad.

El Adaptador en Acción 🚨

El problema que he comentado anteriormente es una de las primeras cosas que vas a leer en un blog de viajes o similares al viajar a UK. Para estos casos venden un adaptador que lo que hace es justamente lo que hace el patrón de diseño Adapter.

El Adaptador (Adapter) es el objeto que tiene la clavija estandarizada en UK para conectarlo a la red eléctrica de allí, y en la otra parte tiene una clavija para conectar el cargador smartphone con estandarización europea.

El Adaptador no modifica el el cargador de móvil ni el enchufe de la pared. Simplemente traduce la interfaz del cargador a la interfaz que la conexión eléctrica de la pared espera, permitiendo poder conectar tu cargador a la electricidad. Es decir, el adaptador se encarga de conectar tu cargador de dos palitos al enchufe de 3 palitos de la pared.

Definición en Programación

En programación es lo mismo al final, el Patrón Adapter es una estructura que permite que dos objetos o clases incompatibles trabajen juntos, actuando como un traductor entre sus interfaces. Vamos, que mediante la función spam de la interfaz SpammerInterface puedas enviar mensajes con la API de Telegram y la de WhatsApp usando la misma función spam e implementando en cada caso la llamada a la respectiva función en cada SDK.


💡 Ejemplos Sencillos (Analogía y PHP)

Analogía para Dummies: El Traductor de Documentos

Imagina que tienes un sistema que solo sabe leer documentos en formato PDF. Y ahora tu jefe te pide que el programa tiene que poder leeer un documento en formato DOCX (Microsoft Word).

  1. Tu Sistema (El Cliente): Solo sabe llamar a leerPDF().
  2. El Documento DOCX (El Incompatible): Solo tiene un método llamado abrirDocx().
  3. El Adaptador (El Traductor): Creas un Adaptador que implementa la interfaz que tu sistema espera (leerPDF()), pero internamente, cuando lo llamas, lo que hace es llamar al método abrirDocx() del documento DOCX.
RolAnalogía (Explicación Simple)¿QUÉ HACE EN PROGRAMACIÓN?
ClienteTu sistema (solo pide PDF).Llama a un método que espera (procesarDatos()).
Interfaz ObjetivoEl formato que necesita el Cliente (PDF).Define el método que el Cliente llama.
IncompatibleLa clase que tiene el formato extraño (DOCX).Tiene el método real, pero con un nombre diferente (obtenerInfo()).
AdaptadorEl Traductor.Implementa la Interfaz Objetivo, pero llama a los métodos del Incompatible para traducirlos.

Ejemplo en PHP (Para Entender la Traducción)

Imagina que tienes un sistema de informes que solo sabe dibujar gráficos usando una biblioteca antigua. Ahora, quieres usar una nueva librería de gráficos más moderna.

<?php

// 1. Interfaz Objetivo (Lo que tu sistema ESPERA)
interface GeneradorGraficos
{
    public function dibujarReporte();
}

// 2. Clase Incompatible (Librería Externa - La que NO encaja)
class LibreriaGraficosAntigua
{
    public function mostrarDiagrama()
    {
        return "Gráfico Antiguo dibujado con método: mostrarDiagrama()";
    }
}

// 3. El Adaptador (El Traductor)
class AdaptadorGraficosAntiguos implements GeneradorGraficos
{
    public function __construct(
        private LibreriaGraficosAntigua $libreriaAntigua
    ) {}

    // Aquí es donde ocurre la MAGIA: 
    // Implementamos el método esperado (dibujarReporte)...
    public function dibujarReporte()
    {
        // ... y lo 'traducimos' llamando al método que realmente existe (mostrarDiagrama).
        return "ADAPTADO: " . $this->libreriaAntigua->mostrarDiagrama();
    }
}

// 4. El Cliente (Tu código, que no sabe nada de LibreriaGraficosAntigua)
function generarInforme(GeneradorGraficos $grafico)
{
    echo "Cliente llama a: " . $grafico->dibujarReporte() . "\n";
}

// USO:
$libreriaVieja = new LibreriaGraficosAntigua();

// Creamos el adaptador para que la vieja librería parezca una nueva
$adaptador = new AdaptadorGraficosAntiguos($libreriaVieja);

// El cliente puede usarlo sin problemas, porque el adaptador "traduce"
generarInforme($adaptador);
// Salida: Cliente llama a: ADAPTADO: Gráfico Antiguo dibujado con método: mostrarDiagrama()

🏢 Ejemplo que se podría dar en un caso real

El patrón Adapter es muy común cuando se trabaja con APIs y servicios externos, por ejemplo.

Caso Común: Integración de Pagos

Imagina que tu plataforma de comercio electrónico tiene que aceptar pagos:

  1. Tu Interfaz Objetivo: Tienes una interfaz que defines tú: ProcesadorPagos con un método ejecutarPago(monto).
  2. Proveedores Incompatibles: Quieres usar Stripe, PayPal, y el banco Paco.
    • Stripe tiene un método llamado chargeCustomer($amount).
    • PayPal tiene un método llamado processTransaction($price).
    • El Banco Paco tiene un método llamado pagamePremo($jurdeles)
  3. Los Adaptadores:
    • Creas un AdaptadorStripe que implementa ProcesadorPagos. Cuando llamas a su método ejecutarPago(monto), internamente llama a Stripe->chargeCustomer($monto).
    • Creas un AdaptadorPayPal que implementa ProcesadorPagos. Cuando llamas a su método ejecutarPago(monto), internamente llama a PayPal->processTransaction($monto).
    • Creas un AdaptadorPaco que implementa ProcessadorPagos. Cuando llamas a su método ejecutarPago(monto), internamente llama a BancoPaco->pagamePremo($monto).

Resultado: Tu código principal solo ve y usa ejecutarPago(monto), sin importarle si el pago se procesa con Stripe, PayPal o el banco del Paco. Si mañana integras otro banco, solo tienes que crear un nuevo Adapter, sin tocar el código central de tu tienda.


✅ Ventajas y ❌ Desventajas

AspectoVentajas (👍)Desventajas (👎)
CompatibilidadPermite que clases con interfaces diferentes trabajen juntas (el objetivo principal).No resuelve problemas de lógica o bugs; solo de interfaz.
SeparaciónAísla tu código del código incompatible o de terceros. Si el tercero cambia, solo modificas el Adaptador.Añade una capa de abstracción, lo que serán más archivos y más clases al proyecto.
ReutilizaciónPuedes reutilizar clases ya existentes sin tener que modificarlas.Puede introducir un impacto mínimo en el rendimiento debido a la llamada indirecta.
LimpiezaMantiene el código tu limpio y simple, ya que solo conoce la Interfaz Objetivo.Algunos lo ven como un “parche” si el diseño inicial no fue modular.

⏰ Cuándo Usarlo y Cuándo Evitarlo

¿Cuándo Usarlo? (Detección y Necesidad)

  1. Integrar Código de Terceros: Cuando usas una librería o API externa (como un servicio de clima, pagos, o mapas) y su forma de llamar a los métodos no coincide con la forma en que tu aplicación quiere llamarlos.
  2. Actualizar Sistemas: Cuando quieres reemplazar una clase vieja npmpor una nueva con una mejor funcionalidad, pero no quieres cambiar un montón de líneas de código que dependen de la clase vieja. Creas un Adaptador que hace que la nueva clase parezca la vieja.
  3. Herencia Compleja: Cuando tienes varias clases existentes que hacen cosas similares pero no tienen una superclase común, y quieres tratarlas de manera uniforme. El Adaptador proporciona esa uniformidad.

¿Cuándo NO Usarlo? (Errores Comunes)

  1. Para solucionar Lógica: Si el problema es que el código no funciona o devuelve resultados incorrectos, el Adaptador no te ayuda. Solo traduce.
  2. Cuando la Interfaz SÍ Coincide: Si la librería de terceros ya tiene métodos con los nombres que tú necesitas, usar un Adaptador es añadir complejidad innecesaria.
  3. Solucionar Problemas Menores: No lo uses para cambiar un método de obtenerDatos() a getDatos(). Un simple cambio de nombre en tu código es más fácil si solo es un lugar. Úsalo cuando el cambio debe ser global para todo tu código cliente.

Errores Comunes al Implementarlo