Patrón de cadena de responsabilidad

El patrón de cadena de responsabilidad (Chain of Responsibility Pattern) crea una cadena de objetos receptores para la solicitud. Este patrón desacopla al emisor y al receptor de la solicitud según el tipo de solicitud. Este tipo de patrón de diseño pertenece a los patrones de comportamiento.

El patrón de cadena de responsabilidad conecta múltiples manejadores (objetos de procesamiento) en una estructura de cadena, haciendo que la solicitud se transmita a lo largo de la cadena hasta que un manejador la procese.

El patrón de cadena de responsabilidad permite que múltiples objetos tengan la oportunidad de procesar la solicitud, evitando así el acoplamiento entre el emisor y el receptor de la solicitud. Estos objetos se conectan en una cadena y la solicitud se transmite a lo largo de la cadena.

Introducción

Intención

Permite transmitir la solicitud a lo largo de la cadena de manejadores hasta que la solicitud sea procesada.

Problemas principales que resuelve

  • Desacoplar al emisor y al receptor de la solicitud, de modo que múltiples objetos tengan la posibilidad de recibir la solicitud y el emisor no necesite saber qué objeto la manejará.

Escenarios de uso

  • Cuando hay múltiples objetos que pueden procesar la solicitud y cuál de ellos la procesa se decide en tiempo de ejecución.
  • Cuando se necesita enviar una solicitud a uno de varios objetos sin querer especificar explícitamente el receptor.

Forma de implementación

  • Definir la interfaz del manejador: Todos los manejadores deben implementar la misma interfaz.
  • Crear manejadores concretos: Clases concretas que implementan la interfaz, contienen la lógica de procesamiento de la solicitud y una referencia al siguiente manejador en la cadena.

Código clave

  • Interfaz Handler: Define un método para procesar la solicitud.
  • Clase ConcreteHandler: Implementa la interfaz Handler, contiene la lógica de procesamiento de la solicitud y una referencia al siguiente manejador.

Ejemplos de aplicación

  1. Pasar la flor al son del tambor: Comportamiento de pasar en el juego hasta que la música se detenga.
  2. Propagación de eventos: En JavaScript, el evento comienza desde el elemento más específico y se propaga hacia arriba nivel por nivel.
  3. Servidor web: Como Apache Tomcat que maneja la codificación de caracteres, los interceptores de Struts2 y los filtros de Servlet.

Ventajas

  1. Reducción del acoplamiento: Desacopla al emisor y al receptor.
  2. Simplificación de objetos: Los objetos no necesitan conocer la estructura de la cadena.
  3. Flexibilidad: Al cambiar los miembros o el orden de la cadena, se pueden agregar o eliminar responsabilidades dinámicamente.
  4. Fácil de extender: Es muy conveniente añadir nuevas clases de procesamiento de solicitudes.

Desventajas

  1. Solicitud no procesada: No se garantiza que la solicitud sea recibida por algún manejador de la cadena.
  2. Impacto en el rendimiento: Puede afectar el rendimiento del sistema, es difícil de depurar y puede provocar llamadas cíclicas.
  3. Difícil de observar: Las características en tiempo de ejecución no son evidentes, lo que puede dificultar la depuración.

Recomendaciones de uso

  • Al procesar solicitudes, si hay varios manejadores potenciales, considere usar el patrón de cadena de responsabilidad.
  • Asegúrese de que cada manejador de la cadena sepa claramente cómo transmitir la solicitud al siguiente eslabón de la cadena.

Precauciones

  • En el desarrollo Java Web, el patrón de cadena de responsabilidad tiene amplias aplicaciones, como cadenas de filtros, interceptores, etc.

Estructura

Involucra principalmente los siguientes roles clave:

  • Manejador abstracto (Handler):

    • Define una interfaz para procesar solicitudes, que generalmente incluye un método para procesar la solicitud (comohandleRequest) y una referencia al siguiente manejador (sucesor).
  • Manejador concreto (ConcreteHandler):

    • Implementa la interfaz del manejador abstracto y es responsable de procesar la solicitud. Si puede procesarla, la maneja directamente; de lo contrario, la transmite al siguiente manejador.
  • Cliente (Client):

    • Crea los objetos manejadores y los conecta en una cadena de responsabilidad. Normalmente, el cliente solo necesita enviar la solicitud al primer manejador de la cadena, sin preocuparse por el proceso específico de manejo de la solicitud.

Implementación

Creamos la clase abstractaAbstractLogger, con niveles detallados de registro. Luego creamos tres tipos de registradores, todos extiendenAbstractLogger. Cada registrador verifica si el nivel del mensaje es de su propio nivel; si lo es, lo imprime en consecuencia; de lo contrario, no lo imprime y pasa el mensaje al siguiente registrador.

责任链模式的 UML 图

Paso 1

Crear la clase registradora abstracta.

AbstractLogger.java

public abstract class AbstractLogger { public static int INFO = 1; public static int DEBUG = 2; public static int ERROR = 3; protected int level; //El siguiente elemento en la cadena de responsabilidad protected AbstractLogger nextLogger; public void setNextLogger(AbstractLogger nextLogger){ this.nextLogger = nextLogger; } public void logMessage(int level, String message){ if(this.level <= level){ write(message); } if(nextLogger !=null){ nextLogger.logMessage(level, message); } } abstract protected void write(String message); }

Paso 2

Crear clases concretas que extienden la clase registradora.

ConsoleLogger.java

public class ConsoleLogger extends AbstractLogger { public ConsoleLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("Standard Console::Logger: " + message); } }

ErrorLogger.java

public class ErrorLogger extends AbstractLogger { public ErrorLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("Error Console::Logger: " + message); } }

FileLogger.java

public class FileLogger extends AbstractLogger { public FileLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("File::Logger: " + message); } }

Paso 3

Crear diferentes tipos de registradores. Asignarles diferentes niveles de error y configurar el siguiente registrador en cada uno. El siguiente registrador en cada registrador representa una parte de la cadena.

ChainPatternDemo.java

public class ChainPatternDemo { private static AbstractLogger getChainOfLoggers(){ AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR); AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG); AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO); errorLogger.setNextLogger(fileLogger); fileLogger.setNextLogger(consoleLogger); return errorLogger; } public static void main(String[] args) { AbstractLogger loggerChain = getChainOfLoggers(); loggerChain.logMessage(AbstractLogger.INFO, "This is an information."); loggerChain.logMessage(AbstractLogger.DEBUG, "This is a debug level information."); loggerChain.logMessage(AbstractLogger.ERROR, "This is an error information."); } }

Paso 4

Ejecutar el programa y mostrar el resultado:

Standard Console::Logger: This is an information.
File::Logger: This is a debug level information.
Standard Console::Logger: This is a debug level information.
Error Console::Logger: This is an error information.
File::Logger: This is an error information.
Standard Console::Logger: This is an error information.
Otras extensiones