Patrón de diseño DECORATOR en PHP
En este post quiero enseñarte otro patrón de diseño que utilizo comúnmente en los proyectos que he tenido la oportunidad de trabajar y es el patrón de diseño DECORATOR o Decorador.
Este patrón nos permite resolver el problema de agregar comportamiento o funcionalidad específica a los objetos de una clase. La POO nos permite recurrir a la herencia cuando se trata de añadir funcionalidad extra a nuestra clase.
Pero, el problema radica en que no podemos heredar de más de una clase, es decir, dada una clase C, no podrá heredar a la vez de una clase A y B.
Es por eso que el patrón de diseño Decorator nos permite añadir comportamiento específico a los objetos de una clase sin tener que añadirlo a la clase como tal.
Por otro lado, nos permite conservar el principio de Open/Closed del patrón de diseño SOLID visto en este post. En donde cada entidad o clase esta abierta para su extensión pero cerrada para su modificación.
También nos permite mantener nuestras clases con responsabilidades únicas por ende nuestras clases evitan complejidad y se mantienen ligeras de código. Se acopla también al principio Single Responsability de SOLID
Ejemplo de implementación del patrón de diseño DECORATOR en PHP
Para este ejemplo imaginemos que necesitamos calcular el precio de una Macbook basado en su versión y tamaño de pantalla.
Lo primero seria definir nuestra clase Macbook la cual implementa el getter para obtener el precio base del producto.
class Macbook
{
protected $basePrice = 1500;
public function getPrice()
{
return $this->basePrice;
}
}
Lo siguiente es generar nuestra clase abstracta decoradora la cual llamaremos MacbookDecorator
abstract class MacbookDecorator
{
protected $price;
protected $macbook;
public function __construct($macbook)
{
$this->macbook = $macbook;
}
public function getPrice()
{
return $this->macbook->getPrice() + $this->price;
}
}
Como podemos ver, añade una nueva funcionalidad la cual es calcular el precio base de la macbook en donde sumamos el precio base con el precio según la versión y tamaño de la pantalla. Verémos mas adelante como funciona.
Ahora vamos a crear una versión nueva del macbook el cual incrementará el precio de la macbook base. Añadiremos la versión MacbookPro, para ello creamos nuestra clase MacbookPro la cual va a extender de nuestro decorador.
class MacbookPro extends MacbookDecorator
{
protected $price = 950;
}
De igual forma crearemos dos clases para nuestros tamaños de pantalla disponible.
class WithDisplaySize15 extends MacbookDecorator
{
protected $price = 350;
}
class WithDisplaySize17 extends MacbookDecorator
{
protected $price = 700;
}
Gracias al decorator podemos combinar nuestras clases para generar variantes de nuestro producto por ejemplo para nuestra tienda de e-commerce.
Probando la implementación anterior
Para probar veamos lo siguiente:
$macbook = new Macbook();
echo 'Una Macbook con pantalla de 13 pulgadas cuesta ' . $macbook->getPrice() . ' euros' . PHP_EOL;
$macbook15Pulg = new WithDisplaySize15(new Macbook());
echo 'Una Macbook con pantalla de 15 pulgadas cuesta ' . $macbook15Pulg->getPrice() . ' euros' . PHP_EOL;
$macbook17Pulg = new WithDisplaySize17(new Macbook());
echo 'Una Macbook con pantalla de 17 pulgadas cuesta ' . $macbook17Pulg->getPrice() . ' euros' . PHP_EOL;
$macbookPro = new MacbookPro(new Macbook());
echo 'Una Macbook Pro con pantalla de 13 pulgadas cuesta ' . $macbookPro->getPrice() . ' euros' . PHP_EOL;
$macbookPro15Pulg = new WithDisplaySize15(new MacbookPro(new Macbook()));
echo 'Una Macbook Pro con pantalla de 15 pulgadas cuesta' . $macbookPro15Pulg->getPrice() . ' euros' . PHP_EOL;
$macbookPro17Pulg = new WithDisplaySize17(new MacbookPro(new Macbook()));
echo 'Una Macbook Pro con patanlla de 17 pulgadas cuesta ' . $macbookPro17Pulg->getPrice() . ' euros' . PHP_EOL;
Con esta implementación, podremos tener el precio de la macbook base ( que viene con 13 pulgadas), la misma versión pero con pantalla de 15 y 17 pulgadas respectivamente.
Lo mismo con la versión MacbookPro. Puedes ver el de este ejemplo en mi **github**
Otra forma de implementar en este patrón de diseño es por ejemplo en nuestros ApplicationServices o UseCases en donde cada uno tiene una funcionalidad específica pero queremos que cuando se ejecuten, se registre un log y se notifique por email sobre la acción realizada.
Aquí el approach seria, crear un decorator para nuestros ApplicationServices en donde inyectemos nuestra clase Logger y nuestra clase Mailer y de cierta forma añadimos funcionalidad extra de una forma simple y sutil, sin sobrecargar nuestras clases y sin repetir funcionalidad.
Hasta aqui todo lo relacionado con este patrón de diseño super útil y que debemos tener en cuenta para nuestros desarrollos.
Todo el contenido del ejemplo puedes conseguirlo en este **gist** de Github.
Recuerda que si tienes alguna sugerencia o pregunta, no dudes en dejar tus comentarios al final del post.
Si te gustó este post, ayúdame a que pueda servirle a muchas más personas, compartiendo mis contenidos en tus redes sociales.
Espero que este post haya sido de gran ayuda para ti, y como siempre, cualquier inquietud o duda que tengas, puedes contactarme por cualquiera de las vías disponibles, o dejando tus comentarios al final de este post. También puedes sugerir que temas o post te gustaría leer a futuro.