Patrón Strategy
En el desarrollo de software es común encontrarnos con clases que deben realizar una misma acción de diferentes maneras.
El problema aparece cuando esa lógica termina en un método lleno de if/else que se vuelve inmanejable al crecer el número de casos.
Hoy veremos cómo resolver ese problema con el patrón Strategy, usando un ejemplo: un guerrero 🧙♂️⚔️ que puede atacar de varias formas.
❌ El problema
Imaginemos un Guerrero que puede atacar con espada, arco o magia.
Una primera implementación ingenua podría ser así:
<?php
class Guerrero {
private string $nombre;
public function __construct(string $nombre) {
$this->nombre = $nombre;
}
public function atacar(string $tipo, string $enemigo): void {
if ($tipo === "espada") {
echo "⚔️ {$this->nombre} golpea a $enemigo con su espada!\n";
} elseif ($tipo === "arco") {
echo "🏹 {$this->nombre} dispara una flecha a $enemigo!\n";
} elseif ($tipo === "magia") {
echo "🔮 {$this->nombre} lanza un hechizo a $enemigo!\n";
} else {
echo "❓ {$this->nombre} no sabe cómo atacar de esa forma.\n";
}
}
}
// 👉 Uso
$guerrero = new Guerrero("Conan");
$guerrero->atacar("espada", "Orco Malvado");
$guerrero->atacar("arco", "Orco Malvado");
$guerrero->atacar("magia", "Orco Malvado");
🔎 Esto funciona, pero tiene problemas:
- Cada vez que añadimos un ataque, tenemos que modificar la clase
Guerrero. - La lógica se llena de
if/elseifdifíciles de mantener. - Viola el principio abierto/cerrado (OCP): la clase debería estar abierta a extensión pero cerrada a modificación.
✅ La solución: Patrón Strategy
El patrón Strategy propone encapsular cada comportamiento en una clase independiente que comparta una interfaz común.
Así, el Guerrero no necesita saber cómo se ejecuta cada ataque, solo delega a la estrategia correspondiente.
1️. Definimos la interfaz de estrategia
<?php
interface EstrategiaAtaque {
public function atacar(string $enemigo): void;
}2️. Creamos estrategias concretas
<?php
class AtaqueEspada implements EstrategiaAtaque {
public function atacar(string $enemigo): void {
echo "⚔️ Cortas a $enemigo con tu espada.\n";
}
}
class AtaqueArco implements EstrategiaAtaque {
public function atacar(string $enemigo): void {
echo "🏹 Disparas una flecha a $enemigo.\n";
}
}
class AtaqueMagia implements EstrategiaAtaque {
public function atacar(string $enemigo): void {
echo "🔮 Lanzas un poderoso hechizo contra $enemigo.\n";
}
}3️. Redefinimos el Guerrero
<?php
class Guerrero {
private string $nombre;
private EstrategiaAtaque $estrategia;
public function __construct(string $nombre, EstrategiaAtaque $estrategia) {
$this->nombre = $nombre;
$this->estrategia = $estrategia;
}
public function setEstrategia(EstrategiaAtaque $estrategia): void {
$this->estrategia = $estrategia;
}
public function atacar(string $enemigo): void {
echo "{$this->nombre} se prepara para atacar...\n";
$this->estrategia->atacar($enemigo);
}
}
4️. Uso del patrón Strategy
<?php
$guerrero = new Guerrero("Conan", new AtaqueEspada());
$enemigo = "Orco";
$guerrero->atacar($enemigo); // ⚔️ Espada
$guerrero->setEstrategia(new AtaqueArco());
$guerrero->atacar($enemigo); // 🏹 Arco
$guerrero->setEstrategia(new AtaqueMagia());
$guerrero->atacar($enemigo); // 🔮 Magia
🏁 Conclusión
El patrón Strategy nos permite:
- 🔄 Reemplazar condicionales complicados por clases simples y mantenibles.
- 🕹️ Cambiar comportamientos en tiempo de ejecución (Conan puede cambiar de arma en plena batalla).
- 📐 Respetar principios SOLID como OCP y SRP.
👉 La próxima vez que tengas varios algoritmos o comportamientos intercambiables, piensa si no es buen momento para aplicar Strategy.
Con esto termina nuestra misión de hoy.
Nos encontramos en la siguiente mazmorra… digo, en el próximo post 💾🧙♂️.