El patrón Chain of Responsibility (Cadena de Responsabilidad) desacopla al emisor de una solicitud de su receptor, permitiendo que más de un objeto tenga la oportunidad de manejar esa solicitud.
Esto se hace encadenando los objetos receptores y pasando la solicitud a lo largo de la cadena hasta que es manejada por algún objeto.
El patrón Chain of Responsibility resuelve varios problemas:
El patrón Chain of Responsibility propone las siguientes soluciones:
Para entender mejor el comportamiento tenemos un diagrama de secuencia:
El cliente envía la solicitud al receiver1, quién la atiende y la pasa al receiver2, quién la atiende y la pasa al receiver3.
Cada receiver podría “hacer algo” sobre la solicitud, no quiere decir que solo tenga que pasarla. Puede distribuirse la funcionalidad.
Este patrón es recomendable cuando:
Desacoplamiento: Reduce el acoplamiento entre emisores y receptores de solicitudes.
Flexibilidad en el Manejo de Solicitudes: Permite que varias entidades manejen la solicitud sin involucrar al emisor.
Responsabilidad Distribuida: Distribuye la responsabilidad entre varios objetos, en lugar de cargar a un solo objeto con toda la funcionalidad.
Rendimiento: La solicitud puede tener que pasar por varios manejadores antes de ser procesada, lo que puede afectar el rendimiento.
Complejidad en la Configuración: Configurar la cadena puede ser complejo y es propenso a errores.
Manejo No Garantizado: No hay garantía de que la solicitud sea manejada si ningún objeto en la cadena la puede procesar.
Debemos crear un sistema de soporte técnico automatizado para una empresa de software.
El sistema va a manejar problemas de soporte que pueden tener distintos niveles de complejidad, desde problemas básicos de usuario hasta problemas técnicos avanzados.
Necesitamos una manera eficiente de procesar y resolver las solicitudes de soporte, sin sobrecargar los niveles de soporte con problemas que no coinciden con su área de especialización.
No es eficiente ni práctico que cada nivel de soporte evalúe todas las solicitudes de manera independiente para determinar si pueden manejarlas.
El patrón Chain of Responsibility es ideal para este caso, porque nos permite pasar una solicitud de soporte a lo largo de una cadena de manejadores potenciales, hasta que uno de ellos maneje la solicitud.
Cada nivel de soporte en la cadena tiene la oportunidad de resolver la solicitud o pasarla al siguiente nivel.
Codificamos en Java lo que preparamos en el diagrama.
Creamos la clase SupportRequest con los niveles de soporte:
class SupportRequest {
private int level;
private String message;
public SupportRequest(int level, String message) {
this.level = level;
this.message = message;
}
public int getLevel() {
return level;
}
public String getMessage() {
return message;
}
}
class SupportLevel {
public static final int BASIC = 1;
public static final int INTERMEDIATE = 2;
public static final int ADVANCED = 3;
}
Definimos la clase abstract SupportHandler:
abstract class SupportHandler {
protected SupportHandler nextHandler;
public void setNextHandler(SupportHandler nextHandler) {
this.nextHandler = nextHandler;
}
public abstract void handleRequest(SupportRequest request);
}
Implementamos los handlers concretos (Receiver):
class BasicSupportHandler extends SupportHandler {
public void handleRequest(SupportRequest request) {
if (request.getLevel() <= SupportLevel.BASIC) {
System.out.println("Basic support handling request: " + request.getMessage());
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
class IntermediateSupportHandler extends SupportHandler {
public void handleRequest(SupportRequest request) {
if (request.getLevel() == SupportLevel.INTERMEDIATE) {
System.out.println("Intermediate support handling request: " + request.getMessage());
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
class AdvancedSupportHandler extends SupportHandler {
public void handleRequest(SupportRequest request) {
if (request.getLevel() >= SupportLevel.ADVANCED) {
System.out.println("Advanced support handling request: " + request.getMessage());
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
El cliente:
public class Client {
public static void main(String[] args) {
SupportHandler basic = new BasicSupportHandler();
SupportHandler intermediate = new IntermediateSupportHandler();
SupportHandler advanced = new AdvancedSupportHandler();
basic.setNextHandler(intermediate);
intermediate.setNextHandler(advanced);
SupportRequest request = new SupportRequest(SupportLevel.BASIC, "I can't log in.");
basic.handleRequest(request);
request = new SupportRequest(SupportLevel.INTERMEDIATE, "My application is not working.");
basic.handleRequest(request);
request = new SupportRequest(SupportLevel.ADVANCED, "Database connection failed.");
basic.handleRequest(request);
}
}
Los participantes que vimos antes son: Handler, Receiver, Client:
En este ejemplo podemos ver un clásico ejemplo del patrón.
El Cliente inicia una solicitud de soporte técnico que se procesa a través de una cadena de manejadores (handlers), arrancando con soporte básico y moviéndose a niveles más avanzados, si es necesario.
Cada manejador tiene la oportunidad de tratar la solicitud o pasarla a lo largo de la cadena.
Se puede usar combinado con el patrón Composite, donde los padres de los componentes pueden actuar como sucesores.