Problema
Crear un flujo de procesamiento complejo que sea fácilmente modificable sin cambiar el código cliente.
Propósito
Permite que múltiples objetos tengan la oportunidad de procesar una solicitud, pasándola a través de una cadena hasta que alguno la maneje. Desacopla el emisor del receptor y hace el flujo fácilmente configurable.
Concepto clave
Cadena configurable: Como middleware de Express - cada handler decide si procesa la request o la pasa al siguiente. Puedes reordenar, agregar o quitar handlers sin tocar el código cliente.
Casos de uso comunes
- Middleware en aplicaciones web
- Sistemas de validación con múltiples reglas
- Sistemas de autorización por niveles
- Procesamiento de eventos con múltiples handlers
- Filtros de contenido
- Sistemas de logging con diferentes niveles
¿Quién es quién en Chain of Responsibility?
Actor | Lo que realmente es | Ejemplo | Analogía |
---|---|---|---|
Handler | Interfaz que define cómo manejar requests | AuthenticationHandler - define authenticate() |
“Ventanilla” (interfaz de atención) |
ConcreteHandler | Manejadores que deciden si procesan o pasan | BasicAuthHandler , JWTAuthHandler |
Ventanilla 1, 2, 3 (cada una maneja ciertos trámites) |
Client | Envía el request, no sabe quién lo procesará | AuthenticationService - inicia la cadena |
Ciudadano con un trámite |
Diagrama
classDiagram
namespace ChainOfResponsibilityPattern {
class Handler {
<<abstract>>
-nextHandler: Handler
+setNext(handler) Handler
+handle(request) Response
+handleRequest(request) Response*
}
class ConcreteHandlerA {
+handleRequest(request) Response
}
class ConcreteHandlerB {
+handleRequest(request) Response
}
class ConcreteHandlerC {
+handleRequest(request) Response
}
class Client {
+makeRequest()
}
}
Handler <|-- ConcreteHandlerA
Handler <|-- ConcreteHandlerB
Handler <|-- ConcreteHandlerC
Handler --> Handler : nextHandler
Client --> Handler
Ejemplo práctico
classDiagram
namespace AuthenticationExample {
class AuthenticationHandler {
<<abstract>>
-nextHandler: AuthenticationHandler
+setNext(handler) AuthenticationHandler
+authenticate(request) AuthResult
+doAuthenticate(request) AuthResult*
}
class BasicAuthHandler {
+doAuthenticate(request) AuthResult
}
class JWTAuthHandler {
+doAuthenticate(request) AuthResult
}
class APIKeyAuthHandler {
+doAuthenticate(request) AuthResult
}
class OAuthHandler {
+doAuthenticate(request) AuthResult
}
class AuthenticationService {
-handlerChain: AuthenticationHandler
+authenticate(request) AuthResult
}
}
AuthenticationHandler <|-- BasicAuthHandler
AuthenticationHandler <|-- JWTAuthHandler
AuthenticationHandler <|-- APIKeyAuthHandler
AuthenticationHandler <|-- OAuthHandler
AuthenticationService --> AuthenticationHandler
Flujo de la cadena
sequenceDiagram
participant Client
participant HandlerA
participant HandlerB
participant HandlerC
Client->>HandlerA: handle(request)
alt HandlerA can handle
HandlerA-->>Client: response
else HandlerA cannot handle
HandlerA->>HandlerB: handle(request)
alt HandlerB can handle
HandlerB-->>HandlerA: response
HandlerA-->>Client: response
else HandlerB cannot handle
HandlerB->>HandlerC: handle(request)
HandlerC-->>HandlerB: response
HandlerB-->>HandlerA: response
HandlerA-->>Client: response
end
end
Ventajas
- Desacoplamiento: Emisor y receptor no se conocen directamente
- Flexibilidad: Fácil agregar, quitar o reordenar handlers
- Responsabilidad única: Cada handler maneja un tipo específico de request
- Configuración dinámica: La cadena puede modificarse en tiempo de ejecución
Desventajas
- Performance: Puede ser lento si la cadena es larga
- Debugging: Difícil rastrear qué handler procesó el request
- Garantías: No garantiza que el request será manejado
- Complejidad: Puede volverse complejo con muchos handlers
Cuándo usar
- Tienes un flujo de procesamiento que cambia frecuentemente
- Múltiples objetos pueden manejar el mismo tipo de solicitud
- Quieres configurar el orden de procesamiento dinámicamente
- Necesitas desacoplar el cliente de los procesadores específicos
Cuándo NO usar
- Solo hay una forma de procesar la solicitud
- Todos los handlers deben procesar la solicitud (usa Decorator)
- El orden no importa (usa Strategy)
- La performance es crítica y tienes muchos handlers