chain of responsibility model


As the name suggests, the Chain of Responsibility Pattern creates a chain of receiver objects for requests. This pattern gives the type of request and decouples the sender and receiver of the request. This type of design pattern is a behavioral pattern.


In this pattern, typically each receiver contains a reference to another receiver. If an object cannot handle the request, it passes the same request to the next recipient, and so on.

Introduction

Intent: Avoid the request sender and receiver to be coupled together, make it possible for multiple objects to receive requests, and connect these objects into a chain, And the request is passed along this chain until an object handles it.

Main solution: The processor on the responsibility chain is responsible for processing the request. The customer only needs to send the request to the responsibility chain, and does not need to care about the request processing details and request delivery, so The chain of responsibility decouples the request sender from the request handler.

When to use: To filter many channels when processing messages.

How to solve:The intercepted classes all implement the unified interface.

Key code: Handler aggregates itself, and determines whether it is appropriate in HanleRequest. If the conditions are not met, it will be passed down, and set before passing it to whom.

Application examples: 1. "Drumming and passing flowers" in Dream of Red Mansions. 2. Event bubbling in JS. 3. The processing of Encoding by Apache Tomcat in JAVA WEB, the interceptor of Struts2, and the Filter of jsp servlet.

Advantages: 1. Reduce coupling. It decouples the sender and receiver of the request. 2. Simplified objects. The object does not need to know the structure of the chain. 3. Enhance the flexibility of assigning responsibilities to objects. Allows dynamic addition or deletion of responsibilities by changing members within the chain or moving their order. 4. It is very convenient to add new request processing classes.

Disadvantages: 1. There is no guarantee that the request will be received. 2. System performance will be affected to a certain extent, and it is inconvenient to debug the code, which may cause loop calls. 3. It may be difficult to observe runtime characteristics, which hinders debugging.

Usage scenarios: 1. Multiple objects can handle the same request. The specific object that handles the request is automatically determined at runtime. 2. Submit a request to one of multiple objects without explicitly specifying the recipient. 3. A group of objects can be dynamically designated to handle requests.

Note: Encountered many applications in JAVA WEB.

Implementation

We create abstract class AbstractLogger, with detailed logging level. Then we create three types of loggers, all extending AbstractLogger. Whether the level of each logger message belongs to its own level, if so, it will be printed accordingly, otherwise it will not be printed and the message will be passed to the next logger.

chain_pattern_uml_diagram.jpg

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;

   //责任链中的下一个元素
   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 an entity class that extends 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. Give them different error levels and within each logger set the next 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 an debug level information.");

      loggerChain.logMessage(AbstractLogger.ERROR, 
         "This is an error information.");
   }
}

Step 4

Verify the output.

Standard Console::Logger: This is an information.
File::Logger: This is an debug level information.
Standard Console::Logger: This is an 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.