Patrón Decorator

Patrón Decorator

En muchas ocasiones necesitamos añadir funcionalidades extra a un objeto sin modificar su código original.
La solución rápida suele ser crear nuevas subclases con variantes, pero esto puede volverse inmanejable.

El patrón Decorator nos permite envolver un objeto con capas adicionales de comportamiento, como si fueran buffs mágicos en un RPG 🧙‍♂️.

❌ El problema

Imaginemos un Mago que puede lanzar un hechizo básico de magia.
Si queremos que a veces el hechizo tenga fuego, otras veces hielo, y en ocasiones hasta veneno, podemos caer en la tentación de hacer muchas subclases:

  • MagoConFuego
  • MagoConHielo
  • MagoConVeneno
  • MagoConFuegoYVeneno
  • … y así hasta el infinito 😵‍💫.

Esto rompe la simplicidad y vuelve difícil mantener el código.
Veamos una primera versión ingenua:

<?php

class Mago {
    private string $nombre;

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

    public function lanzarHechizo(string $enemigo): void {
        echo "✨ {$this->nombre} lanza un simple rayo mágico contra $enemigo.\n";
    }
}

$mago = new Mago("Gandalf");
$mago->lanzarHechizo("Orco Malvado");

✅ La solución: Patrón Decorator

Con el patrón Decorator podemos envolver al Mago con distintos efectos mágicos sin modificar su clase original.
Cada decorador añade su propia capa de poder, como si fuesen items mágicos equipables.

1️⃣ Definimos la interfaz base

<?php

interface Hechizo {
    public function lanzar(string $enemigo): void;
}

2️⃣ Creamos el hechizo básico del mago

<?php

class HechizoBasico implements Hechizo {
    private string $nombre;

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

    public function lanzar(string $enemigo): void {
        echo "✨ {$this->nombre} lanza un rayo mágico a $enemigo.\n";
    }
}

3️⃣ Creamos el decorador base

<?php

abstract class HechizoDecorator implements Hechizo {
    protected Hechizo $hechizo;

    public function __construct(Hechizo $hechizo) {
        $this->hechizo = $hechizo;
    }

    public function lanzar(string $enemigo): void {
        $this->hechizo->lanzar($enemigo);
    }
}

4️⃣ Decoradores concretos (fuego, hielo, veneno…)

<?php

class HechizoFuego extends HechizoDecorator {
    public function lanzar(string $enemigo): void {
        parent::lanzar($enemigo);
        echo "🔥 ¡El fuego envuelve a $enemigo!\n";
    }
}

class HechizoHielo extends HechizoDecorator {
    public function lanzar(string $enemigo): void {
        parent::lanzar($enemigo);
        echo "❄️ $enemigo queda congelado en hielo.\n";
    }
}

class HechizoVeneno extends HechizoDecorator {
    public function lanzar(string $enemigo): void {
        parent::lanzar($enemigo);
        echo "☠️ $enemigo sufre daño por veneno.\n";
    }
}

5️⃣ Uso del patrón Decorator

<?php

$mago = new HechizoBasico("Gandalf");

// Hechizo simple
$mago->lanzar("Orco Malvado");

// Hechizo con fuego
$magoFuego = new HechizoFuego($mago);
$magoFuego->lanzar("Orco Malvado");

// Hechizo con fuego y veneno
$magoFuegoVeneno = new HechizoVeneno(new HechizoFuego($mago));
$magoFuegoVeneno->lanzar("Orco Malvado");

// Hechizo con fuego, hielo y veneno (full combo)
$magoFull = new HechizoVeneno(new HechizoHielo(new HechizoFuego($mago)));
$magoFull->lanzar("Orco Malvado");

🏁 Conclusión

El patrón Decorator nos permite:

  • ✨ Añadir comportamientos de forma dinámica sin tocar la clase original.
  • 🔄 Combinar efectos fácilmente (fuego + hielo + veneno).
  • 📐 Cumplir con el principio abierto/cerrado (OCP).

La próxima vez que quieras extender funcionalidades sin multiplicar subclases, recuerda al Mago que potencia sus hechizos con Decorator 🧙‍♂️🔥❄️☠️.

Con esto completamos otro capítulo de nuestra saga de patrones.
Que tus clases sean abiertas a la extensión y cerradas a los bugs 🧙‍♂️✨.