What is the responsibility chain design pattern
Responsibility chain design pattern is a common design pattern in software development. In this pattern, a request is processed step by step to form a chain. The request is processed in the nodes of the chain, and one node processes its own logic and then passes it to the next node.
In many open source libraries, you can see the use of the responsibility chain pattern, such as OkHttp, PermissionX, Filter in Spring, etc.
In daily development, such as login verification, return value processing and multiple pop-up boxes, the responsibility chain mode can be used. The following two cases are used to realize the responsibility chain mode.
Daily case
Requirement Description: the responsibility chain mode is used to implement the employee leave approval process. The team leader can approve leave less than 3 days, the manager can approve leave less than 10 days, and the boss can approve leave more than 10 days.
Analysis: using the object-oriented method, vacation is the business to be processed in the demand, and the team leader, manager and boss belong to the processing nodes in the responsibility chain.
When each node processes, if it meets the requirements, it will return the processing results. If it does not meet the requirements, it will go to the next node for processing.
1, Create holiday class Event
public class Event { // Leave days private int date; public int getDate() { return date; } public void setDate(int date) { this.date = date; } }
2, Create a chain of responsibility
1. Create chain node abstract class
Each processing node object needs to inherit the abstract class and implement the proceed method. The execution logic is implemented in the proceed method. If the requirements are met, the execution returns. If the requirements are not met, it is handed over to the next chain node.
Use the constructor mode to add chain nodes. Refer to the article: This is the elegant use of the responsibility chain model
public abstract class CheckChain { // Current processing node protected CheckChain checker; // Set next processor public void setNextChecker(CheckChain checker) { this.checker = checker; } // Processing method, which each processor should implement public abstract void proceed(Event event); // Create using constructor mode public static class Builder { // Record the first processor and the next processor respectively, similar to the linked list structure private CheckChain head; private CheckChain tail; // Add processor public Builder addChecker(CheckChain chain) { if (this.head == null) { this.head = this.tail = chain; return this; } // Set next processor this.tail.setNextChecker(chain); this.tail = chain; return this; } public CheckChain build() { return this.head; } } }
2. Create responsibility chain node
Create team leader, manager and boss processing nodes respectively.
Group leader chain node. The group leader can approve leave less than 3 days. If it exceeds 3 days, it needs to be approved by the manager.
public class GroupChain extends CheckChain { @Override public boolean proceed(Event event) { if (event.getDate() > 3) { System.out.println("The team leader's authority is not enough, and it is transferred to the superior leader for approval"); if (checker != null) { return checker.proceed(event); } return false; } else { System.out.println("Approved by the team leader"); return true; } } }
In the manager chain node, managers can approve leave less than 10 days, and those more than 10 days need to be approved by the boss.
public class ManagerChain extends CheckChain { @Override public boolean proceed(Event event) { if (event.getDate() > 10) { System.out.println("The manager's authority is not enough, so it is transferred to the superior leader for approval"); if (checker != null) { return checker.proceed(event); } return false; } else { System.out.println("Approved by manager"); return true; } } }
The boss can approve all holidays.
public class BossChain extends CheckChain { @Override public boolean proceed(Event event) { System.out.println("Approved by the boss"); return true; } }
3, Test procedure
// Create a leave slip for 30 days Event event = new Event(); event.setDate(30); // Create leader, manager and boss chain nodes GroupChain groupChain = new GroupChain(); new CheckChain.Builder() .addChecker(groupChain) .addChecker(new ManagerChain()) .addChecker(new BossChain()) .build(); // The team leader initiates the operation boolean result = groupChain.proceed(event); System.out.println("Whether the leave is successful:" + result);
Execution results:
The team leader's authority is not enough, and it is transferred to the superior leader for approval The manager's authority is not enough, so it is transferred to the superior leader for approval Approved by the boss Whether the leave is successful: true
In this test procedure, for those who ask for leave, they do not focus on who has the authority to approve the leave, but on whether they finally ask for leave successfully. Therefore, the responsibility chain model only focuses on the results, not the process.
Handwritten OkHttp responsibility chain
The above implementation is only one of the implementation of the responsibility chain mode. The implementation of the responsibility chain in OkHttp is slightly different. In order to be more familiar with the responsibility chain mode, let's further understand the responsibility chain mode and OkHttp principle by writing the implementation mode of the responsibility chain in OkHttp.
Let's first look at a code that initiates a network request:
// Initialize OkHttpClient through constructor mode OkClient client = new OkClient.Builder() .addInterceptor(new BridgeInterceptor()) .addInterceptor(new NetworkInterceptor()) .addInterceptor(new LogInterceptor()) .addInterceptor(new CallServerInterceptor()) .build(); // The request is created through the constructor pattern Request request = new Request.Builder() .url("http:123.com") .build(); // Get the execution result after initiating the request Response response = client.newCall(request).execute(); // Output execution results System.out.println(response.toString());
The above code is a network request modeled on OkHttp. Let's see how the request process is implemented through the responsibility chain mode.
1, Create the OkClient class, which adds corresponding parameters through the constructor pattern
public class OkClient { // Create a collection to store interceptors private ArrayList<Interceptor> interceptors = new ArrayList<>(); public OkClient(Builder builder) { this.interceptors = builder.arrayList; } // Returns the interceptor collection public List<Interceptor> interceptors() { return interceptors; } // Create originating request method public RealCall newCall(Request request) { return new RealCall(this, request); } public static class Builder { private ArrayList<Interceptor> arrayList = new ArrayList<>(); public Builder addInterceptor(Interceptor interceptor) { arrayList.add(interceptor); return this; } public OkClient build() { return new OkClient(this); } } }
2, Create responsibility chain interface
OkHttp uses interfaces, and the previous case used abstract classes.
1. Interceptor interface and internal Chain interface
public interface Interceptor { // Intercept processing response Response intercept(Chain chain); // The OkHttp responsibility chain processes not only Request requests but also Response requests interface Chain { Request request(); Response proceed(Request request); } }
3, Create Request class and Response class
1. Request class
The Request class is created through the constructor mode, as shown below. The Request class will pass the Request url, Request type, Request header and Request body, and will check and set these values in the interceptor.
public class Request { private String url; private String mediaType; private String body; private String header; public Request(Builder builder) { this.url = builder.url; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } public String getHeader() { return header; } public void setHeader(String header) { this.header = header; } public String getMediaType() { return mediaType; } public void setMediaType(String mediaType) { this.mediaType = mediaType; } public static class Builder { private String url; public Builder url(String url) { this.url = url; return this; } public Request build() { return new Request(this); } } }
2. Response class
The Response class is relatively simple and returns the required print information, as shown below:
public class Response { private String bridgeInterceptor; private String logInterceptor; private String networkInterceptor; private String callServerInterceptor; // Omit some codes }
4, Create interceptor
The Interceptor created needs to implement the Interceptor interface, and create BridgeInterceptor, NetworkInterceptor, LogInterceptor and CallServerInterceptor respectively.
1. Create BridgeInterceptor class
public class BridgeInterceptor implements Interceptor { @Override public Response intercept(Chain chain) { // Processing requests Request request = chain.request(); String mediaType = request.getMediaType(); // Set MediaType in this interceptor if (TextUtils.isEmpty(mediaType)) { request.setMediaType("BridgeInterceptor"); } // Processing response results Response response = chain.proceed(request); // Set values for response results if (TextUtils.isEmpty(response.getBridgeInterceptor())) { response.setBridgeInterceptor("BridgeInterceptor"); } return response; } }
2. Create NetworkInterceptor class
public class NetworkInterceptor implements Interceptor { @Override public Response intercept(Chain chain) { // Processing requests Request request = chain.request(); // Set the request header in the interceptor if (TextUtils.isEmpty(request.getHeader())) { request.setHeader("RetryInterceptor"); } // Processing response results Response response = chain.proceed(request); // Set values for response results if (TextUtils.isEmpty(response.getNetworkInterceptor())) { response.setNetworkInterceptor("NetworkInterceptor"); } return response; } }
3. Create LogInterceptor class
public class LogInterceptor implements Interceptor { @Override public Response intercept(Chain chain) { Request request = chain.request(); // Set the request body in the interceptor if (TextUtils.isEmpty(request.getBody())) { request.setBody("LogInterceptor"); } // Processing response results Response response = chain.proceed(request); // Set values for response results if (TextUtils.isEmpty(response.getLogInterceptor())) { response.setLogInterceptor("LogInterceptor"); } return response; } }
4. Create CallServerInterceptor class
The CallServerInterceptor interceptor belongs to the last interceptor in OkHttp. It will no longer execute the processed method and will directly return the Response
public class CallServerInterceptor implements Interceptor { @Override public Response intercept(Chain chain) { Request request = chain.request(); System.out.println("CallServerInterceptor: " + request.toString()); // If the request header, request type and request body are not empty, a network request is initiated and the result is returned if (!TextUtils.isEmpty(request.getHeader()) && !TextUtils.isEmpty(request.getMediaType()) && !TextUtils.isEmpty(request.getBody())) { // Initiate a network request and return the result Response response = new Response(); response.setCallServerInterceptor("CallServerInterceptor"); return response; } return null; } }
5, Create RealCall class
In OkHttp, the calling process is OkHttpClient.. Newcall() -- > create RealCall – > execute RealCall Execute() method -- > RealCall Getresponsewithinterceptorchain() method, add an interceptor in this method and call the proceed method of the first interceptor;
public Response getResponseWithInterceptorChain() { List<Interceptor> interceptors = new ArrayList<>(); interceptors.addAll(client.interceptors()); Interceptor.Chain chain = new RealInterceptorChain(interceptors, 0, this.request); return chain.proceed(request); }
6, Create RealInterceptorChain class
In the processed method of the RealInterceptorChain class, the call of the responsibility chain is started through the index index.
public class RealInterceptorChain implements Interceptor.Chain { private int index; private List<Interceptor> interceptors; private Request request; public RealInterceptorChain(List<Interceptor> interceptors, int index, Request request) { this.interceptors = interceptors; this.index = index; this.request = request; } @Override public Request request() { return this.request; } @Override public Response proceed(Request request) { if (index >= interceptors.size()) throw new Error("The current index and interceptor length do not match"); // Get the next responsibility chain interceptor and index+1 RealInterceptorChain next = new RealInterceptorChain(interceptors, index + 1, request); // Get current interceptor Interceptor interceptor = interceptors.get(index); // Execute the interception method and pass the next interceptor Response response = interceptor.intercept(next); return response; } }
7, Final output
// Print out the request parameters added during the request process CallServerInterceptor: Request{url='http:123.com', mediaType='BridgeInterceptor', body='LogInterceptor', header='RetryInterceptor'} // There are 4 interceptor results in the returned Response Response{bridgeInterceptor='BridgeInterceptor', logInterceptor='LogInterceptor', networkInterceptor='NetworkInterceptor', callServerInterceptor='CallServerInterceptor'}
The responsibility chain mode in OkHttp has been implemented. It can be seen that the implementation in OkHttp is also very elegant. Through this article, you can not only learn the principle of responsibility chain mode, but also be familiar with the function principle of interceptors in the request and return process of OkHttp.
The above source code can view the project 034-android-chain
Love official account.