Follow the public account JavaStorm easy to read and receive the latest dry goods
Chain of Responsibility model
define
Chain of Responsibility Pattern: Avoid coupling the sender of a request with the receiver, make it possible for multiple objects to receive a request, connect these objects into a Chain, and pass the request along the Chain until an object handles it. The responsibility chain pattern is an object behavior pattern.
In common sense, we try to pass the buck on something, we go to A, A kicks the ball, says it’s none of my business, goes to B, and we go to B, and B says, it’s none of my business, go to C, and that’s it, we get kicked around, that’s the idea of the chain of responsibility model.
role
Handler (Abstract Handler) : It defines the interface for handling requests. It is typically designed as an abstract class, holding a reference to a Handler and passing it on to the next Handler if it cannot handle the request itself. Therefore, an object of type abstract handler is defined in the abstract handler as its reference to the next handler. With this reference, the handler can be linked into a chain.
ConcreteHandler: It is a subclass of abstract handler, which can process user requests. In the concrete handler class, the abstract request processing method defined in the abstract handler is realized. Before processing the request, we need to judge whether there is a corresponding processing permission. The next object in the chain can be accessed in the concrete handler for forwarding the request.
Pure chain of responsibility model
- A specific processing object can choose only one of the processors: it can either assume full responsibility or pass the processing responsibility to the next processor, without allowing one processor to process part of it and then pass it on.
- A request must be received by a handler object, and no request can be processed by any handler object
Impure chain of responsibility model
- Allows a request to be processed by a specific handler before being passed down
- After a specific handler has processed a request, it can continue processing the request.
- A request can end up not being processed by any processor.
Code sample
Pure chain of responsibility model
Now simulate a scenario request that can be processed if the number matches an odd number, specifies a number, some lower limit, and so on, or fails to be processed otherwise.
First, we define the abstract handler, along with a processing method template that defines which requests can be processed and which can’t be passed to Next.
package com.zero.headfirst.chain.support;
import java.util.Objects;
/** * Abstract handler: Defines an abstract request handler that holds a reference to an abstract handler used to generate a processing chain */
public abstract class AbstractSupport {
private String name;
private AbstractSupport next;
/** * defines the algorithm template for processing requests */
public final void process(NumberData numberData) {
if (resolve(numberData)) {
done(numberData);
} else if (Objects.nonNull(next)) {
next.process(numberData);
} else{ fail(numberData); }}/** * cannot handle *@param numberData
*/
protected void fail(NumberData numberData) {
System.out.println(numberData + " cannot be resolved.");
}
/** * Successfully processed *@param numberData
*/
protected void done(NumberData numberData) {
System.out.println(numberData + " is resolved by " + this + ".");
}
/** * subclass overrides * to see if it can handle@param numberData
* @return* /
protected abstract boolean resolve(NumberData numberData);
public AbstractSupport setNext(AbstractSupport next) {
this.next = next;
return next;
}
public AbstractSupport(String name) {
this.name = name;
}
@Override
public String toString(a) {
return "AbstractSupport{" +
"name='" + name + '\' ' +
'} '; }}Copy the code
Then define our specific handler, NoSupport is a class that never resolves, just passes the buck.
package com.zero.headfirst.chain.support;
/** * is never resolved, return false */
public class NoSupport extends AbstractSupport {
public NoSupport(String name) {
super(name);
}
@Override
protected boolean resolve(NumberData numberData) {
return false; }}Copy the code
LimitSupport resolves requests for a specified scope
package com.zero.headfirst.chain.support;
/** * resolves the specified interval */
public class LimitSupport extends AbstractSupport {
private Integer limit;
public LimitSupport(String name, Integer limit) {
super(name);
this.limit = limit;
}
@Override
protected boolean resolve(NumberData numberData) {
returnnumberData.getNumber() < limit; }}Copy the code
OddSupport class, to solve the problem of odd numbers
package com.zero.headfirst.chain.support;
/** * odd number processing */
public class OddSupport extends AbstractSupport {
public OddSupport(String name) {
super(name);
}
@Override
protected boolean resolve(NumberData numberData) {
return numberData.getNumber() % 2= =1; }}Copy the code
SpecialSupport specifies the number processor
package com.zero.headfirst.chain.support;
/** * specifies the number of special processing */
public class SpecialSupport extends AbstractSupport {
private Integer number;
public SpecialSupport(String name, Integer number) {
super(name);
this.number = number;
}
@Override
protected boolean resolve(NumberData numberData) {
returnnumberData.getNumber().equals(number); }}Copy the code
Finally, we define a client to assemble our chain of responsibilities, link all the processors together, and simulate requests
package com.zero.headfirst.chain.support;
public class ChainTest {
public static void main(String[] args) {
AbstractSupport alice = new NoSupport("Alice");
AbstractSupport jams = new OddSupport("jams");
AbstractSupport bob = new LimitSupport("Bob".100);
AbstractSupport charlie = new SpecialSupport("Charlie".429);
// Define the chain of responsibility
alice.setNext(jams).setNext(bob).setNext(charlie);
for (int i = 0; i < 500; i += 33) {
alice.process(newNumberData(i)); }}}Copy the code
Our print result: except for the number that we define the processor can process, other numbers cannot be processed.
NumberData{number=0} is resolved by AbstractSupport{name='Bob'}.
NumberData{number=33} is resolved by AbstractSupport{name='jams'}.
NumberData{number=66} is resolved by AbstractSupport{name='Bob'}.
NumberData{number=99} is resolved by AbstractSupport{name='jams'}.
NumberData{number=132} cannot be resolved.
NumberData{number=165} is resolved by AbstractSupport{name='jams'}.
NumberData{number=198} cannot be resolved.
NumberData{number=231} is resolved by AbstractSupport{name='jams'}.
NumberData{number=264} cannot be resolved.
NumberData{number=297} is resolved by AbstractSupport{name='jams'}.
NumberData{number=330} cannot be resolved.
NumberData{number=363} is resolved by AbstractSupport{name='jams'}.
NumberData{number=396} cannot be resolved.
NumberData{number=429} is resolved by AbstractSupport{name='jams'}.
NumberData{number=462} cannot be resolved.
NumberData{number=495} is resolved by AbstractSupport{name='jams'}.
Copy the code
Impure chain of responsibility model
For example, the company needs to approve the leave. For example, if the leave is less than 3 days, the supervisor will approve it. 4-10 days, manager approval; 11-30 days, general manager approval; More than 30 days, not approved, etc. This is a step by step decision. If we ignore design patterns, we can use if… The else… I solved it, but the problem, as you can imagine, is far more complicated in practice than in this example.
Now we solve this with the chain of responsibility design pattern, connecting each role to process the leave data flow.
Abstract processor
package com.zero.design.actions.dutychain;
/** * Responsibility chain abstract class, containing core handling methods, and subsequent responsibility handler Settings. Inherited by different processors */
public abstract class AbstractLeaderHandler {
protected String handlerName;
/** * A successor in the chain of responsibility, that is, this object cannot be handled and passes to the next Leader */
protected AbstractLeaderHandler nextLeaderHandler;
public AbstractLeaderHandler(String handlerName) {
this.handlerName = handlerName;
}
/** * The core business method that handles the request * needs to be implemented by the different processors that inherit the class *@param request
*/
public abstract void handleRequest(LeaveRequest request);
/** * Sets the successor in the responsibility chain *@param nextLeaderHandler
*/
public AbstractLeaderHandler setNextLeaderHandler(AbstractLeaderHandler nextLeaderHandler) {
this.nextLeaderHandler = nextLeaderHandler;
returnnextLeaderHandler; }}Copy the code
Specific processor – Master processor
Responsible for dealing with holidays of less than 3 days, more than 3 days to submit to supervisor
package com.zero.design.actions.dutychain;
/** * The main processor, inherits the abstract processor interface, handles the corresponding business or sets up the next processor */
public class DirectorHandler extends AbstractLeaderHandler {
public DirectorHandler(String handlerName) {
super(handlerName);
}
@Override
public void handleRequest(LeaveRequest request) {
int days = request.getLeaveDays(); // Get the days of leave
String name = request.getName(); // Get the name of the person asking for leave
String reason = request.getReason(); // Get the reason for asking for leave
if (days <= 3) {
// If the requirements are met within 3 days, the director will approve directly
System.out.println("Employees" + name + "Leave" + days + "God, reason:" + reason);
System.out.println("Director" + this.handlerName + "Approved");
} else {
System.out.println("Too many days off, Chief." + this.handlerName + "When it's done, it's over to the manager.");
if(this.nextLeaderHandler ! =null) {
Otherwise, if the next Leader exists on the chain, let him handle it
this.nextLeaderHandler.handleRequest(request); }}}}Copy the code
Manager processor
Handle leave less than 10 days, and submit more than 10 days to general office
package com.zero.design.actions.dutychain;
/** * Manager processor */
public class ManagerHandler extends AbstractLeaderHandler {
public ManagerHandler(String handlerName) {
super(handlerName);
}
@Override
public void handleRequest(LeaveRequest request) {
int days = request.getLeaveDays(); // Get the days of leave
String name = request.getName(); // Get the name of the person asking for leave
String reason = request.getReason(); // Get the reason for asking for leave
if(days <= 10) { // If the requirements within 10 days are met, the manager will approve directly
System.out.println("Employees" + name + "Leave" + days + "God, reason:" + reason);
System.out.println("Manager" + this.handlerName + "Approved");
} else {
System.out.println("Too many days off, manager." + this.handlerName + "The processing is over, and the general office will handle it.");
if(this.nextLeaderHandler ! =null) { Otherwise, if the next Leader exists on the chain, let him handle it
this.nextLeaderHandler.handleRequest(request); }}}}Copy the code
General office processor
Process requests for holidays greater than 10 days
package com.zero.design.actions.dutychain;
/** * General office processor */
public class GeneralManagerHandler extends AbstractLeaderHandler {
public GeneralManagerHandler(String handlerName) {
super(handlerName);
}
@Override
public void handleRequest(LeaveRequest request) {
int days = request.getLeaveDays(); // Get the days of leave
String name = request.getName(); // Get the name of the person asking for leave
String reason = request.getReason(); // Get the reason for asking for leave
if(days <= 30) { // If the requirements within 10 days are met, the manager will approve directly
System.out.println("Employees" + name + "Leave" + days + "God, reason:" + reason);
System.out.println("Agent" + this.handlerName + "Approved");
} else {
System.out.println("Too many days off, general office." + this.handlerName + "Can't handle it.");
if(this.nextLeaderHandler ! =null) { Otherwise, if the next Leader exists on the chain, let him handle it
this.nextLeaderHandler.handleRequest(request);
} else {
System.out.println("Leave of absence unsuccessful"); }}}}Copy the code
Client Role
Declare a processing chain that simulates the leave data flow
package com.zero.design.actions.dutychain;
/** * mock requests: * They process requests under different conditions, and pass requests to the next person on the chain for processing as long as they are not within their scope */
public class Client {
public static void main(String[] args) {
// To initialize the chain of responsibility, use singletons instead
AbstractLeaderHandler directorHandler = new DirectorHandler("Zhang Shengnan");
AbstractLeaderHandler managerHandler = new ManagerHandler("Bill");
AbstractLeaderHandler generalManagerHndler = new GeneralManagerHandler("Prince of zhao");
// Organize the relationship between responsibility objects
directorHandler.setNextLeaderHandler(managerHandler).setNextLeaderHandler(generalManagerHndler);
// Start asking for leave
LeaveRequest request = new LeaveRequest("Ni Shengwu".15."Sleep at home"); directorHandler.handleRequest(request); }}Copy the code
Finally, we look at the print
Too many days of leave, director Zhang Shengnan to deal with the end, but also to the manager to deal with too many days of leave, manager Li Si to deal with the end, to the general office to deal with the employee Ni Shengwu leave for 15 days, reason: sleep at home, general office Zhao Wang approvedCopy the code
conclusion
Advantages of the chain of responsibility model
- Objects only need to know that the request will be processed, and objects in the chain do not need to know the structure of the chain, the client is responsible for the creation of the chain, reducing the coupling degree of the system
- The request-processing object maintains only one reference to its successors, rather than all its references to candidate handlers, simplifying the interconnections of objects
- A responsibility chain gives us more flexibility in assigning responsibilities to objects, adding, deleting and changing the chain dynamically at run time to change the responsibility for handling a request
- When you add a new concrete request handler, you don’t need to modify the original code, you just need to create a new chain on the client side, in accordance with the “open close principle”.
disadvantages
- A request may not be processed because the chain of responsibility is not configured correctly
- For a long chain of responsibilities, the processing of the request may involve multiple processing objects, which will affect the system performance and make it inconvenient to debug
- The system may fall into an infinite loop due to improper creation of the responsibility chain
Usage scenarios
- In the Netty
Pipeline
和ChannelHandler
Organize code logic through the chain of responsibility design pattern - Responsibility chain pattern in the Tomcat filter.
- Plugin mechanism in Mybatis uses the responsibility chain mode to configure various official or custom plugins, similar to Filter, which can perform some operations when executing Sql statements
- There are multiple objects that can handle the same request, and which object will handle the request will be determined at run-time. The client just needs to submit the request to the chain and doesn’t care who the request is handled by or how it is handled
- Submit a request to one of multiple objects without explicitly specifying the recipient
- You can dynamically specify a set of objects to handle requests, and clients can dynamically create chains of responsibilities to handle requests and change the order of the handlers in the chain