Patron de diseño: Decorador (decorator pattern) en php

Enviado por Francisco Carrizales el Jue, 07/02/2019 - 19:55

Este es uno de los patrones de diseño que me gusta mucho. Por que te permite sumar funcionalidad con cada clase que vas agregando, la cual queda en una estructura fácil de mantener, ya que cada clase encapsula una funcionalidad individual que en conjunto dan como resultado algo genial.

Este patrón como su nombre lo sugiere es decorar, es agregar funcionalidad dinámicamente a un objeto concreto, para permitirle modificar el resultado y en la cual se pueden realizar muchas combinaciones.

El escenario en el cual se utiliza este patrón de diseño, es cuando el cliente o el usuario tiene la capacidad de seleccionar muchas opciones y en las cuales puede realizar combinaciones.

Esta es la palabra frase clave Se usa cuando hay combinaciones. Y no se tiene control de que se selecciona.

Ej1.

Imagina el siguiente escenario. Estás desarrollando un juego de rpg y deseas calcular cual es el daño total a realizar. Para ello tienes 10 espadas disponibles, Tienes espacio para 2 accesorios y tienes disponibles 20 accesorios disponibles, además existen Buffs que aumenta los daños, Equipo que aumenta el daño, En fin, tienes un montón de combinaciones que puedes realizar, Esto traducirlo a código debería ser un caos.

Es aquí donde el patrón de diseño de ayuda a como debes estructurar las clases, para que el usuario o en este caso el jugador pueda hacer todas las combinaciones que se le ocurran.

Ej2

El siguiente ejemplo es uno que vi, en un libro de Head First Design Patterns que si tienen oportunidad de leerlos, es muy recomendable. El ejemplo es el siguiente, es cómo calcular el precio de un café, en una cafetería, en la cual la cantidad de combinaciones posibles es abrumadora, que si es un americano. si es un expresso si es otro tipo. agregale caramelo agregale vanilla, agregale bombones, agregale chispitas. a pero si las chispas + chocolate hay descuento. etc etc. Las combinaciones se las dejas al usuario.

El concepto es que a partir de un objeto concreto comienzas a adjuntar otros objetos comúnmente llamados decoradores, para que el resultado inicial sea modificado por cada uno de los objetos adjuntos. Dando como resultado algo totalmente distinto.

Algo como el siguiente diagrama:

diagrama-patron-diseño-decorador

Para nuestro primer ejemplo sería algo como:

diagrama-patron-diseño-decorador-ejemplo1

De tal forma que se pueden adjuntar más decoradoras como se necesiten.

El diagrama de uml es de esta manera:

diagrama-uml-patron-diseño-decorador

En cristiano:

  1. Definir una interfaz con el método a realizar (Ej1, calcularDaño)  (Ej2 calcularCosto)
  2. Definir una clase Concreta que implemente esta interfaz (Ej1,  Personaje  ) ( Ej1, Expresso )
  3. Definir una clase abstracta que implementa la interfaz del paso 1 donde tengamos información a reutilizar entre los objetos Concretos y los decoradores.
  4. Definir tantas clases se necesiten que hereden de la clase abstracta del paso 3
  5. Hacer la decoración

 

Comencemos con el ejemplo 1.

1

interface iCalculoDano
{
    public function calcularDano();
}

2

class Personaje implements iCalculoDano
{
    protected $_danoBase = 10;

    public function __construct()
    {
        echo '<br> Inicializamos el Personaje';
    }

    public function calcularDano()
    {
        return $this->_danoBase;
    }
}

3

abstract class DanoDecorador implements iCalculoDano
{
    protected $_personaje;

    public function __construct(iCalculoDano $personaje)
    {
        echo '<br> Inicializamos ' .get_class($this);
        $this->_personaje  = $personaje;
    }
}

Nota importante, es el contructor que recibe un objecto que debe implementar la interfaz que implementamos en el paso1 y que tenemos una variable $_personaje.

4


class Espada1 extends DanoDecorador
{
    private $_dano = 5;

    public function calcularDano()
    {
        return $this->_personaje->calcularDano() + $this->_dano ;
    }
}


class Accesorio1 extends DanoDecorador
{
    private $_dano = 6;

    public function calcularDano()
    {
        return $this->_personaje->calcularDano() + $this->_dano ;
    }
}

class Accesorio2 extends DanoDecorador
{
    private $_dano = -1;

    public function calcularDano()
    {
        return $this->_personaje->calcularDano() + $this->_dano ;
    }
}


class DebuffClima extends DanoDecorador
{
    private $_dano = -5;

    public function calcularDano()
    {
        return $this->_personaje->calcularDano() + $this->_dano ;
    }
}

5

$personaje = new Personaje();

$personaje = new Espada1($personaje);

$personaje = new Accesorio1($personaje);

$personaje = new Accesorio2($personaje);

if (rand(1, 2) == 1) {
    $personaje = new DebuffClima($personaje);
}

echo '<br> El daño es: ' . $personaje->calcularDano();

Hay que notar que el objectoConcreto es pasado como parámetro en los decoradores y así sucesivamente, Hasta que al final se invoca el método para calcular.

Conclusión

Realmente no se como expresar en palabras, como este patrón de diseño decorador, te ayuda mucho, a la hora de programar este tipo de escenario, en el que se presentan combinaciones. No es hasta que lo implementas en un caso real, que te das cuenta el potencial. Te ayuda a crear aplicaciones que antes creías imposibles

 

Etiquetas

Añadir nuevo comentario

HTML Restringido

  • Etiquetas HTML permitidas: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Saltos automáticos de líneas y de párrafos.
  • Las direcciones de correos electrónicos y páginas web se convierten en enlaces automáticamente.