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.