What is the chain of responsibility model? When will it be used?
-
A request can be handled by multiple objects, which combine into a chain and pass the request along the chain until an object handles it. In doing so, the coupling of the sender and receiver of the request is avoided.
-
Application scenario: View tree in Android, transfer of touch events. Back return key, various key events transfer. The life cycle of an Activity sends a Fragment\Fragment event sends a child Fragment.
How to realize the responsibility chain mode?
-
Start by defining a handler interface that all handlers need to implement.
-
Define a Chain object, provide to add and remove handler methods, internal collection store handler (handler interface), external call method, traversed the handler set, call the traversed handler object handler method, if there is a return value, it represents the object processing, return the result after processing.
Chain of responsibility mode gracefully handles image Url requests adding Token authentication
In our last strategic Patterns blog, we used Strategic Patterns to build an alternative Glide, Fresco implemented ImageLoader. However, some projects require a token authentication in the header to request the image Url.
We know Glide has a GlideUrl and can assemble urls and headers, so the easiest thing to do is to reload a GlideUrl when it’s passed in from the outside and just load it. But at this point we should think about the Url type request media is Url, what if it is a file or a stream or some other type? Also add some validation or add-ons, and you’ll have a lot of overloaded methods. Then the chain of responsibility model is more suitable.
- Ideas:
- The external still passes in the original Url.
- Create the request object ImageRequest and wrap the URL and header parameters.
- Build filter filter interface, each filter object should be implemented, do the corresponding processing.
- Create Chain Chain objects, provide methods to add and remove filters, provide process processing methods, traverse filters, and finally submit the processing results to ImageLoader
- When the ImageLoader receives the result, it passes it on to the configured ILoaderStrategy interface implementation class. For example, GlideLoader implements GlideUrl, which constructs the parameters in ImageRequest.
- Create the request object as
Public class ImageRequest {/** * request to load resources */ private Objectsource; /** * private Map<String, String> headers; public ImageRequest(Objectsource, Map<String, String> headers) {
this.source = source; this.headers = headers; } / /... omitsetGet method}Copy the code
- Create the filter interface IFilter
Public interface IFilter {/** * returns the priority of the current filter, the smaller the priority */ int getPriority(); /** * Whether can be processed, returntrueThe representative can handle it,false*/ Boolean isCanHandle(ImageRequest Request); /** * To prepare the filtered callback, need to return the filtered request, including Url and Headers request Headers */ ImageRequestdoFilter(ImageRequest request);
}
Copy the code
- Create a filter for adding tokens
public class GlobalImageFilter implements IFilter {
@Override
public int getPriority() {
return1; } @override public Boolean isCanHandle(ImageRequest Request) {} @override public Boolean isCanHandle(ImageRequest Request)source = request.getSource();
boolean isCanHandle = false;
if (source instanceof String) {
String url = (String) source;
if (url.startsWith("http") || url.startsWith("https")) {
isCanHandle = true; }}return isCanHandle;
}
@Override
public ImageRequest doFilter(ImageRequest Request) {// Here the Token is added to the Header Map. Map<String, String> headers = request.getHeaders(); headers.put(ApiConstant.Api.IMAGE_TOKEN, EncryptionUtil.getEncrytionStr());returnrequest; }}Copy the code
- Creating a Chain object
public class FilterChain { private ArrayList<IFilter> mFilters = new ArrayList<>(); public void addFilter(IFilter filter) { mFilters.add(filter); } public void removeFilter(IFilter filter) { mFilters.remove(filter); } public ImageRequest process(ImageRequest Request)if (mFilters.isEmpty()) {
returnrequest; } // Only one, no need to traverseif (mFilters.size() == 1) {
IFilter filter = mFilters.get(0);
if (filter.isCanHandle(request)) {
returnmFilters.get(0).doFilter(request); }} // Sort by priority, Collections.sort(mFilters, new Comparator<IFilter>() {@override public int compare(IFilter o1, IFilter o2) {collections.sort (mFilters, new Comparator<IFilter>() {@override public int compare(IFilter o1, IFilter o2) {returnInteger.compare(o1.getPriority(), o2.getPriority()); }}); // Execute multiple traversals, each traversal saves the result, the next traversal passes in the result of the previous filter processingfor (IFilter filter : mFilters) {
if(filter.isCanHandle(request)) { request = filter.doFilter(request); }}returnrequest; }}Copy the code
- Add FilterChain instance to ImageLoader. Provides methods for adding and removing filters.
public class ImageLoader { private ILoaderStrategy mLoader; private Context mContext; Private FilterChain mChain = new FilterChain(); public ImageLoader with(Context context) { mContext = context;returnthis; } / /... Omit some methods public FilterChaingetFilterChain() {
return mChain;
}
public ImageLoader addFilter(IFilter filter) {
mChain.addFilter(filter);
return this;
}
public ImageLoader removeFilter(IFilter filter) {
mChain.removeFilter(filter);
returnthis; }}Copy the code
- Registration filter
Imageloader.with (this).setLoader(new GlideLoader()) // Add a Url to the filter to add validation header.addFilter (new GlobalImageFilter());Copy the code
- Finally, the policy implementation class, load method, calls imageloader.getFilterChain ().process(internally calls FilterChain process() for responsibility chain handling)
public class GlideLoader implements ILoaderStrategy { //... @override public void load(@notnull Context Context, @notnull LoadOption option, @notnull ImageView targetView) {//1typeValue = option.getDrawableType().getTypeValue();
ImageRequest request = ImageLoader. getFilterChain().process(new ImageRequest(typeValue, new HashMap()));
Object loadType = typeValue;
if(request.getSource instanceof String) { loadType = new GlideUrl(request.getSource(), } request.getHeaders()); Glide. With (context).load(loadType).into(targetView); }}Copy the code
conclusion
-
The chain of responsibility disperses the processing into a specific handler implementation, reducing the coupling between the handler and the caller.
-
A common problem with design patterns is that they increase the number of subclasses.