Patrones Estructurales en PHP

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.

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.


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().

    // Interfaz esperada por el cliente
    interface Televisor {
      public function encender();
      public function apagar();
    }
    
    // Clase a adaptar
    class Lavadora {
      public function lavar() {
        echo "Lavando...";
      }
    
      public function detener() {
        echo "Deteniendo lavado...";
      }
    }
    
    // Clase adaptador
    class LavadoraAdapter implements Televisor {
    
      protected $lavadora;
    
      public function __construct(Lavadora $lavadora) {
        $this->lavadora = $lavadora;
      }
    
      public function encender() {
        $this->lavadora->lavar();
      }
    
      public function apagar() {
        $this->lavadora->detener();
      }
    }
    
    // Cliente
    $lavadora = new Lavadora();
    $lavadoraAdaptada = new LavadoraAdapter($lavadora);
    
    $lavadoraAdaptada->encender(); // Lavando...
    $lavadoraAdaptada->apagar(); // Deteniendo lavado...
    
  • Ejercicio 2

    Implementa el patrón Decorator en PHP para agregar champiñones extras a una pizza básica.

    // Interfaz común
    interface Pizza {
      public function getPrecio();
    }
    
    // Componente concreto
    class PizzaBase implements Pizza {
    
      public function getPrecio() {
        return 10;
      }
    
    }
    
    // Decorator
    class ChampinonesExtraDecorator implements Pizza {
    
      protected $pizza;
    
      public function __construct(Pizza $pizza) {
        $this->pizza = $pizza;
      }
    
      public function getPrecio() {
        return $this->pizza->getPrecio() + 3;
      }
    }
    
    // Uso
    $pizzaBase = new PizzaBase();
    echo $pizzaBase->getPrecio(); // 10
    
    $pizzaConChampinonesExtra = new ChampinonesExtraDecorator($pizzaBase);
    echo $pizzaConChampinonesExtra->getPrecio(); // 13

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.

Deja un Comentario

¡Bienvenido a nuestro sitio web!

Este sitio utiliza cookies para mejorar tu experiencia de navegación y proporcionar funciones adicionales relacionadas con el análisis de tráfico y el rendimiento del sitio. Política de cookies.