Chain of Responsibility Design Pattern in Java : 2 Implementation

Chain of Responsibility(COR) Pattern is used, when more than one object handles a request and performs their corresponding responsibilities to complete the whole task.

The pattern can be used to achieve loose coupling in software design where the request can be passed through a chain of objects or request handler for processing, based on some criteria in each handler object it will handle the request or pass it to the next handler. Following is a representation of COR pattern.

Now the COR pattern can be implemented in 2 ways.

One Request Handler will serve the input request:

Here the request initiator is not aware about the exact handler, which will serve its request. Every handler in chain will have the responsibility to decide, if they can serve the request. If any handler decides to forward the request, it should be capable of choosing the next handler and forward it. There is a possibility where none of the handler may serve the request.

This type of implementation of COR pattern is present in Java’s exception handling mechanism. We can have multiple catch blocks in a try-catch block code. Here every catch block is kind of a handler to handle that particular exception.

So when any exception occurs in the try block, it sends the exception to the first catch block to process. If the catch block is not able to handle it, it forwards the exception to next handler in chain i.e. next catch block. If even the last catch block is not able to handle it, then the exception is thrown outside of the chain to the calling program.

Multiple Request Handler will serve the input request:

There is another type of implementation of COR pattern, where more than one handler participates in the processing to complete the whole task. In this implementation each handler performs its part of task (if required based on some condition) and forward the request to the next handler. And the whole task it completed, once the request traveled through the complete chain.

Spring’s HandlerInterceptorAdapter is an example of this type of COR pattern. The HandlerInterceptorAdapter defines three methods as preHandle(), postHandle() and afterCompletion().

Here the preHandle() follows the COR pattern. If we have multiple interceptors for intercepting a request. Then the request flows through all the preHandle() methods sequentially. Where each preHandle() can perform some task and modify the request before it reaches to the Controller. Here’s what a simple preHandle() implementation will look like:

@Override

public boolean preHandle(

  HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception {

    // your code

    return true;

}

This method is called before handling a request. If it returns true the spring framework send the request further to the next handler method (to the next interceptor’s preHandle). If the method returns false, Spring assumes that request has been handled and no further processing is required. By this boolean flag each handler of the chain chooses the next handler. Now let us have a simple implementation of COR pattern.

We have a request that needs to collect data from different external and third party data storage like Database, File storage and Cloud environments. Here we will develop a chain of such data collectors, which are responsible for collecting data from different sources. First we will define the type of RequestHandler. 

This is an interface defining the basic blue print of all the concrete Request handler.

package com.tuturself.cor;

public interface RequestHandler {

	void setNextRequestHandler(RequestHandler requestHandler);

        boolean process(RequestData requestData);
}

Now , we will define the 3 concrete request handler, which are responsible for collecting data from each of the source : Database, File and Cloud. And in addition each of this handler has a pointer to the next handler object. Thus control can be passed to the next handler when the current handler finishes. 

DBDataHandler.java

package com.tuturself.cor;

public class DBDataHandler implements RequestHandler {

	private RequestHandler requestHandler;

	@Override
	public void setNextRequestHandler(RequestHandler requestHandler) {
		this.requestHandler = requestHandler;

	}

	@Override
	public boolean process(RequestData requestData) {
		requestData.setMetaDBData("Meta Data from DB is populated");
		return requestHandler == null ? true : requestHandler.process(requestData);
	}
}

FileDataHandler.java

package com.tuturself.cor;

public class FileDataHandler implements RequestHandler {

	private RequestHandler requestHandler;

	@Override
	public void setNextRequestHandler(RequestHandler requestHandler) {
		this.requestHandler = requestHandler;

	}

	@Override
	public boolean process(RequestData requestData) {
		requestData.setMetaFileData("Meta Data from File is populated");
		return requestHandler == null ? true : requestHandler.process(requestData);
	}
}

CloudDataHandler.java

package com.tuturself.cor;

public class CloudDataHandler implements RequestHandler {

	private RequestHandler requestHandler;

	@Override
	public void setNextRequestHandler(RequestHandler requestHandler) {
		this.requestHandler = requestHandler;

	}

	@Override
	public boolean process(RequestData requestData) {
		requestData.setMetaCloudData("Meta Data from Cloud is populated");
		return requestHandler == null ? true : requestHandler.process(requestData);
	}
}

Now we will have a domain object, which is carrying data through the chain of handler named RequestData

package com.tuturself.cor;

public class RequestData {

	private String metaDBData;
	private String metaFileData;
	private String metaCloudData;

	public String getMetaDBData() {
		return metaDBData;
	}

	public void setMetaDBData(String metaDBData) {
		this.metaDBData = metaDBData;
	}

	public String getMetaFileData() {
		return metaFileData;
	}

	public void setMetaFileData(String metaFileData) {
		this.metaFileData = metaFileData;
	}

	public String getMetaCloudData() {
		return metaCloudData;
	}

	public void setMetaCloudData(String metaCloudData) {
		this.metaCloudData = metaCloudData;
	}

	@Override
	public String toString() {
		return "RequestData [metaDBData=" + metaDBData + ",\n "
				+ "metaFileData=" + metaFileData + ",\n metaCloudData="
				+ metaCloudData + "]";
	}
}

Now let us test the code, here we are creating object of each handler type, then creating a chian of handlers, and finally calling the process method of the first handler object in the chain. The first handler will call the next handler when it is finished, And this step will be continued until the chain is finished.

package com.tuturself.cor;

import java.util.*;

public class TestCOR {

	public static void main(String[] args) {
		RequestData requestData = new RequestData();
		List<RequestHandler> requestHandlers = new ArrayList<>();
		requestHandlers.add(new DBDataHandler());
		requestHandlers.add(new FileDataHandler());
		requestHandlers.add(new CloudDataHandler());
		// create the chain of Handler
		for (int i = 0; i < requestHandlers.size() - 1; i++) {
			requestHandlers.get(i).setNextRequestHandler(requestHandlers.get(i + 1));
		}
		requestHandlers.get(0).process(requestData);
		System.out.println(requestData);
	}
}

Output

RequestData [metaDBData=Meta Data from DB is populated,
 metaFileData=Meta Data from File is populated,
 metaCloudData=Meta Data from Cloud is populated]

Benefits of Chain of Responsibility Pattern: -

  1. Decouples the sender of the request and its receivers.
  2. Simplifies our object because it doesn't’t have to know the chain’s structure and keep direct references to its members.
  3. Allows to add or remove responsibilities dynamically by changing the members or order of the chain.

Drawbacks of Chain of Responsibility Pattern: -

  1. Execution of the request isn’t guaranteed; it may fail off the end of the chain if no object handles it.
  2. Can be hard to observe the run-time characteristics and debug.

 

core java 12

FOLLOW US ON LinkedIn



Explore Tutu'rself