Patrones de Diseño Avanzados en PHP

Patrones de Diseño Avanzados más utilizados

¡Bienvenidos a la clase sobre Patrones de Diseño Avanzados en PHP! En esta clase profundizaremos en algunos de los patrones de diseño más complejos pero a la vez más poderosos para desarrollar aplicaciones PHP orientadas a objetos de alta calidad.

Aprenderemos qué son, cómo se implementan, sus beneficios y desventajas, así como ejemplos prácticos de su aplicación en PHP. Dominar estos patrones avanzados te permitirá crear sistemas altamente escalables, flexibles y fáciles de mantener.

Los patrones que estudiaremos en detalle son: Mediator, Memento, State, Template Method y Visitor. Todos ellos forman parte del libro clásico "Patrones de Diseño" de la pandilla de 4, los llamados "Gang of Four”.

Así que prepárate para llevar tus habilidades de programación PHP al siguiente nivel. ¡Manos a la obra!

Introducción a los Patrones de Diseño Avanzados

Los patrones de diseño avanzados son soluciones ya probadas para resolver problemas comunes en el desarrollo de software orientado a objetos. Ayudan a crear sistemas más flexibles y fáciles de mantener.

Se consideran "avanzados" porque su implementación es más compleja que la de los patrones básicos como Singleton o Factory. Requieren un alto nivel de abstracción y dominio de los principios de POO.

Entre sus principales beneficios están:

  • - Permiten desacoplar componentes de una aplicación

  • - Facilitan la comunicación entre clases y objetos

  • - Encapsulan funcionalidades que varían

  • - Promueven el reuso de código

Algunos ejemplos de patrones avanzados son Mediator, State, Visitor, Memento y Template Method. En esta clase nos enfocaremos en los 5 mencionados anteriormente por ser de los más utilizados en proyectos reales.

Veamos cada uno en detalle:


Patrón Mediator en PHP

El patrón Mediator tiene como fin encapsular la comunicación entre objetos. Centraliza esta interacción en un único punto, el mediador, evitando así el acoplamiento directo entre los objetos.

Es muy útil cuando tenemos un conjunto de objetos que necesitan comunicarse de forma intensiva. El mediator promueve un bajo acoplamiento y evita que los objetos se referencien explícitamente entre sí.

Estructura Patrón Mediator

Los elementos principales son:

  • - Mediator: define la interfaz para la comunicación entre objetos.

  • - Colleagues: los objetos que se comunican a través del mediator.

  • - ConcreteMediator: implementa la lógica de comunicación entre objetos. Conoce y mantiene las referencias a los objetos colleagues.

  • - ConcreteColleague: cada clase que se comunica con otras a través del mediator.

Ejemplo Práctico Patrón Mediator

Imaginemos un sistema de chat donde los usuarios pueden comunicarse en un canal común.

Primero, la interfaz Mediator:

interface ChatMediator
{
  public function sendMessage(string $msg, User $user);

  public function addUser(User $user);
}

El ConcreteMediator maneja la interacción entre Users:

class Chatroom implements ChatMediator


  private $users;

  public function sendMessage(string $msg, User $user)
  {
    foreach ($this->users as $u) {
      if ($u !== $user) {
        $u->receive($msg);
      }
    }
  }

  public function addUser(User $user)
  {
    $this->users[] = $user;
  }

}

Los Users se comunican sólo a través del Chatroom:

class User implements Colleague
{

  private $name;
  private $chatMediator;

  public function __construct(string $name, ChatMediator $chatMediator)
  {
    $this->name = $name;
    $this->chatMediator = $chatMediator;
  }

  public function send(string $msg)
  {
    $this->chatMediator->sendMessage($msg, $this);
  }

  public function receive(string $msg)
  {
    echo $this->name . ' received: ' . $msg;
  }

}

$mediator = new Chatroom();

$john = new User('John Doe', $mediator);
$jane = new User('Jane Doe', $mediator);

$mediator->addUser($john);
$mediator->addUser($jane);

$john->send('Hi there!');

// Output// Jane Doe received: Hi there!

Beneficios Patrón Mediator

  • - Bajo acoplamiento entre objetos

  • - Evita referencias explícitas entre objetos

  • - Simplifica la comunicación

  • - Centraliza el control

  • - Promueve reutilización de objetos

Desventajas Patrón Mediator

  • - El mediator se vuelve muy complejo si hay muchas interdependencias entre objetos

  • - La lógica queda centralizada en el mediator

Cuando usar Patrón Mediator

  • - Cuando tenemos un conjunto de objetos altamente acoplados

  • - Cuando es difícil mantener las referencias entre los objetos

  • - Cuando queremos reutilizar objetos en diferentes escenarios

  • - Cuando tenemos una red de comunicación compleja


Patrón Memento en PHP

El patrón Memento permite guardar y restaurar el estado previo de un objeto sin revelar los detalles de su implementación.

Es útil cuando necesitamos darle al objeto la capacidad de volver a un estado anterior, como por ejemplo en una operación de deshacer/rehacer.

Estructura Patrón Memento

Los elementos principales son:

  • - Origen: el objeto real del que queremos guardar y restaurar estados.

  • - Memento: almacena el estado interno del origen. Es un objeto plano, sólo transporta información.

  • - Cuidador: es responsable de los mementos. Los crea, guarda y restaura cuando el origen los solicita.

Ejemplo Práctico Patrón Memento

Vamos a implementar la funcionalidad de deshacer/rehacer en un editor de texto:

class Editor
{
  private $content;

  public function type(string $words)
  {
    $this->content = $this->content . ' ' . $words;
  }

  public function getContent()
  {
    return $this->content;
  }

  public function save()
  {
    return new EditorState($this->content);
  }

  public function restore(EditorState $state)
  {
    $this->content = $state->getContent();
  }

}

// Memento
class EditorState
{
  private $content;

  public function __construct(string $content)
  {
    $this->content = $content;
  }

  public function getContent()
  {
    return $this->content;
  }
}

// Cuidador
class History
{
   private $states = [];

   public function push(EditorState $state)
   {
     $this->states[] = $state;
   }

   public function pop()
   {
     return array_pop($this->states);
   }

}

$editor = new Editor();

// Type some stuff
$editor->type('This is the first sentence.');
$editor->type('This is second.');

// Guardar el estado
$history = new History();
$history->push($editor->save());

// Type more
$editor->type('And this is third.');

// Deshacer
$editorState = $history->pop();
$editor->restore($editorState);

echo $editor->getContent();// This is the first sentence. This is second.

Beneficios Patrón Memento

  • - Permite salvar y restaurar estados del origen

  • - El memento es plano, no revela detalles internos

  • - El cuidador simplifica el acceso a los mementos

  • - Promueve encapsulamiento

Desventajas Patrón Memento

  • - Requiere agregar comportamiento extra al origen

  • - Excesivos mementos consumen memoria

  • - El cuidador puede volverse muy complejo

Cuando usar Patrón Memento

  • - Para implementar funcionalidades de deshacer/rehacer

  • - Para restaurar estados de objetos complejos

  • - Para guardar estados y luego restaurarlos

  • - Cuando necesitamos tomar snapshots de un objeto


Patrón State en PHP

Permite a un objeto modificar su comportamiento cada vez que cambia su estado interno. Parecido a una máquina de estados.

Es útil cuando el comportamiento de un objeto depende de su estado, y este debe cambiar en runtime. Evita condicionales complejos.

Estructura Patrón State

Los elementos principales son:

  • - Contexto: mantiene una referencia al objeto State que define el estado actual.

  • - State: interface que define el comportamiento asociado a un estado particular.

  • - ConcreteState: cada clase implementa un estado concreto y el comportamiento relacionado.

  • - Transition: interfaz para cambiar de estado.

Ejemplo Práctico Patrón State

Vamos a implementar una clase para un pedido que puede estar en diferentes estados:

Primero la interfaz State:

interface OrderState
{
  public function next(OrderContext $order);
}

El contexto con el estado actual:

class OrderContext
{

  private $state;

  public function __construct(OrderState $state)
  {
    $this->transitionTo($state);
  }

  public function transitionTo(OrderState $state)
  {
    echo "Context: Transition to " . get_class($state) . "<br>";
    $this->state = $state;
    $this->state->next($this);
  }

}

Los Concrete States:

// Pedido creado
class OrderedState implements OrderState
{
  public function next(OrderContext $order)
  {
    echo "Pedido recibido.<br>";
  }
}

// Pagado
class PaidState implements OrderState
{
  public function next(OrderContext $order)
  {
    echo "Pedido pagado.<br>";
  }
}

// Enviado
class ShippedState implements OrderState
{
  public function next(OrderContext $order)
  {
    echo "Pedido enviado.<br>";
  }
}

Uso:

// Inicialmente pedido creado
$order = new OrderContext(new OrderedState());

// Pagar pedido
$order->transitionTo(new PaidState());

// Enviar pedido
$order->transitionTo(new ShippedState());

Salida:

Context: Transition to OrderedState
Pedido recibido.

Context: Transition to PaidState
Pedido pagado.

Context: Transition to ShippedState
Pedido enviado.

Beneficios Patrón State

  • - Evita condicionales complejos para el comportamiento

  • - Fácil de extender agregando nuevos estados

  • - Promueve el principio de responsabilidad única

  • - Centraliza transiciones de estado

Desventajas Patrón State

  • - Excesivos estados y transiciones complican el diseño

  • - Requiere crear una nueva subclase por cada estado

Cuando usar Patrón State

  • - Cuando el comportamiento de un objeto depende de su estado

  • - Para evitar condicionales o estructuras switch/case extensas

  • - Cuando las transiciones de estado deben ser explícitas

  • - Cuando hay código específico asociado al estado


Patrón Template Method en PHP

Define en una superclase el esqueleto de un algoritmo y permite que las subclases overriding ciertos pasos. Promueve el reuso de código.

Es útil para implementar variaciones de un mismo algoritmo, centralizando las partes comunes y extrayendo las partes variables.

Estructura Patrón Template Method

Los elementos principales son:

  • - AbstractClass: superclase abstracta que define la plantilla del algoritmo.

  • - ConcreteClass: subclases que implementan los métodos abstractos.

  • - PrimitiveOperations: métodos concretos que forman parte del algoritmo.

  • - AbstractOperations: métodos abstractos para ser implementados.

Ejemplo Práctico Patrón Template Method

Vamos a implementar distintos algoritmos de clasificación que comparten una estructura similar:

<?php
abstract class SortAlgorithm
{

    // Plantilla del algoritmo
    final public function sort(array $dataset): array
    {
        $result = array();
        $this->preProcessDataset($dataset);

        // Algoritmo concreto
        $result = $this->sortAlgorithm($dataset);

        $result = $this->postProcessDataset($result);

        return $result;
    }

    // Pasos concretos
    final protected function preProcessDataset(array &$dataset): array
    {
        // Lógica de preprocesamiento
        echo "Preprocesando dataset..." . PHP_EOL;
        return array();
    }

    final protected function postProcessDataset(array &$result): array
    {
        // Lógica de postprocesamiento
        echo "Postprocesando resultado..." . PHP_EOL;
        return array();
    }

    // Pasos abstractos
    abstract protected function sortAlgorithm(array $dataset): array;
}


// Subclase concreta 1
class BubbleSort extends SortAlgorithm
{
    protected function sortAlgorithm(array $dataset): array
    {
        $result = array();
        echo "Ordenando con Bubble Sort" . PHP_EOL;
        // ... código burbuja
        return $result;
    }
}

// Subclase concreta 2
class QuickSort extends SortAlgorithm
{
    protected function sortAlgorithm(array $dataset): array
    {
        $result = array();
        echo "Ordenando con Quick Sort" . PHP_EOL;
        // ... código quicksort
        return $result;
    }
}

Uso:

$dataset = [5, 1, 4, 2, 8];

$bubbleSort = new BubbleSort();
$result = $bubbleSort->sort($dataset);

// Output// Preprocesando dataset...// Ordenando con Bubble Sort// Postprocesando resultado...

$quickSort = new QuickSort();
$result = $quickSort->sort($dataset);

// Output// Preprocesando dataset...// Ordenando con Quick Sort// Postprocesando resultado...

Beneficios Patrón Template Method

  • - Reutiliza código común y extrae partes variables

  • - Evita duplicación de código

  • - Simplifica clases extensibles

  • - Desacopla algoritmos de sus variaciones

Desventajas Patrón Template Method

  • - Poco flexible para cambios profundos

  • - Requiere subclasificar aún cuando no sea necesario

  • - Lógica más difícil de entender en un inicio

Cuando usar Patrón Template Method

  • - Para implementar variaciones de algoritmos

  • - Cuando se necesita reutilizar parte de un algoritmo

  • - Para evitar duplicación de código en familias de clases similares

  • - Cuando la estructura del algoritmo es estable pero los pasos internos varían


Patrón Visitor en PHP

Permite definir operaciones adicionales sobre una jerarquía de clases sin modificar las clases originales. Usa doble despacho.

Es útil cuando se necesita agregar funcionalidad a una jerarquía de clases y no es posible modificar el código.

Estructura Patrón Visitor

Los elementos principales son:

  • - Visitor: declara una operación de visita para cada clase de la jerarquía

  • - Element: interface común de los elementos visitables

  • - ConcreteElement: implementa el Element y define el método accept() que recibe el Visitor

  • - ObjectStructure: puede enumerar elementos y ejecutar el Visitor sobre ellos

Ejemplo Práctico Patrón Visitor

Supongamos una jerarquía de FigurasGeométricas. Queremos agregar funcionalidad para calcular áreas:

<?php
// Elementos a visitar
interface Shape
{
    public function accept(Visitor $visitor);
}

class Circle implements Shape
{
    public $radius;

    public function accept(Visitor $visitor)
    {
        $visitor->visitCircle($this);
    }
}

class Square implements Shape
{
    public $side;

    public function accept(Visitor $visitor)
    {
        $visitor->visitSquare($this);
    }
}

// Visitor
interface Visitor
{
    public function visitCircle(Circle $circle);
    public function visitSquare(Square $square);
}

// ConcreteVisitor
class AreaCalculator implements Visitor
{

    public function visitCircle(Circle $circle)
    {
        $area = pi() * pow($circle->radius, 2);
        echo "Circle area: " . $area;
    }

    public function visitSquare(Square $square)
    {
        $area = pow($square->side, 2);
        echo "Square area: " . $area;
    }
}

// Object structure
$shapes = [
    new Circle(2),
    new Square(5)
];

$areaCalc = new AreaCalculator();

foreach ($shapes as $shape) {
    $shape->accept($areaCalc);
}

// Output// Circle area: 12.566370614359172// Square area: 25

Beneficios Patrón Visitor

  • - Permite agregar nuevas operaciones sin cambiar las clases

  • - Cumple el principio de responsabilidad única

  • - Promueve el bajo acoplamiento

  • - Simplifica la extensibilidad de jerarquías

Desventajas Patrón Visitor

  • - Excesivos Visitors pueden complicar el código

  • - Requiere acceso al código fuente de las clases

  • - overhead por uso de interfaces y doble despacho

Cuando usar Patrón Visitor

  • - Para extender una jerarquía de clases sin modificaciones

  • - Cuando se necesitan operaciones que dependen de las clases concretas

  • - Para evitar un diseño muy acoplado

  • - Cuando se requiere recorrer una estructura de objetos


Conclusión

Los patrones de diseño avanzados resuelven problemas comunes de forma elegante promoviendo bajo acoplamiento, encapsulamiento y flexibilidad.

Dominar su implementación en PHP te permitirá crear aplicaciones más robustas, escalables y fáciles de mantener.

Al aplicar correctamente estos patrones estarás siguiendo las mejores prácticas de programación orientada a objetos.

Espero que esta clase te haya mostrado el poder de patrones como Mediator, Memento, State, Template Method y Visitor para tomar tu código PHP al siguiente nivel. ¡Sigue practicando y pronto los usarás como segundo sentido!


Preguntas Frecuentes

¿Qué nivel de PHP se requiere para aplicar patrones de diseño avanzados?

Se recomienda tener un buen dominio de PHP orientado a objetos y de principios como encapsulamiento, polimorfismo, herencia, abstracción, etc. También es útil conocer patrones básicos como Singleton, Factory, Adapter, etc.

¿Cuáles son los patrones de diseño avanzados más populares?

Algunos de los patrones avanzados más populares son Strategy, Observer, Decorator, Proxy, Composite y Flyweight. Los 5 vistos en esta clase (Mediator, Memento, State, Template Method y Visitor) también se usan ampliamente.

¿Es obligatorio usar patrones avanzados en todos los proyectos PHP?

No es obligatorio. Dependerá de la complejidad y tamaño del software. Para proyectos pequeños pueden no ser necesarios patrones tan avanzados. Pero entre más grande y complejo sea el sistema, más útiles resultarán.

¿Qué beneficios prácticos tiene aplicar estos patrones avanzados?

Entre los beneficios están: mejor mantenibilidad de código, eliminar duplicación, desacoplar componentes, aislar partes variables, facilitar reutilización, encapsular funcionalidad y en general crear sistemas más flexibles y escalables.

¿Cómo saber si necesito usar un patrón avanzado en mi proyecto PHP?

Algunas señales son: alto acoplamiento entre clases, dificultad para reutilizar/extender código, excesivos condicionales por estado variable, duplicación de lógica en varios lugares, objetos demasiado complejos o con responsabilidades de más.


Preguntas Frecuentes

Se recomienda tener un buen dominio de PHP orientado a objetos y de principios como encapsulamiento, polimorfismo, herencia, abstracción, etc. También es útil conocer patrones básicos como Singleton, Factory, Adapter, etc.

Algunos de los patrones avanzados más populares son Strategy, Observer, Decorator, Proxy, Composite y Flyweight. Los 5 vistos en esta clase (Mediator, Memento, State, Template Method y Visitor) también se usan ampliamente.

No es obligatorio. Dependerá de la complejidad y tamaño del software. Para proyectos pequeños pueden no ser necesarios patrones tan avanzados. Pero entre más grande y complejo sea el sistema, más útiles resultarán.

Entre los beneficios están: mejor mantenibilidad de código, eliminar duplicación, desacoplar componentes, aislar partes variables, facilitar reutilización, encapsular funcionalidad y en general crear sistemas más flexibles y escalables.

Algunas señales son: alto acoplamiento entre clases, dificultad para reutilizar/extender código, excesivos condicionales por estado variable, duplicación de lógica en varios lugares, objetos demasiado complejos o con responsabilidades de más.

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.