Responsibility chain model

Posted by snascendi on Tue, 18 Jan 2022 09:12:54 +0100

definition

The Chain of Responsibility Pattern creates a chain of receiver objects for requests. This mode gives the type of request and decouples the sender and receiver of the request. This type of design pattern belongs to behavioral pattern. In this pattern, each recipient usually contains a reference to another recipient. If an object cannot process the request, it passes the same request to the next recipient, and so on.

introduce

Intention: avoid coupling the sender and receiver of the request, make it possible for multiple objects to receive the request, connect these objects into a chain, and pass the request along the chain until an object processes it.

Main solution: the handler in the responsibility chain is responsible for processing the request. The customer only needs to send the request to the responsibility chain without paying attention to the processing details of the request and the transmission of the request. Therefore, the responsibility chain decouples the sender of the request from the handler of the request.

When to use: filter many channels when processing messages.

How to solve this problem: all intercepted classes implement a unified interface.

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

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

Advantages: 1. Reduce the coupling degree. It decouples the sender and receiver of the request. 2. Simplifies objects. So that the object does not need to know the structure of the chain. 3. Enhance the flexibility of assigning responsibilities to objects. By changing the members in the chain or transferring their order, it is allowed to dynamically add or delete responsibilities. 4. It is convenient to add new request processing classes.

Disadvantages: 1. There is no guarantee that the request will be received. 2. The system performance will be affected to some extent, and it is inconvenient to debug the code, which may cause circular calls. 3. It may not be easy to observe the characteristics of the runtime, which hinders debugging.

Usage scenario: 1. Multiple objects can process the same request. The specific object to process the request is automatically determined by the runtime. 2. Submit a request to one of multiple objects without explicitly specifying the recipient. 3. You can dynamically specify a set of objects to handle requests.

realization

We create the abstract class} AbstractLogger with detailed logging level. Then we create three types of recorders, all of which extend {AbstractLogger. Whether the message level of each recorder belongs to its own level. If so, it will be printed accordingly. Otherwise, it will not be printed and the message will be transmitted to the next recorder.

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

 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 recorders. Give them different error levels and set the next recorder in each recorder. The next recorder in each recorder 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 and output the 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.

Topics: Java