🔗 El patrón Chain of Responsibility: La cadena de mando
El patrón Chain of Responsibility, es un patrón de comportamiento que tiene una misión clara: evitar acoplar el emisor de una petición a su receptor, dando a más de un objeto la oportunidad de manejarla.
El problema que resuelve es este:
¿Cómo puedes hacer que una solicitud sea atendida por el objeto adecuado sin que el emisor sepa quién es, y sin tener un if/else gigante y monstruoso?
La respuesta es encadenar los objetos receptores y pasar la solicitud a lo largo de la cadena hasta que un objeto la maneje.

🔗 1. Un ejemplo sencillo: El soporte técnico
Imagina que llamas a tu compañía de internet porque no te va el WiFi (La petición):
- Nivel 1 (robot): Te atiende una máquina. “¿Ha probado a reiniciar?”. Si no funciona, te pasa al siguiente nivel.
- Nivel 2 (operador humano): Te atiende una persona. Revisa tu factura. Si es un problema técnico complejo, te pasa al siguiente nivel.
- Nivel 3 (ingeniero): Te atiende un técnico especializado. Este sí sabe qué cable tocar.
Concepto (analogía) | Rol en Chain of Responsibility | Tarea principal |
|---|---|---|
Tú (cliente) | Cliente | Inicia la petición sin saber quién la resolverá finalmente. |
Robot / Operador / Ingeniero | Manejadores concretos | Deciden si procesan la petición o se la pasan al siguiente compañero. |
🛠️ 2. Los tres pilares del patrón Chain of Responsibility
Para montar esta cadena, necesitamos tres piezas clave:
1. El manejador (handler)
- Define una interfaz para manejar las peticiones.
- Opcionalmente, implementa el enlace al siguiente manejador (el puntero
next).
2. Manejadores concretos
- Son los que hacen el trabajo real.
- Comprueban si pueden manejar la petición. Si pueden, lo hacen. Si no, la pasan al siguiente.
3. El cliente
- Inicia la petición en un objeto de la cadena (normalmente el primero).
🤔 1. ¿Qué pasa si la petición llega al final de la cadena y nadie la ha atendido?
💾 3. El ejemplo clásico: Middleware en web
Un ejemplo muy común son los Middlewares en frameworks web (como Laravel o Express). Cuando llega una petición HTTP:
- Middleware de Auth: ¿Está logueado? Si no, error. Si sí, pasa al siguiente.
- Middleware de Validación: ¿Los datos son correctos? Si no, error. Si sí, pasa al siguiente.
- Controlador: Procesa la petición y devuelve la respuesta.
🤔 2. ¿Cuál es la principal ventaja de este patrón respecto a un switch/case gigante?
✅ 4. ¿Por qué usarlo?
- Desacoplamiento: El emisor no necesita conocer la cadena completa ni quién resolverá su problema.
- Flexibilidad: Puedes añadir o quitar eslabones de la cadena dinámicamente.
- Principio de responsabilidad única: Cada clase se encarga de una sola cosa (o de pasar la bola).
❌ 5. Desventaja a considerar
- Recepción no garantizada: Como hemos dicho en el test, la petición podría recorrer toda la cadena y caerse por el precipicio final sin ser atendida. Debes tener cuidado con eso.
🤔 3. ¿Es obligatorio que todos los manejadores pasen la petición al siguiente?
💡 6. Conclusión
El Chain of Responsibility es ideal para flujos de trabajo secuenciales donde varios objetos pueden tener algo que decir. Convierte un árbol de decisiones complejo en una lista ordenada y limpia de objetos que colaboran (o se pasan el marrón) entre sí.
🧠 7. Ejemplo práctico en PHP
Vamos a implementar el sistema de soporte técnico del que hablábamos.
<?php
// 🔹 1. La Interfaz del Manejador (Handler)
abstract class SoporteHandler
{
protected ?SoporteHandler $siguiente = null;
public function setSiguiente(SoporteHandler $handler): SoporteHandler
{
$this->siguiente = $handler;
// Devolvemos el handler para poder encadenar: $a->setSiguiente($b)->setSiguiente($c)
return $handler;
}
public function manejar(string $problema): ?string
{
if ($this->siguiente) {
return $this->siguiente->manejar($problema);
}
return null; // Nadie pudo atenderlo
}
}
// 🔹 2. Manejadores Concretos
class Robot extends SoporteHandler
{
public function manejar(string $problema): ?string
{
if ($problema === 'basico') {
return "🤖 Robot: He reiniciado tu router. ¿Funciona? (Problema resuelto)";
}
echo "🤖 Robot: No sé resolver esto, paso al humano...\n";
return parent::manejar($problema);
}
}
class OperadorHumano extends SoporteHandler
{
public function manejar(string $problema): ?string
{
if ($problema === 'facturacion') {
return "👨💼 Operador: He corregido tu factura. (Problema resuelto)";
}
echo "👨💼 Operador: Esto es muy técnico, paso al ingeniero...\n";
return parent::manejar($problema);
}
}
class Ingeniero extends SoporteHandler
{
public function manejar(string $problema): ?string
{
if ($problema === 'fuego') {
return "👷 Ingeniero: He apagado el fuego del servidor. (Problema resuelto)";
}
echo "👷 Ingeniero: Ni yo sé qué pasa...\n";
return parent::manejar($problema);
}
}
// 🔹 3. Cliente (Configuración y Uso)
$robot = new Robot();
$operador = new OperadorHumano();
$ingeniero = new Ingeniero();
// Montamos la cadena: Robot -> Operador -> Ingeniero
$robot->setSiguiente($operador)->setSiguiente($ingeniero);
// Probamos
echo "--- Intento 1: Problema Básico ---\n";
echo $robot->manejar('basico') . "\n\n";
echo "--- Intento 2: Problema de Facturación ---\n";
echo $robot->manejar('facturacion') . "\n\n";
echo "--- Intento 3: Fuego en el servidor ---\n";
echo $robot->manejar('fuego') . "\n\n";
echo "--- Intento 4: Alienígenas ---\n";
$resultado = $robot->manejar('alienigenas');
if ($resultado === null) {
echo "❌ Nadie pudo resolver el problema de los alienígenas.\n";
}
// 🖥️ Salida:
// --- Intento 1: Problema Básico ---
// 🤖 Robot: He reiniciado tu router. ¿Funciona? (Problema resuelto)
//
// --- Intento 2: Problema de Facturación ---
// 🤖 Robot: No sé resolver esto, paso al humano...
// 👨💼 Operador: He corregido tu factura. (Problema resuelto)
//
// --- Intento 3: Fuego en el servidor ---
// 🤖 Robot: No sé resolver esto, paso al humano...
// 👨💼 Operador: Esto es muy técnico, paso al ingeniero...
// 👷 Ingeniero: He apagado el fuego del servidor. (Problema resuelto)
//
// --- Intento 4: Alienígenas ---
// 🤖 Robot: No sé resolver esto, paso al humano...
// 👨💼 Operador: Esto es muy técnico, paso al ingeniero...
// 👷 Ingeniero: Ni yo sé qué pasa...
// ❌ Nadie pudo resolver el problema de los alienígenas.
EA, ¡saluditos y nos vemos en los bares! 🍻