Aprende a Aplicar Patrones Estructurales en PHP
Bienvenidos a esta clase sobre patrones estructurales en PHP. En esta clase aprenderemos qué son los patrones estructurales y veremos cómo implementar algunos de los más utilizados en PHP. Los patrones estructurales nos ayudan a organizar las clases y objetos en una aplicación, de forma que podamos estructurar sistemas más flexibles y escalables.
Índice
Introducción a los Patrones Estructurales
Los patrones estructurales son patrones de diseño software que ayudan a organizar las clases y objetos en una aplicación. Su propósito es facilitar el diseño al identificar una forma simple de realizar relaciones entre entidades.
Hay varios patrones estructurales que podemos aplicar en PHP:
- Adapter
- Decorator
- Proxy
- Facade
Estos patrones nos permiten adaptar interfaces, agregar funcionalidades, controlar accesos y simplificar funcionalidades complejas. Veámoslos en detalle.
Patrón Adapter en PHP
El patrón Adapter permite la colaboración entre clases con interfaces incompatibles. Su propósito es convertir la interfaz de una clase en otra interfaz que el cliente espera.
Definición del Patrón Adapter
El patrón Adapter funciona como un adaptador entre dos interfaces. La clase Adapter recibe las solicitudes desde la interfaz que el cliente conoce y las traduce a la interfaz que el componente adaptado entiende.
Ejemplo del Patrón Adapter en PHP
Imaginemos que tenemos una clase Leon
con el siguiente método:
class Leon {
public function rugir() {
return "Rugido de león";
}
}
Y otra clase Gato
con este método:
class Gato {
public function maullar() {
return "Miau";
}
}
Si queremos hacer que Leon
sea adaptable a la interfaz de Gato
podemos crear un LeonAdapter
:
class LeonAdapter extends Gato {
protected $leon;
public function __construct(Leon $leon) {
$this->leon = $leon;
}
public function maullar() {
return $this->leon->rugir();
}
}
Y utilizarlo así:
$leon = new Leon();
$leonAdaptado = new LeonAdapter($leon);
echo $leonAdaptado->maullar(); // Rugido de león
De esta forma LeonAdapter
implementa la interfaz esperada por el cliente (Gato
) y se comunica con Leon
a través de su interfaz original.
Casos de Uso del Patrón Adapter
Algunos casos donde podemos aplicar el patrón Adapter:
- Para hacer que una clase con una interfaz incompatible sea usable a través de otra interfaz.
- Cuando queremos reutilizar una clase existente en un contexto diferente al original.
- Cuando tenemos clases heredadas con interfaces incompatibles que deben colaborar entre sí.
Ventajas y Desventajas del Patrón Adapter
Ventajas:
1º Permite la reutilización de código existente.
2º Aísla el código de conversiones.
3º Mejora la extensibilidad al invertir las dependencias.
Desventajas:
1º Aumenta la complejidad al introducir nuevas interfaces y clases.
Patrón Decorator en PHP
El patrón Decorator permite agregar funcionalidades a objetos de forma dinámica y transparente.
Definición del Patrón Decorator
El patrón Decorator envuelve un objeto para proporcionarle funcionalidades adicionales de forma dinámica. El decorator tiene la misma interfaz que el objeto original para que sean intercambiables.
Ejemplo del Patrón Decorator en PHP
Imaginemos que tenemos una interfaz Cafe
con los métodos getDescripcion()
y getPrecio()
:
interface Cafe {
public function getDescripcion();
public function getPrecio();
}
Y una implementación concreta CafeSimple
:
class CafeSimple implements Cafe {
public function getDescripcion() {
return "Café simple";
}
public function getPrecio() {
return 1.50;
}
}
Podemos crear decoradores como LecheDecorator
que envuelven un Cafe
y le añaden funcionalidad:
class LecheDecorator implements Cafe
{
protected $cafe;
public function __construct(Cafe $cafe)
{
$this->cafe = $cafe;
}
public function getDescripcion()
{
return $this->cafe->getDescripcion() . ", leche";
}
public function getPrecio()
{
return $this->cafe->getPrecio() + 0.5;
}
}
Y usarlo así:
$CafeSimple = new CafeSimple();
echo $CafeSimple->getDescripcion(); // Café simple
$cafeConLeche = new LecheDecorator($CafeSimple);
echo $cafeConLeche->getDescripcion(); // Café simple, leche
De esta forma podemos añadir dinámicamente funcionalidades decorando un objeto.
Casos de Uso del Patrón Decorator
Podemos usar el patrón Decorator cuando:
- Queremos añadir funcionalidades a objetos de forma transparente sin usar subclases.
- Cuando las extensiones deben ser capaces de combinarse.
- Para implementar responsabilidades que pueden ser compuestas dinámicamente.
Ventajas y Desventajas del Patrón Decorator
Ventajas:
1º Más flexible que la herencia estática.
2º Evita subclases en explosión.
3º Facilita agregar/quitar responsabilidades.
Desventajas:
1º Difícil de depurar debido a múltiples encapsulamientos.
2º Excesivos decorators pueden complicar el código.
Patrón Proxy en PHP
El Proxy controla el acceso a un objeto, actuando como sustituto.
Definición del Patrón Decorator
El patrón Proxy crea un objeto que hace de intermediario para acceder a otro objeto. El proxy recibe solicitudes e implementa cualquier comportamiento adicional antes o después de redirigirlas al objeto original.
Ejemplo del Patrón Proxy en PHP
Imaginemos una clase CuentaBancaria
que permite depositar/retirar dinero:
class CuentaBancaria {
protected $saldo;
public function depositar($monto) {
$this->saldo += $monto;
}
public function retirar($monto) {
$this->saldo -= $monto;
}
public function getSaldo() {
return $this->saldo;
}
}
Podemos crear un Proxy para controlar el acceso:
class ProxyCuentaBancaria {
protected $cuenta;
public function __construct(CuentaBancaria $cuenta) {
$this->cuenta = $cuenta;
}
public function depositar($monto) {
$this->cuenta->depositar($monto);
}
public function retirar($monto) {
if ($this->cuenta->getSaldo() >= $monto) {
$this->cuenta->retirar($monto);
} else {
throw new Exception('Saldo insuficiente');
}
}
public function getSaldo() {
return $this->cuenta->getSaldo();
}
}
Y usarlo así:
$cuenta = new CuentaBancaria();
$proxy = new ProxyCuentaBancaria($cuenta);
$proxy->depositar(100);
$proxy->retirar(20); //Ok
$proxy->retirar(120); //Exception saldo insuficiente
De esta forma el proxy protege el acceso y añade validación adicional.
Casos de Uso del Patrón Proxy
Podemos aplicar el patrón Proxy cuando necesitemos:
- Controlar el acceso a un objeto.
- Retrasar la inicialización de un objeto costoso.
- Realizar algo antes/después de una solicitud.
- Registrar solicitudes.
Ventajas y Desventajas del Patrón Proxy
Ventajas:
1º Controla el acceso al objeto original.
2º Permite funcionalidades adicionales.
3º El proxy es intercambiable por el objeto real.
Desventajas:
1º Aumenta la complejidad de la aplicación.
Patrón Facade en PHP
El patrón Facade simplifica la interfaz de un subsistema complejo.
Definición del Patrón Facade
El patrón Facade proporciona una interfaz simple para acceder a la funcionalidad de un subsistema complejo. Oculta la complejidad y expone operaciones de alto nivel.
Ejemplo del Patrón Facade en PHP
Imaginemos un subsistema para enviar emails:
class Remitente {
public function enviar($email) {
// Envía el email
}
}
class Formateador {
public function formatear($contenido) {
// Da formato al contenido
}
}
class Conexion {
public function conectar() {
// Abre conexión
}
}
Podemos crear una Facade que utilice estas clases:
class EmailFacade {
protected $remitente;
protected $formateador;
protected $conexion;
public function __construct() {
$this->remitente = new Remitente();
$this->formateador = new Formateador();
$this->conexion = new Conexion();
}
public function enviarEmail($contenido, $destinatario) {
$this->conexion->conectar();
$contenidoFormateado = $this->formateador->formatear($contenido);
$this->remitente->enviar($destinatario, $contenidoFormateado);
}
}
Y la utilizaríamos así:
$facade = new EmailFacade();
$facade->enviarEmail("Hola mundo", "[email protected]");
De esta forma abstraemos toda la complejidad en la Facade.
Casos de Uso del Patrón Facade
Podemos aplicar Facade cuando:
- Queremos simplificar la interfaz de un subsistema complejo.
- Necesitamos desacoplar un cliente de un subsistema.
- Realizar algo antes/después de una solicitud.
- Queremos estructurar un sistema en capas.
Ventajas y Desventajas del Patrón Facade
Ventajas:
1º Simplifica la interfaz de un subsistema.
2º Aísla de los cambios en un subsistema.
3º Promueve un desacoplamiento débil.
Desventajas:
No evita que un cliente dependa de implementaciones concretas.
Conclusión
Los patrones estructurales nos permiten organizar las clases y objetos en una aplicación de forma flexible y escalable.
Cada patrón resuelve problemas específicos de diseño software:
- El Adapter convierte interfaces incompatibles.
- El Decorator agrega funcionalidades dinámicamente.
- El Proxy controla el acceso a objetos.
- El Facade simplifica subsistemas complejos.
Al aplicar los patrones correctamente podemos crear sistemas más robustos, escalables y fáciles de mantener. Los patrones estructurales son una importante herramienta que todo desarrollador web debe conocer.
Preguntas Frecuentes
El Adapter resuelve el problema de la incompatibilidad entre interfaces al convertir la interfaz de una clase en otra interfaz esperada por el cliente.
Debemos usar el Decorator cuando necesitemos añadir funcionalidades a objetos de forma dinámica y transparente, sin utilizar herencia.
El Proxy sirve para controlar el acceso a un objeto, actuando como sustituto interponiéndose entre el cliente y el objeto real.
El Facade funciona como interfaz simplificada para un subsistema complejo, ocultando la complejidad de las clases del subsistema y exponiendo operaciones de alto nivel.
Sí, es común combinar patrones estructurales como Adapter + Facade o Decorator + Proxy para resolver problemas más complejos de diseño.
Ejercicios Resueltos
Ejercicio 1
Realiza un ejemplo de uso del patrón Adapter en PHP para adaptar una clase Lavadora a una interfaz Televisor con los métodos encender() y apagar().
Ejercicio 2
Implementa el patrón Decorator en PHP para agregar champiñones extras a una pizza básica.