Chain of Responsibility Pattern

Chain of Responsibility Pattern creates a chain of receiver objects for a request. This pattern decouples the sender and receiver of the request based on the type of request. This type of design pattern belongs to behavioral patterns.

The Chain of Responsibility pattern connects multiple handlers (processing objects) in a chain structure, allowing the request to be passed along the chain until a handler processes it.

The Chain of Responsibility pattern allows multiple objects to have the opportunity to handle the request, thereby avoiding coupling between the sender and receiver of the request. These objects are connected into a chain, and the request is passed along the chain.

Introduction

intent

Allows the request to be passed along the chain of handlers until the request is handled.

Main problems solved

  • Decouples the sender and receiver of the request, allowing multiple objects to have the possibility of receiving the request, while the sender does not need to know which object will handle it.

Use Case

  • When there are multiple objects that can handle a request, and which specific object handles it is determined at runtime.
  • When you need to submit a request to one of multiple objects without explicitly specifying the receiver.

Implementation Approach

  • Define handler interface: All handlers must implement the same interface.
  • Create concrete handlers: Concrete classes implementing the interface, containing request handling logic and a reference to the next handler in the chain.

Key code

  • Handler interface: Define a method for handling requests.
  • ConcreteHandler class: Implements the Handler interface, containing request handling logic and a reference to the next handler.

Application example

  1. Hot Potato: Passing behavior in a game, until the music stops.
  2. Event Bubbling: In JavaScript, events start from the most specific element and propagate upward level by level.
  3. Web serverExamples include Apache Tomcat handling character encoding, Struts2 interceptors, and Servlet Filters.

Advantages

  1. Reduce Coupling: Decouple the sender and receiver.
  2. Simplify Objects: The object does not need to know the structure of the chain.
  3. Flexibility: Dynamically add or remove responsibilities by changing the members or order of the chain.
  4. Easy to Extend: It is convenient to add new request handler classes.

Disadvantages

  1. Request not handled: There is no guarantee that the request will be received by a handler in the chain.
  2. Performance Impact: May affect system performance, and debugging is difficult, may lead to circular calls.
  3. Difficult to Observe: Runtime characteristics are not obvious, which may hinder debugging.

Usage suggestions

  • When handling a request, if there are multiple potential handlers, consider using the Chain of Responsibility pattern.
  • Ensure that each handler in the chain clearly knows how to pass the request to the next link in the chain.

Notes

  • In Java Web development, the Chain of Responsibility pattern is widely used, such as filter chains, interceptors, etc.

Structure

It mainly involves the following core roles:

  • Abstract Handler:

    • Define an interface for handling requests, usually containing a method to handle the request (such ashandleRequest) and a reference to the next handler (successor).
  • Concrete Handler:

    • Implements the abstract handler interface and is responsible for handling requests. If it can handle the request, it handles it directly; otherwise, it passes the request to the next handler.
  • Client:

    • Create handler objects and connect them into a chain of responsibility. Usually, the client only needs to send the request to the first handler in the chain, without needing to care about the specific processing of the request.

Implementation

We create an abstract classAbstractLogger, with detailed log levels. Then we create three types of loggers, all extendingAbstractLogger. Each logger checks whether the message's level belongs to its own level; if so, it prints it accordingly; otherwise, it does not print and passes the message to the next logger.

责任链模式的 UML 图

Step 1

Create an abstract logger class.

AbstractLogger.java

public abstract class AbstractLogger { public static int INFO = 1; public static int DEBUG = 2; public static int ERROR = 3; protected int level; //The next element in the chain of responsibility 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); }

Step 2

Create entity classes that extend the logger class.

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); } }

Step 3

Create different types of loggers. Assign them different error levels, and set the next logger in each logger. The next logger in each logger represents part of the chain.

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."); } }

Step 4

Execute the program, output results:

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.
other extensions