Responsibility chain model of common design patterns

concept

Chain of Responsibility Pattern is to treat each node in the chain as an object, each node processes different requests, and internally maintains the next node object. When a request is sent from the first segment of the chain, it will be passed to each node object in turn along the path of the chain until the request is processed by an object. This type of design pattern belongs to behavioral pattern.
The responsibility chain model mainly includes two roles:

  • Abstract handler: define one processing method and maintain the reference of the next processing node Handle object;
  • Concrete handle: process the request and forward it if not interested.

realization

Next, take login verification as an example

1. Create abstract processor

public abstract class Handle {
    protected Handle chain;

    public void next(Handle handle){
        this.chain=handle;
    }

    public abstract void doHandle(Member member);
}

2. Specific handler

public class ValidateHadle extends Handle {
    @Override
    public void doHandle(Member member) {
        if("".equals(member.getName())||"".equals(member.getPwd())){
            System.out.println("User name or password is empty");
            return;
        }
        System.out.println("User name password format verification completed");
        chain.doHandle(member);
    }
}
public class LoginHandle extends Handle {
    @Override
    public void doHandle(Member member) {
        System.out.println("Login succeeded");
        chain.doHandle(member);
    }
}
public class AuthHandle extends Handle {
    @Override
    public void doHandle(Member member) {
        System.out.println("Welcome administrator!");
    }
}

3. Test

public class ChainTest {
    public static void main(String[] args) {
        Handle validateHandle=new ValidateHadle();
        Handle loginHandle=new LoginHandle();
        Handle authHandle=new AuthHandle();
        validateHandle.next(loginHandle);
        loginHandle.next(authHandle);
        validateHandle.doHandle(new Member("tom","123"));
    }
}

Operation result:

Use scenario

  • The same request is processed for objects, but it is determined by which object is processed dynamically at run time
  • Submit a request to one of multiple objects without explicitly specifying the recipient
  • A set of objects can be dynamically specified to process requests

Application in Spring framework

In JDK, there is a very common class Filter

public interface Filter {
    public void init(FilterConfig filterConfig) throws ServletException;
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;
    public void destroy();

}

The Filter interface is very simple, equivalent to the Handle abstract role in the responsibility chain. Let's take a look at the FilterChain class, the last parameter in the doFilter method

public interface FilterChain {
    public void doFilter(ServletRequest request, ServletResponse response)
            throws IOException, ServletException;

}

Only one doFilter method is defined, so how do they concatenate into chains? Let's take a look at Spring's implementation of the MockFilterChain class

public class MockFilterChain implements FilterChain {

	private ServletRequest request;

	private ServletResponse response;

	private final List<Filter> filters;

	private Iterator<Filter> iterator;
	public MockFilterChain() {
		this.filters = Collections.emptyList();
	}
	public MockFilterChain(Servlet servlet) {
		this.filters = initFilterList(servlet);
	}
	public MockFilterChain(Servlet servlet, Filter... filters) {
		Assert.notNull(filters, "filters cannot be null");
		Assert.noNullElements(filters, "filters cannot contain null values");
		this.filters = initFilterList(servlet, filters);
	}

	private static List<Filter> initFilterList(Servlet servlet, Filter... filters) {
		Filter[] allFilters = ObjectUtils.addObjectToArray(filters, new ServletFilterProxy(servlet));
		return Arrays.asList(allFilters);
	}
	public ServletRequest getRequest() {
		return this.request;
	}
	public ServletResponse getResponse() {
		return this.response;
	}
	@Override
	public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
		Assert.notNull(request, "Request must not be null");
		Assert.notNull(response, "Response must not be null");
		Assert.state(this.request == null, "This FilterChain has already been called!");

		if (this.iterator == null) {
			this.iterator = this.filters.iterator();
		}

		if (this.iterator.hasNext()) {
			Filter nextFilter = this.iterator.next();
			nextFilter.doFilter(request, response, this);
		}

		this.request = request;
		this.response = response;
	}
}

It puts all the filters of the chain into the list, and then calls the doFilter method to iterate over the list, that is to say, the filters in the list will be executed in sequence
The UML class diagram is as follows:

summary

advantage

  1. Decouple request and processing
  2. Simplified objects. So that the object does not need to know the structure of the chain
  3. The link structure is flexible. You can dynamically add or delete responsibilities by changing the link structure order
  4. Easy to extend new request processing classes

shortcoming

  1. Too long responsibility chain will lead to too long processing time, thus affecting the overall performance
  2. If the node object has a circular reference, it will cause a dead cycle, resulting in system crash

Tags: Spring JDK

Posted on Fri, 12 Jun 2020 00:52:18 -0400 by irishgirl2004