Patrón Adapter
Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy — lección 76 de 91.
El Patrón Adapter es un patrón de diseño estructural que permite que objetos con interfaces incompatibles trabajen juntos. Actúa como un puente entre dos interfaces, traduciendo las llamadas de un formato a otro.
Imagina que estás construyendo una aplicación que procesa pagos y necesitas integrar una biblioteca de pagos de terceros. La biblioteca funciona perfectamente, pero su interfaz no coincide con lo que tu código espera. En lugar de reescribir toda tu aplicación, creas un adaptador que traduce entre ambos.
<?php
// La interfaz que su aplicación espera
interface PaymentProcessor {
public function pay(float $amount): string;
}
// Biblioteca de terceros con una interfaz diferente
class StripeAPI {
public function createCharge(int $cents, string $currency): string {
return "Charged {$cents} {$currency} via Stripe";
}
}
El adaptador implementa la interfaz esperada mientras utiliza internamente la clase de terceros:
<?php
class StripeAdapter implements PaymentProcessor {
public function __construct(private StripeAPI $stripe) {}
public function pay(float $amount): string {
$cents = (int)($amount * 100);
return $this->stripe->createCharge($cents, 'USD');
}
}
$stripe = new StripeAPI();
$processor = new StripeAdapter($stripe);
echo $processor->pay(29.99);
Resultado:
Charged 2999 USD via StripeEl adaptador convierte dólares a centavos y llama al método apropiado. El código de su aplicación solo conoce PaymentProcessor, permaneciendo felizmente ajeno a los requisitos específicos de Stripe. Si más adelante cambia a un proveedor de pagos diferente, simplemente crea un nuevo adaptador sin cambiar su código existente.
Desafío
FácilVamos a construir un sistema de conversión de temperatura utilizando el Patrón Adapter. Tienes un sensor de temperatura heredado (legacy) que proporciona lecturas en Fahrenheit, pero tu aplicación meteorológica moderna espera todas las temperaturas en Celsius. En lugar de modificar cualquiera de los sistemas, crearás un adaptador que cierre la brecha.
Organizarás tu código en cuatro archivos:
TemperatureSensor.php— Define una interfazTemperatureSensorque es la que espera tu aplicación meteorológica. Debe tener un métodogetTemperature(): floatque devuelva la temperatura en Celsius.LegacySensor.php— Crea una claseLegacySensorque simule el hardware antiguo. Esta clase tiene una interfaz diferente a la que necesita tu aplicación:- Recibe una lectura en Fahrenheit en su constructor (utiliza la promoción de propiedades del constructor)
- Tiene un método
readFahrenheit(): floatque devuelve el valor Fahrenheit almacenado
LegacySensorAdapter.php— Incluye tanto la interfaz TemperatureSensor como la clase LegacySensor. Crea una claseLegacySensorAdapterque implementeTemperatureSensory envuelva unLegacySensor. Tu adaptador debe:- Aceptar un
LegacySensoren su constructor - Implementar
getTemperature()leyendo el valor Fahrenheit del sensor heredado y convirtiéndolo a Celsius usando la fórmula:(fahrenheit - 32) * 5 / 9
- Aceptar un
main.php— Incluye el archivo LegacySensorAdapter. Recibirás una entrada: una temperatura en Fahrenheit.Crea un
LegacySensorcon la temperatura de entrada, luego envuélvelo en unLegacySensorAdapter. Llama agetTemperature()en el adaptador e imprime el resultado redondeado a dos decimales.El formato de salida debe ser:
"Temperature: " . round($celsius, 2) . "C"
Este desafío demuestra cómo el Patrón Adapter te permite integrar interfaces incompatibles sin modificar el código existente. Tu aplicación meteorológica funciona exclusivamente con la interfaz TemperatureSensor, sin saber en absoluto que las lecturas reales provienen de un sensor Fahrenheit heredado.
Hoja de referencia
El Patrón Adapter es un patrón de diseño estructural que permite que objetos con interfaces incompatibles trabajen juntos actuando como un puente entre ellos.
Estructura Básica
Define la interfaz que espera tu aplicación:
<?php
interface PaymentProcessor {
public function pay(float $amount): string;
}Una clase de terceros con una interfaz diferente:
<?php
class StripeAPI {
public function createCharge(int $cents, string $currency): string {
return "Charged {$cents} {$currency} via Stripe";
}
}Creando el Adaptador
El adaptador implementa la interfaz esperada mientras utiliza internamente la clase de terceros:
<?php
class StripeAdapter implements PaymentProcessor {
public function __construct(private StripeAPI $stripe) {}
public function pay(float $amount): string {
$cents = (int)($amount * 100);
return $this->stripe->createCharge($cents, 'USD');
}
}Uso
$stripe = new StripeAPI();
$processor = new StripeAdapter($stripe);
echo $processor->pay(29.99);
// Salida: Charged 2999 USD via StripeEl adaptador traduce entre interfaces, permitiendo que el código de tu aplicación permanezca sin cambios al integrar librerías de terceros o cambiar implementaciones.
Pruébalo tú mismo
<?php
require_once 'LegacySensorAdapter.php';
// Leer entrada
$fahrenheit = floatval(trim(fgets(STDIN)));
// TODO: Crear un LegacySensor con la temperatura de entrada
// TODO: Envolverlo en un LegacySensorAdapter
// TODO: Llamar a getTemperature() en el adaptador
// TODO: Imprimir el resultado redondeado a dos decimales
// Formato de salida: Temperature: [celsius]C
?>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 OOPClases vs ObjetosLa palabra clave $thisMétodosPropiedadesConstructor (__construct)Destructor (__destruct)Resumen - Calculadora simple4Herencia
Herencia básicaLa palabra clave parent::Sobrescritura de métodosLa palabra clave finalClases abstractasResumen - Jerarquía de empleados7Encapsulamiento
Público, Protegido, PrivadoModificadores de acceso a fondoGetters y SettersOcultamiento de informaciónPromoción de constructor (8.0)Resumen - Sistema de registros de estudiantes10Conceptos avanzados de POO
Composición vs. HerenciaInyección de dependenciasClases anónimasEnums (PHP 8.1)Fibers (PHP 8.1)Clonación de objetos a fondoGeneradores e Iteradores13Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository2Espacios de nombres y Autoloading
Introducción a los espacios de nombresLa palabra clave useEstándar de Autoloading PSR-4Autoloader de ComposerResumen - Proyecto organizado5Interfaces y Contratos
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesInterfaz vs Clase AbstractaType Hinting con InterfacesResumen - Calculadora de Formas8Métodos mágicos
Introducción a los métodos mágicos__toString y __debugInfo__get, __set, __isset, __unset__call y __callStatic__clone y clonación de objetos__serialize y __unserializeResumen - Colección personalizada3Propiedades de clase
Propiedades de instancia vs. estáticasConstantes en clasesMétodos y propiedades estáticosPropiedades privadas y protegidasPropiedades de solo lectura (PHP 8.1)Resumen - Gestor de cuenta bancaria6Polimorfismo
Sobrescritura de métodos revisadaPolimorfismo mediante interfacesType Hinting y tipos de uniónEnlace estático tardíoResumen - Procesador de pagos9Traits
Introducción a los TraitsUso de múltiples TraitsResolución de conflictos de TraitsMétodos abstractos en TraitsTraits frente a herencia12Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy