This paper will be introduced from a simple scene, step by step optimization, and finally give specific responsibility chain design mode implementation.
The scene is introduced into
- First of all, let’s consider a scenario: users want to post on the forum, but their thoughts are varied. They may post normally, they may insert SHALLOW HTML code in the web page, they may use the wrong emoticon format, and they may send some sensitive information.
- As the administrator of the forum, the user’s posts must be filtered to display, otherwise the forum will not be able to operate. Now let’s consider the simplest way to do this.
public class Demo1 {
public static void main(String[] args) {
String msg = "Hi :), I have to say something super sensitive.";// Suppose there is a post like this
MsgProcessor mp = new MsgProcessor();
mp.setMsg(msg);// Process the postSystem.out.println(mp.process()); }}// Post handler
class MsgProcessor{
private String msg;
public String process(a){
// Handle the HTML tag <>
String str = msg.replace("<"."[").replace(">"."]");
// Take care of sensitive characters
str = str.replace("Sensitive"."Normal");
// Handle the wrong emoticons
str = str.replace(":)"."^_^");
return str;
}
//get()/set()...
}
// Output the resultHello ^_^, [script]haha[/script] I'm going to say something super normalCopy the code
The responsibility chain model is reflected at the beginning
- As you can see from the code above, the post processor filters posts differently. We can map one filtering method to a filter and extract the filter interface up.
public class Demo2 {
public static void main(String[] args) {
String msg = "Hi :), I have to say something super sensitive.";
MsgProcessor mp = newMsgProcessor(); mp.setMsg(msg); System.out.println(mp.process()); }}class MsgProcessor{
private String msg;
private Filter[] filters = {new HtmlFilter(), new SensitiveFilter(), new ExpressionFilter()};
public String process(a){
for(Filter f : filters){
msg = f.doFilter(msg);
}
return msg;
}
public String getMsg(a) {
return msg;
}
public void setMsg(String msg) {
this.msg = msg; }}// Filter interface
interface Filter{
public String doFilter(String s);
}
// Process HTML tags
class HtmlFilter implements Filter{
@Override
public String doFilter(String s) {
return s.replace("<"."[").replace(">"."]"); }}// Handle sensitive phrases
class SensitiveFilter implements Filter{
@Override
public String doFilter(String s) {
return s.replace("Sensitive"."Normal"); }}// Handle expressions
class ExpressionFilter implements Filter{
@Override
public String doFilter(String s) {
return s.replace(":)"."^_^"); }}Copy the code
- The code above already has a chain of responsibility model. As the post is sent to the server, it passes through three filters in turn, which form a filter chain.
- Let’s consider what happens if we want to add a new filter chain to our post process, at the end or in the middle of the original chain.
- The message is processed as it passes through the filter chain. We can treat the filter chain as a filter and let it be implemented as well
Filter
Interface, then you can arbitrarily add other filters and chains to a filter chain. - The following code implements the filter chain
FilterChain
And replace the original with a filter chainMsgProcessor
.
public class Demo3 {
public static void main(String[] args) {
String msg = "Hi :), I have to say something super sensitive.";// Unprocessed posts
FilterChain fc1 = new FilterChain();// Create a filter chain
fc1.add(new HtmlFilter())
.add(new SensitiveFilter());// Add a filter to filter chain 1
FilterChain fc2 = new FilterChain();// Create a filter chain
fc2.add(new ExpressionFilter());// Add a filter to filter chain 2
fc1.add(fc2);// Add chain 2 as a Filter to chain 1 (Filter chain implements Filter interface)
msg = fc1.doFilter(msg);// Use filter chain 1 to filter postsSystem.out.println(msg); }}class FilterChain implements Filter{
private List<Filter> list = new ArrayList<>();
public FilterChain add(Filter filter){
this.list.add(filter);
return this;
}
@Override
public String doFilter(String s) {
for(Filter f : list){
s = f.doFilter(s);
}
returns; }}class HtmlFilter implements Filter{
@Override
public String doFilter(String s) {
return s.replace("<"."[").replace(">"."]"); }}class SensitiveFilter implements Filter{
@Override
public String doFilter(String s) {
return s.replace("Sensitive"."Normal"); }}class ExpressionFilter implements Filter{
@Override
public String doFilter(String s) {
return s.replace(":)"."^_^"); }}interface Filter{
public String doFilter(String s);
}
Copy the code
More sophisticated design, showing the chain of responsibility pattern
- Before moving on to optimization, let’s consider a more realistic requirement, where a request (a post) is sent as a datagram to the server, which in addition to filtering the request needs to give a response and possibly process the response as well. As shown in the figure below
- When a message (containing the request body and the response body) is sent to the server, it passes through filters 1, 2, 3. When the processing is complete and the response sending server is wrapped, it also passes through filters 3, 2, 1.
- You might think it’s a bit like a stack structure, but how does this logic work?
- First we can have the filter hold a reference to the filter chain, executing each filter in turn by calling the filter chain. In order for filters to execute each filter in turn, the filter holds one
index
Serial number, by which the sequence of execution is controlled. As for the backresponse
The reverse order request is implemented through the method return. This part of the design is difficult to explain clearly with words, please be sure to see the following code and the analysis after the code, with pictures. - This part is the essence of the chain of responsibility, understand this part of the code, look at the Web development filter source no pressure.
public class Demo4 {
public static void main(String[] args) {
String msg = "Hi :), I have to say something super sensitive.";// The following three lines simulate a request
Request request = new Request();
request.setRequestStr(msg);
Response response = new Response();/ / response
FilterChain fc = new FilterChain();// Filter chain
HtmlFilter f1 = new HtmlFilter();// Create a filter
SensitiveFilter f2 = new SensitiveFilter();
ExpressionFilter f3 = new ExpressionFilter();
fc.add(f1);// Add the filter to the filter chain
fc.add(f2);
fc.add(f3);
fc.doFilter(request, response, fc);// Call the doFilter() method of the filter chain directlySystem.out.println(request.getRequestStr()); }}interface Filter{
public void doFilter(Request request, Response response, FilterChain fc);
}
class FilterChain implements Filter{
private List<Filter> list = new ArrayList<>();
private int index = 0;
public FilterChain add(Filter filter){
this.list.add(filter);
return this;
}
@Override
public void doFilter(Request request, Response response, FilterChain fc) {
if(index == list.size()){
return;// If index is the size of the container, the processing of the request is complete.
}
Filter f = list.get(index);// Get filter in index orderindex++; f.doFilter(request, response, fc); }}class HtmlFilter implements Filter{
@Override
public void doFilter(Request request, Response response, FilterChain fc) {
request.setRequestStr(request.getRequestStr().replace("<"."[").replace(">"."]"));
System.out.println("Processing request in HtmlFilter");// Process request first
fc.doFilter(request, response, fc);// Call the doFilter method of the Filter chain and tell it to execute the doFilter method of the next Filter. The code handling response will be suspended
System.out.println("Processing response in HtmlFilter"); }}class SensitiveFilter implements Filter{
@Override
public void doFilter(Request request, Response response, FilterChain fc) {
request.setRequestStr(request.getRequestStr().replace("Sensitive"."Normal"));
System.out.println("Process request in SensitiveFilter");
fc.doFilter(request, response, fc);
System.out.println("Handle response in SensitiveFilter"); }}class ExpressionFilter implements Filter{
@Override
public void doFilter(Request request, Response response, FilterChain fc) {
request.setRequestStr(request.getRequestStr().replace(":)"."^_^"));
System.out.println("Processing request in ExpressionFilter");
fc.doFilter(request, response, fc);
System.out.println("Processing response in ExpressionFilter"); }}class Request{
private String requestStr;// A real Request object contains a lot of information
public String getRequestStr(a) {
return requestStr;
}
public void setRequestStr(String requestStr) {
this.requestStr = requestStr; }}class Response{
private String responseStr;
public String getResponseStr(a) {
return responseStr;
}
public void setResponseStr(String responseStr) {
this.responseStr = responseStr; }}Copy the code
- I’ll describe the process once, and you can follow the text to find the corresponding code to understand.
- So let’s first create one of each
Request
andResponse
Object.Request
It needs to be processed by filters 1, 2, and 3 as it enters the back end,Response
The output object is processed by filters 3, 2, and 1 in sequence. - Once the request and response objects are created, we create a chain of filters and add filters 1, 2, and 3 in sequence. The entire process will be left to the filter chain.
- Then we call the filter chain
doFilter()
Method to process the Request object - This is in the filter chain
index
The value is 0 and passesindex
We find the first filter and call itsdoFilter()
Method, let’s look at this code
class HtmlFilter implements Filter{
@Override
public void doFilter(Request request, Response response, FilterChain fc) {
request.setRequestStr(request.getRequestStr().replace("<"."[").replace(">"."]"));
System.out.println("Processing request in HtmlFilter");// Process request first
fc.doFilter(request, response, fc);// Call the doFilter method of the Filter chain and tell it to execute the doFilter method of the next Filter. The code handling response will be suspended
// Execute response on return
System.out.println("Processing response in HtmlFilter"); }}Copy the code
- Enter the
doFilter()
After the method, the first will be rightrequest
The request is processed, and then the filter chain is calleddoFilter()
Method. That’s the subtlety of the whole chain of responsibility model, it explains whydoFilter()
A filter chain argument is added so that each filter can call the filter chain itself to execute the next filter. - Why call the filter chain itself? Because when the filter itself is called, the program jumps back to the filter chain
doFilter
Method is executedindex
Equals 1, which means you get the second filter, and then you continue. - It is because of this jump that the filter pairs
response
The handler cannot execute temporarily, and must wait for the method above to return to the filter chain before it can be executed. - So we’ll see at the end
response
The response is executed by filters 3, 2, 1(and in reverse order of request).
- If you still don’t understand the above picture, please leave a message to me.
- The whole chain of responsibility model has emerged from nothing
Read the Tomcat Filter source code for a deeper understanding.
- I believe that through the above explanation, you have a further understanding of the whole chain of responsibility model.
- Let’s read the Tomcat Filter source code to feel.
public interface Filter {
void init(FilterConfig var1) throws ServletException;
// doFilter(), request, reponse, filterChain.
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
void destroy(a);
}
Copy the code
- We can see that
Filter
The definition of the interface is pretty much the same as we explained, with the addition of initialization and destruction methods.
public interface FilterChain {
void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}
Copy the code
- At the same time we see it
FilterChain
It also extracts up into interfaces, but hereFilterChain
Do not implementFilter
Interface, which means we can’t put twoFilterChain
On the other hand, the filters in Tomcat are not as scalable as in our example^_^
- Further reading
- Java to achieve a simple network of tanks versus small games
- The Observer model and the OCP open-closed principle
- Use of regular expressions in Java