In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article introduces the relevant knowledge of "detailed explanation of the principle of java design pattern responsibility chain pattern". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!
Catalogue
Introduction
Chain of responsibility pattern definition
Class diagram
Role
Core
Sample code
1. Abstraction of request handlers
2. Abstraction of request handlers
3. The establishment of responsibility chain.
A case of asking for leave by realizing the chain of responsibility
Case class diagram
Expandability
Pure and impure chain of responsibility model
Pure chain of responsibility model
Impure chain of responsibility model
Main advantages of chain of responsibility model
The main shortcomings of the chain of responsibility model
Applicable scenario
Simulation implementation of filter Mechanism in Tomcat
The running process is as follows
Analyze the responsibility chain pattern in Tomcat filter
Introduction
Take the leave request process as an example, the leave request process for ordinary employees of a company is simplified as follows:
When an ordinary employee initiates an application for leave, it only needs to be approved by the supervisor when the number of days of leave is less than 3 days; when the number of days of leave is more than 3 days, the supervisor needs to submit it to the manager for approval, and the manager's approval is approved. if the number of days of leave is more than 7 days, it needs to be further submitted to the general manager for approval.
The simplified code to implement this leave process using if-else is as follows:
Public class LeaveApproval {public boolean process (String request, int number) {boolean result = handleByDirector (request); / / Supervisor handles if (result = = false) {/ / Supervisor disapproves return false;} else if (number)
< 3) { // 主管批准且天数小于 3 return true; } result = handleByManager(request); // 准管批准且天数大于等于 3,提交给经理处理 if (result == false) { // 经理不批准 return false; } else if (number < 7) { // 经理批准且天数小于 7 return true; } result = handleByTopManager(request); // 经理批准且天数大于等于 7,提交给总经理处理 if (result == false) { // 总经理不批准 return false; } return true; // 总经理最后批准 } private boolean handleByDirector(String request) { // 主管处理该请假申请 if(request.length()>10) return false; return true;} private boolean handleByManager (String request) {/ / Manager processes the leave application if (request.length () > 5) return false; return true;} private boolean handleByTopManager (String request) {/ / General Manager processes the leave application if (request.length () > 3) return false Return true;}}
The problem seems simple and can be solved by dividing five into two, but there are several problems with the plan:
The LeaveApproval class is relatively large, and the examination and approval methods of each superior are concentrated in this class, which violates the "single responsibility principle" and is difficult to test and maintain.
When the leave process needs to be modified, for example, if the number of days is increased by more than 30 days, it needs to be submitted to the chairman for processing, and the source code must be modified (and strictly tested again), which violates the "opening and closing principle".
The process lacks flexibility and cannot be modified after the process is determined (unless the source code is modified), and the client cannot customize the process.
The above problems can be solved by using the chain of responsibility model.
Chain of responsibility pattern definition
Avoid coupling the request sender and receiver, making it possible for multiple objects to receive the request, connect the objects into a chain, and pass the request along the chain until an object processes it. The responsibility chain model is a kind of object behavior model.
The chain of responsibility can be a straight line, a ring, or a tree structure, and the most common chain of responsibility is linear, that is, passing requests along an one-way chain, as shown in the following figure. Every object on the chain is the request handler, and the responsibility chain pattern can organize the request handler into a chain and let the request pass along the chain, and the processor on the chain will process the request accordingly. In this process, the client actually needs not to care about the processing details of the request and the transmission of the request, but only needs to send the request to the chain, thus decoupling the request sender and the request processor.
The key to the understanding of the chain of responsibility lies in the understanding of the chain, which includes the following two points:
A chain is a collection of a series of nodes. In the responsibility chain, a node is essentially the handler of the request.
Each node of the chain can be split and reassembled flexibly. In the responsibility chain, it is essentially the decoupling of the request sender and the request processor.
Class diagram
Role
As we can see from the structure diagram of the chain of responsibility pattern, there can be multiple specific request handlers, and all request handlers have the same interface (inheriting from the same abstract class). The chain of responsibility model mainly includes the following two roles
Handler (Abstract Handler): an interface for handling requests, which is generally designed as an abstract class with abstract request processing methods, so as to facilitate inheritance by different concrete handlers, thus implementing specific request processing methods. In addition, since the next of each request handler is a handler, the abstract handler itself contains a successor of its own as its reference to the next, in order to chain the handler into a chain
ConcreteHandler (concrete handlers): it is a subclass of abstract handlers that can handle user requests. Abstract request handling methods defined in abstract handlers are implemented in the concrete handler class. Before processing the request, it needs to be judged to see if it has the appropriate processing authority. If it is possible to process the request, process it, otherwise forward the request to the successor. The next object in the chain can be accessed among the specific processors for forwarding the request.
In the chain of responsibility pattern, a request processing chain is formed by the reference of each request handler object to its subordinate. The request will be passed on this chain until one of the request handlers on the chain can process the request. In fact, the client that made the request does not know which request handler on the chain will process the request, which allows the system to dynamically reorganize the chain and assign responsibilities without affecting the client.
Core
The key to implementing the chain of responsibility pattern is to aggregate itself (holding references of its own type) in the abstract class Handler and determine whether it can handle requests in the handleRequest method. If the current processor cannot process it, set its successor and pass it down until the request is processed.
Sample code 1. Abstraction of request handlers
The core of the chain of responsibility model is the abstraction of the request handler. In the process of implementation, abstract processors are generally set as abstract classes
The typical implementation code is as follows:
Public abstract class Handler {/ / protected: maintain the reference to the next family protected Handler successor; public void setSuccessor (Handler successor) {this.successor=successor;} public abstract void handleRequest (String request);}
In the above code, the abstract handler class defines a reference to the next family (which is usually decorated with protected) to forward the request to the next home, forming a request processing chain. At the same time, abstract request handling methods are declared in the abstract handler class so that they can be implemented by subclasses.
2. Abstraction of request handlers
The concrete handler is a subclass of the abstract handler. The typical code of the concrete handler class is as follows:
Public class ConcreteHandler extends Handler {public void handleRequest (String request) {if (request fulfillment) {/ / process request} else {this.successor.handleRequest (request); / / forward request}}
In the concrete processing class, the request is judged in order to be processed accordingly, so it generally has two major functions:
Handle requests, and different concrete processors implement abstract request processing methods handleRequest () in different forms.
Forward the request. If the request exceeds the permissions of the current handler class, you can forward the request to the next
3. The establishment of responsibility chain.
It should be noted that the chain of responsibility pattern does not create a chain of responsibility, and the creation of the chain of responsibility must be done by other parts of the system, generally by the client that uses the chain of responsibility. The chain of responsibility pattern reduces the coupling between the request sender and the request handler, thus giving multiple request handlers the opportunity to process the request.
A case of asking for leave by realizing the chain of responsibility
Leave information category, including the name of the applicant and the number of days taken
@ Data@AllArgsConstructorpublic class LeaveRequest {name of String name;// person on leave Integer num;// days off}
The abstract handler class Handler, which maintains a nextHandler property that is a reference to the next handler of the current handler; declares the abstract method process
/ / Abstract handler @ Datapublic abstract class Handler {/ / maintain itself to refer to protected Handler handler; / / the name of the current handler protected String name; / / pass the name of the current handler public Handler (String name) {this.name=name;} / / Abstract method to process the request for leave public abstract Boolean process (LeaveRequest leaveRequest);}
Three concrete processing classes, which implement the process method of the abstract processing class respectively.
Supervisor:
Public class Director extends Handler {public Director (String name) {super (name);} / process the request for leave @ Override public Boolean process (LeaveRequest leaveRequest) {/ / if the random number is greater than 3, approve the request boolean result = (new Random (). NextInt (10)) > 3 String log = "Supervisor:% s, approval:% s request for leave, leave days:% d, approval result:% s"; System.out.println (String.format (log,name,leaveRequest.getName (), leaveRequest.getNum (), result==true?) Pass ":" No "); if (result) / / approve {/ / if the number of days of leave exceeds 3 days, then submit it to the higher level for further approval of if (leaveRequest.num > 3) {return nextHandler.process (leaveRequest);} / / the number of days of leave is less than 3, and the approval is approved by return true } / / failed approval return false;}}
Manager
Public class Manager extends Handler {public Manager (String name) {super (name);} / process request for leave @ Override public Boolean process (LeaveRequest leaveRequest) {boolean result = (new Random () .nextInt (10)) > 3 / / approval is granted if the random number is greater than 3, otherwise String log = "Manager:% s, approval:% s, leave days:% d, approval result:% s"; System.out.println (String.format (log,name,leaveRequest.getName (), leaveRequest.getNum (), result==true?) Approve ":" fail "); if (result) {/ / leave too many days, still need to submit to a higher level for approval of if (leaveRequest.getNum () > 7) {return nextHandler.process (leaveRequest);} / / otherwise directly through return true } return false;}}
General manager
Public class TopManager extends Handler {public TopManager (String name) {super (name);} @ Override public Boolean process (LeaveRequest leaveRequest) {/ / approval request boolean result = (new Random (). NextInt (10)) > 3 if the random number is greater than 3; String log = "General Manager:% s, approval:% s, leave days:% d, approval result:% s" System.out.println (String.format (log,name,leaveRequest.getName (), leaveRequest.getNum (), result==true?) Through ":"); if (result) / / approve {/ / there are only three processors by default, but if you add them later, you also need to leave a location / / if you continue to add if (nextHandlerprocessors null) {return nextHandler.process (leaveRequest);} return true } / / failed approval return false;}}
Processor chain class:
/ / processor chain public class HandlerChain {/ / maintains the first processor private Handler director=new Director ("small hoax"); / / by default there are three processors public HandlerChain () {/ / default there are three processor chains / / and these three processors have successively related director.nextHandler=new Manager ("kids"); director.nextHandler.nextHandler=new TopManager ("super big hoax") } / / add a processor into the collection public void addHandler (Handler handler) {Handler temp=director; while (temp.nextHandlercollection null) {temp=temp.nextHandler;} temp.nextHandler=handler } / / execute processor chain public void process (LeaveRequest leaveRequest) {/ / the first processor, if the processor is available, it does not need to be handed over to the next processor / / otherwise, continue to the next processor for processing director.process (leaveRequest);}}
Client test:
Public class Client {public static void main (String [] args) {LeaveRequest leaveRequest=new LeaveRequest ("big hoax", 10); HandlerChain handlerChain=new HandlerChain (); handlerChain.process (leaveRequest);}}
Case class diagram
Unlike the class diagram given above, I separate the calling processor chain processing business logic from the client through a processor chain class to further decouple
Expandability
If one more step is needed in the approval process at this time, it will be very convenient.
For example, if we need to add a God to do the final processing of the leave process, we just need to create a God processor to implement the processor abstract class, and then add it to the processor chain.
Public class God extends Handler {public God (String name) {super (name);} @ Override public Boolean process (LeaveRequest leaveRequest) {System.out.println ("God bless you, so you can have a holiday"); return true;}}
Client:
Public class Client {public static void main (String [] args) {LeaveRequest leaveRequest=new LeaveRequest ("big hoax", 10); HandlerChain handlerChain=new HandlerChain (); handlerChain.addHandler (new God (God)); handlerChain.process (leaveRequest);}}
If you want to continue to add processors, you need to reserve an interface in God's process method
This is very troublesome. I do not continue to decouple the method here. Interested partners can continue to try to decouple.
Pure and impure chain of responsibility model
A specific handler object can only choose one of two behaviors: either assume full responsibility or shift the responsibility to the next family. It is not allowed for a specific handler object to transfer the responsibility downward after assuming part or all of the responsibility.
A request must be received by a handler object, and there cannot be a situation in which a request is not processed by any processor object
Impure chain of responsibility model
Allow a request to be partially processed by a specific handler and then passed down
Or after a specific processor has processed a request, its subsequent processor can continue to process the request
And a request can eventually not be received by any processor object.
Main advantages of chain of responsibility model
The object only needs to know that the request will be processed, and the objects in the chain do not need to know the structure of the chain, and the client is responsible for the creation of the chain, which reduces the coupling degree of the system.
The request processing object only needs to maintain a reference to its successor, rather than its reference to all candidate handlers, which simplifies the interconnection of objects.
When assigning responsibilities to objects, the responsibility chain can give us more flexibility. We can dynamically add or delete the chain at run time, and change the responsibility of processing a request.
When adding a new specific request handler, you do not need to modify the original code, but only need to rebuild the chain on the client side, which is in line with the "opening and closing principle".
The main shortcomings of the chain of responsibility model
A request may not be processed because the chain of responsibilities is not configured correctly
For a long chain of responsibilities, the processing of requests may involve multiple processing objects, which will affect the performance of the system to a certain extent, and it is not convenient to debug.
It may be due to improper creation of the chain of responsibility, resulting in circular calls, causing the system to fall into a dead loop.
Applicable scenario
There are multiple objects that can process the same request, and the specific object that handles the request will be determined at run time. The client only needs to submit the request to the chain, regardless of who the requested object is and how it is handled.
Submit a request to one of multiple objects without explicitly specifying the recipient
A set of objects can be dynamically specified to handle requests, and the client can dynamically create a chain of responsibilities to process requests, and it can also change the order of processors in the chain.
Simulation implementation of filter Mechanism in Tomcat
Step 1: define the class Request that encapsulates the request and the class Response that encapsulates the result response
@ Data@AllArgsConstructor@NoArgsConstructorpublic class Reponse {private List data=new ArrayList (); public void addData (String data) {this.data.add (data);}} @ Data@NoArgsConstructor@AllArgsConstructorpublic class Request {private Object data;}
Step 2: define the interface Filter with filtering function. The specific filtering rules need to implement this interface.
/ * * define the interface Filter, which needs to be implemented for specific filtering rules. The last parameter added is: * fc.doFilter (request, response,fc) in the Main function. When performing this step, you can use three filtering rules to process strings according to the rule chain * / public interface Filter {void doFilter (Request request,Reponse reponse,FilterChain filterChain);}
Step 3: define specific filtering rules
Public class StuAgeFilter implements Filter {@ Override public void doFilter (Request request, Reponse reponse, FilterChain filterChain) {Stu stu = (Stu) request.getData (); if (stu.getName (). Contains ("hoax") {/ / the name does not meet the requirements reponse.addData ("name does not meet the requirements") } / / the name meets the requirements reponse.addData ("name meets the requirements"); filterChain.doFilter (request,reponse,filterChain);}} / / Student filter-filter out public class StuFilter implements Filter {@ Override public void doFilter (Request request, Reponse reponse, FilterChain filterChain) {Stu stu = (Stu) request.getData () If (stu.getAge () {System.out.println (x);});}}
The running process is as follows
Analyze the responsibility chain pattern in Tomcat filter
Servlet filters are Java classes that can be used for Servlet programming to intercept client requests before they access back-end resources and to process responses from the server before they are sent back to the client.
Servlet defines the source code of filter interface Filter and filter chain interface FilterChain as follows
Public interface Filter {public void init (FilterConfig filterConfig) throws ServletException; public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; public void destroy ();} public interface FilterChain {void doFilter (ServletRequest var1, ServletResponse var2) throws IOException, ServletException;}
The steps we take to customize a filter are:
1) write a filter class that implements the javax.servlet.Filter interface, as shown below
Public class MyFilter implements Filter {@ Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {/ / do some custom processing. System.out.println ("before executing the doFilter () method..."); chain.doFilter (request, response); / / pass the request to the next filter System.out.println ("after executing the doFilter () method...");} @ Override public void destroy () {} @ Override public void init (FilterConfig filterConfig) throws ServletException {}}
2) add the configuration of the filter to the web.xml file, such as intercepting all requests
MyFilter com.whirly.filter.MyFilter MyFilter / *
When starting Tomcat is our filter, it works. So how does the filter work?
Tomcat has a Pipeline Valve mechanism, which also uses the responsibility chain mode. A request will be transferred in Pipeline, and Pipeline will call the corresponding Valve to complete the specific logical processing.
One of the basic Valve is StandardWrapperValve, and one of its functions is to call ApplicationFilterFactory to generate Filter chain. The specific code is in the invoke method.
You need to complete the loading and initialization of the filter and generate the filter chain based on the configuration information before running the filter:
The filter is loaded in the configureContext method of the ContextConfig class, loading the relevant information of filter and filterMap respectively, and saving it in the context
The filter is initialized in the startInternal method of the StandardContext class, saved in filterConfigs and stored in the context
When the request flows to StandardWrapperValve, in the invoke method, a pair ApplicationFilterChain is created for each request based on the filter mapping configuration information, including the target Servlet and the corresponding filter chain, and the filter is executed by calling the doFilter method of the filter chain
The key code for StandardWrapperValve to call ApplicationFilterFactory to create a filter chain for the request and call the filter chain is as follows:
Final class StandardWrapperValve extends ValveBase {public final void invoke (Request request, Response response) throws IOException, ServletException {/ / omit other logic processing. / / call ApplicationFilterChain.createFilterChain () to create filter chain ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain (request, wrapper, servlet) If (servlet! = null & & filterChain! = null) {/ / omit} else if (request.isAsyncDispatching ()) {request.getAsyncContextInternal () .doInternalDispatch ();} else if (comet) {filterChain.doFilterEvent (request.getEvent ()) } else {/ / call the doFilter method of the filter chain to start filtering filterChain.doFilter (request.getRequest (), response.getResponse ());}
The key code of the filter chain ApplicationFilterChain is as follows. The filter chain is actually an ApplicationFilterConfig array.
Final class ApplicationFilterChain implements FilterChain, CometFilterChain {private ApplicationFilterConfig [] filters = new ApplicationFilterConfig [0]; / / filter chain private Servlet servlet = null; / / Target /... @ Override public void doFilter (ServletRequest request, ServletResponse response) throws IOException, ServletException {if (Globals.IS_SECURITY_ENABLED) {/ /...} else {internalDoFilter (request,response) / / call the internalDoFilter method}} private void internalDoFilter (ServletRequest request, ServletResponse response) throws IOException, ServletException {/ / Call the next filter if there is one if (pos < n) {/ / extract the current filter configuration from the filter array, and then the subscript increments 1 ApplicationFilterConfig filterConfig = filters [pos++]; Filter filter = null Try {filter = filterConfig.getFilter (); / / remove the filter object if (Globals.IS_SECURITY_ENABLED) {final ServletRequest req = request; final ServletResponse res = response; Principal principal = ((HttpServletRequest) req) .filter () Object [] args = new Object [] {req, res, this}; SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);} else {/ / call the doFilter of the filter to complete the filtering function of a filter filter.doFilter (request, response, this) } return / / it is important here that it will not repeat the following servlet.service (request, response)} / / after all the filters in the filter chain are executed. Call Servlet's service to complete the processing of the request if ((request instanceof HttpServletRequest) & & (response instanceof HttpServletResponse)) {if (Globals.IS_SECURITY_ENABLED) {} else {servlet.service (request, response) }} else {servlet.service (request, response);}} / / omitted.}
Filter
Public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println ("before executing the doFilter () method"); chain.doFilter (request, response); / / pass the request to the next filter System.out.println ("after executing the doFilter () method...");}
When the subscript is less than the filter array length n, the filter chain is not finished, so the current filter is extracted from the array, the filter's doFilter method is called to complete the filter processing, FilterChain's doFilter is called in the filter's doFilter, the filter chain is returned to ApplicationFilterChain, and the filter chain is judged according to whether the subscript is less than the array length. If the filter is not finished, the filter is fetched from the array and the doFilter method is called. So the filter chain here is strung together by nested recursion.
When all the filters are executed, the last time you enter the ApplicationFilterChain.doFilter method, pos < n is false. Instead of entering the if (pos < n), the following code is executed to determine (request instanceof HttpServletRequest) & & (response instanceof HttpServletResponse). If it is a http request, call servlet.service (request, response); to process the request.
After processing, reverse the stack along the order in which the filter is called, and execute the processing logic after chain.doFilter () in the filter respectively. It should be noted that there is a return;, at the end of the body of the if (pos < n) method, which ensures that only the last call into the ApplicationFilterChain.doFilter method can execute the subsequent servlet.service (request, response) method.
Draw a brief call stack as follows:
The ApplicationFilterChain class acts as an abstract handler, and the concrete handler role is played by each Filter
This is the end of the introduction of "detailed explanation of the principle of java Design pattern responsibility chain pattern". Thank you for your reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.