The definition and characteristics of responsibility chain pattern
The definition of responsibility chain pattern : Give multiple objects a chance to process requests , This avoids the coupling between the sender and the receiver of the request , Link this object into a chain , And pass the request along the chain , Until someone deals with him .
Standard responsibility chain model , Personal summary has the following characteristics :
- Every object in the chain has an opportunity to process requests
- Each object in the chain holds the next reference to the object to be processed
- An object in the chain cannot process the current request , Then it will pass the same request to the next object
Use a diagram to show the following architecture after using the responsibility chain pattern :
in other words , The responsibility chain pattern satisfies the loose coupling between the request sender and the request handler , Abstract non core parts , Processing the request object in the way of chain call .
I don't understand ? So let's show you through practical examples .
Don't use the responsibility chain model
Why use the responsibility chain model , Then we need to know what's wrong with not using the responsibility chain model , Then how to optimize the code by using the responsibility chain pattern .
Now there's a scene : Xiao Ming is going to school , My mother made a list for Xiao Ming before he went to school ( Wash your hair 、 have breakfast 、 wash one 's face ), Xiao Ming must follow his mother's request , You can't go to school until you've done the things on the list .
First we define a preparation list PreparationList:
1 public class PreparationList { 2 3 /** 4 * Wash your face 5 */ 6 private boolean washFace; 7 8 /** 9 * Do you wash your hair 10 */ 11 private boolean washHair; 12 13 /** 14 * Breakfast or not 15 */ 16 private boolean haveBreakfast; 17 18 public boolean isWashFace() { 19 return washFace; 20 } 21 22 public void setWashFace(boolean washFace) { 23 this.washFace = washFace; 24 } 25 26 public boolean isWashHair() { 27 return washHair; 28 } 29 30 public void setWashHair(boolean washHair) { 31 this.washHair = washHair; 32 } 33 34 public boolean isHaveBreakfast() { 35 return haveBreakfast; 36 } 37 38 public void setHaveBreakfast(boolean haveBreakfast) { 39 this.haveBreakfast = haveBreakfast; 40 } 41 42 @Override 43 public String toString() { 44 return "ThingList [washFace=" + washFace + ", washHair=" + washHair + ", haveBreakfast=" + haveBreakfast + "]"; 45 } 46 47 }
Three things are defined : Wash your hair 、 wash one 's face 、 to have breakfast .
Then define a learning class , According to my mother's request , Finish what my mother asked before going to school :
1 public class Study { 2 3 public void study(PreparationList preparationList) { 4 if (preparationList.isWashHair()) { 5 System.out.println(" wash one 's face "); 6 } 7 if (preparationList.isWashHair()) { 8 System.out.println(" Wash your hair "); 9 } 10 if (preparationList.isHaveBreakfast()) { 11 System.out.println(" to have breakfast "); 12 } 13 14 System.out.println(" I can go to school !"); 15 } 16 17 }
This example fulfills our requirements , But not elegant enough , Our main process is learning , But couple the actions to be done with the learning , There are two problems with this :
- PreparationList When you add one thing , For example, add makeup 、 Clean a room , Must be modified study Methods to adapt
- When the order of these things needs to change , Must be modified study Method , For example, first wash your hair and then wash your face , that 7~9 The line code must be in line with 4~6 Lines of code are interchanged
The worst way to write , Just to satisfy the function , Against the principle of opening and closing , That is, when we expand the function, we need to modify the main process , Unable to close the modification 、 Open to expansion .
Use the responsibility chain model
Let's take a look at how to use the responsibility chain model , Since the characteristics of the responsibility chain model are “ Each object in the chain holds a reference to the next object ”, So let's do this .
First abstract out a AbstractPrepareFilter:
1 public abstract class AbstractPrepareFilter { 2 3 private AbstractPrepareFilter nextPrepareFilter; 4 5 public AbstractPrepareFilter(AbstractPrepareFilter nextPrepareFilter) { 6 this.nextPrepareFilter = nextPrepareFilter; 7 } 8 9 public void doFilter(PreparationList preparationList, Study study) { 10 prepare(preparationList); 11 12 if (nextPrepareFilter == null) { 13 study.study(); 14 } else { 15 nextPrepareFilter.doFilter(preparationList, study); 16 } 17 } 18 19 public abstract void prepare(PreparationList preparationList); 20 21 }
Leave an abstract method prepare Give subclasses to implement , Hold the reference to the next object in the abstract class nextPrepareFilter, If there is , execute ; If it doesn't mean that all objects in the chain have been executed , perform Study Class study() Method :
1 public class Study { 2 3 public void study() { 4 System.out.println(" Study "); 5 } 6 7 }
Then we realize AbstractPrepareList, It's simpler , The first is shampoo :
1 public class WashFaceFilter extends AbstractPrepareFilter { 2 3 public WashFaceFilter(AbstractPrepareFilter nextPrepareFilter) { 4 super(nextPrepareFilter); 5 } 6 7 @Override 8 public void prepare(PreparationList preparationList) { 9 if (preparationList.isWashFace()) { 10 System.out.println(" wash one 's face "); 11 } 12 13 } 14 15 }
Then wash your face :
1 public class WashHairFilter extends AbstractPrepareFilter { 2 3 public WashHairFilter(AbstractPrepareFilter nextPrepareFilter) { 4 super(nextPrepareFilter); 5 } 6 7 @Override 8 public void prepare(PreparationList preparationList) { 9 if (preparationList.isWashHair()) { 10 System.out.println(" Wash your hair "); 11 } 12 13 } 14 15 }
Finally have breakfast :
1 public class HaveBreakfastFilter extends AbstractPrepareFilter { 2 3 public HaveBreakfastFilter(AbstractPrepareFilter nextPrepareFilter) { 4 super(nextPrepareFilter); 5 } 6 7 @Override 8 public void prepare(PreparationList preparationList) { 9 if (preparationList.isHaveBreakfast()) { 10 System.out.println(" to have breakfast "); 11 } 12 13 } 14 15 }
Finally, let's see how the caller writes :
1 @Test 2 public void testResponsibility() { 3 PreparationList preparationList = new PreparationList(); 4 preparationList.setWashFace(true); 5 preparationList.setWashHair(false); 6 preparationList.setHaveBreakfast(true); 7 8 Study study = new Study(); 9 10 AbstractPrepareFilter haveBreakfastFilter = new HaveBreakfastFilter(null); 11 AbstractPrepareFilter washFaceFilter = new WashFaceFilter(haveBreakfastFilter); 12 AbstractPrepareFilter washHairFilter = new WashHairFilter(washFaceFilter); 13 14 washHairFilter.doFilter(preparationList, study); 15 }
Now use the responsibility chain mode to modify this logic , See that we have achieved the decoupling between learning and preparation , That is, the core thing we need to learn , At this time, no matter how much preparation work is added , No need to modify study Method , Just modify the caller .
But is this a good way to write ? I think this kind of writing conforms to the principle of opening and closing , But two obvious disadvantages are not client friendly :
- increase 、 Reduce responsibility chain objects , Need to modify client code , For example, I want to add a house cleaning operation , that testResponsibility() The method needs to be changed
- AbstractPrepareFilter washFaceFilter = new WashFaceFilter(haveBreakfastFilter) This call is not elegant enough , Clients need to think about it , In the end, when it is called, three are called Filter Which of them? Filter
So , Let's have the ultimate version of 、 Upgraded responsibility chain model .
Upgraded responsibility chain mode
Above we wrote a responsibility chain model , This is a primary way to write in line with the responsibility chain model , Finally, I wrote , There are obvious shortcomings in this way of writing , Then let's take a look at how to write the updated responsibility chain model , Solve the above problems .
The following way of writing is also Servlet How to implement , The first is to abstract one Filter:
1 public interface StudyPrepareFilter { 2 3 public void doFilter(PreparationList preparationList, FilterChain filterChain); 4 5 }
Notice that there is one more FilterChain, That's the chain of responsibility , It is used to string up all the responsible objects , So is it StudyPrepareFilter A subclass of :
1 public class FilterChain implements StudyPrepareFilter { 2 3 private int pos = 0; 4 5 private Study study; 6 7 private List<StudyPrepareFilter> studyPrepareFilterList; 8 9 public FilterChain(Study study) { 10 this.study = study; 11 } 12 13 public void addFilter(StudyPrepareFilter studyPrepareFilter) { 14 if (studyPrepareFilterList == null) { 15 studyPrepareFilterList = new ArrayList<StudyPrepareFilter>(); 16 } 17 18 studyPrepareFilterList.add(studyPrepareFilter); 19 } 20 21 @Override 22 public void doFilter(PreparationList thingList, FilterChain filterChain) { 23 // All filters are finished 24 if (pos == studyPrepareFilterList.size()) { 25 study.study(); 26 } 27 28 studyPrepareFilterList.get(pos++).doFilter(thingList, filterChain); 29 } 30 31 }
That is, there is a counter , Suppose that all StudyPrepareFilter The call is not finished , So call the next , Otherwise execution Study Of study() Method .
Then it's easier , Realization StudyPrepareFilter The class can , First of all, shampoo :
1 public class WashHairFilter implements StudyPrepareFilter { 2 3 @Override 4 public void doFilter(PreparationList preparationList, FilterChain filterChain) { 5 if (preparationList.isWashHair()) { 6 System.out.println(" Wash your hair "); 7 } 8 9 filterChain.doFilter(preparationList, filterChain); 10 } 11 12 }
Be careful , Here each implementation class needs to explicitly call filterChain Of doFilter Method . wash one 's face :
1 public class WashFaceFilter implements StudyPrepareFilter { 2 3 @Override 4 public void doFilter(PreparationList preparationList, FilterChain filterChain) { 5 if (preparationList.isWashFace()) { 6 System.out.println(" Wash your face "); 7 } 8 9 filterChain.doFilter(preparationList, filterChain); 10 } 11 12 }
have breakfast :
1 public class HaveBreakfastFilter implements StudyPrepareFilter { 2 3 @Override 4 public void doFilter(PreparationList preparationList, FilterChain filterChain) { 5 if (preparationList.isHaveBreakfast()) { 6 System.out.println(" After breakfast "); 7 } 8 9 filterChain.doFilter(preparationList, filterChain); 10 } 11 12 }
Finally, let's look at the caller :
1 @Test 2 public void testResponsibilityAdvance() { 3 PreparationList preparationList = new PreparationList(); 4 preparationList.setWashFace(true); 5 preparationList.setWashHair(false); 6 preparationList.setHaveBreakfast(true); 7 8 Study study = new Study(); 9 10 StudyPrepareFilter washFaceFilter = new WashFaceFilter(); 11 StudyPrepareFilter washHairFilter = new WashHairFilter(); 12 StudyPrepareFilter haveBreakfastFilter = new HaveBreakfastFilter(); 13 14 FilterChain filterChain = new FilterChain(study); 15 filterChain.addFilter(washFaceFilter); 16 filterChain.addFilter(washHairFilter); 17 filterChain.addFilter(haveBreakfastFilter); 18 19 filterChain.doFilter(preparationList, filterChain); 20 }
Solve the problems of the first version of the responsibility chain model perfectly , Now add 、 Modifying the client calling code of the responsible object does not need to be changed .
Some people may ask , You add 、 Reduce the number of people responsible ,testResponsibilityAdvance() Method , Not yet addFilter, Or delete a line ? Let's think back to ,Servlet We increase or decrease Filter Do you need to change any code ? no need , All we need to change is web.xml nothing more . Same thing ,FilterChain There are studyPrepareFilterList, We can put FilterChain Make one Spring Bean, be-all Filter The concrete implementation classes are Spring Bean, Inject studyPrepareFilterList Just fine , The pseudo code is :
1 <bean id="filterChain" class="xxx.xxx.xxx.FilterChain"> 2 <property name="studyPrepareFilterList"> 3 <list> 4 <ref bean="washFaceFilter" /> 5 <ref bean="washHairFilter" /> 6 <ref bean="haveBreakfastFilter" /> 7 </list> 8 </property> 9 </bean>
Does this solve the problem perfectly ? We added 、 Reduce Filter, Or modify Filter The order , It just needs to be modified .xml File can , Not only does the core logic conform to the opening and closing principles , The caller also complies with the opening and closing principle .
The use scenario of the responsibility chain mode
That's not enough , The most typical is Servlet Medium Filter, With the above analysis , You should be able to understand Servlet The working principle of the responsibility chain model in , Then why one by one Filter It needs to be configured in web.xml in .
The structure of the responsibility chain model
think about it , It seems that the responsibility chain model has no complicated structure , Abstract responsibility , Implement responsible interfaces , The client initiates the call , I found a picture on the Internet to show :
Advantages and use scenarios of the responsibility chain mode
Finally, the advantages of the responsibility chain model , There are the following points :
- It realizes the loose coupling between request sender and request handler
- Responsibility objects can be added dynamically 、 Delete the responsible object 、 Change the order of responsible objects , Very flexible
- Each responsible person focuses on doing his own thing , Clear responsibilities
When to use the responsibility chain model ? I think so about this question : When designing the system , It's better to distinguish primary and secondary , Which part is the core process , Which part is the auxiliary process , Are there any auxiliary processes N many if...if...if... Scene , If so, and each if There is a unified abstraction , So abstract auxiliary process , Put each if Chain call as a responsible object , Elegant implementation , Easy to reuse and expandable .