Core Java

Chain of Responsibility Design Pattern Example

This article is part of our Academy Course titled Java Design Patterns.

In this course you will delve into a vast number of Design Patterns and see how those are implemented and utilized in Java. You will understand the reasons why patterns are so important and learn when and how to apply each one of them. Check it out here!

1. Chain of Responsibility Pattern

The Chain of Responsibility pattern is a behavior pattern in which a group of objects is chained together in a sequence and a responsibility (a request) is provided in order to be handled by the group. If an object in the group can process the particular request, it does so and returns the corresponding response. Otherwise, it forwards the request to the subsequent object in the group.

For a real life scenario, in order to understand this pattern, suppose you got a problem to solve. If you are able to handle it on your own, you will do so, otherwise you will tell your friend to solve it. If he’ll able to solve he will do that, or he will also forward it to some other friend. The problem would be forwarded until it gets solved by one of your friends or all your friends have seen the problem, but no one is able to solve it, in which case the problem stays unresolved.

Let’s address a real life scenario. Your company has got a contract to provide an analytical application to a health company. The application would tell the user about the particular health problem, its history, its treatment, medicines, interview of the person suffering from it etc, everything that is needed to know about it. For this, your company receives a huge amount of data. The data could be in any format, it could text files, doc files, excels, audio, images, videos, anything that you can think of would be there.

Now, your job is to save this data in the company’s database. Users will provide the data in any format and you should provide them a single interface to upload the data into the database. The user is not interested, not even aware, to know that how you are saving the different unstructured data?

The problem here is that you need to develop different handlers to save the various formats of data. For example, a text file save handler does not know how to save an mp3 file.

To solve this problem you can use the Chain of Responsibility design pattern. You can create different objects which process different formats of data and chain them together. When a request comes to a single object, it will check whether it can process and handle the specfic file format. If it can, it will process it; otherwise, it will forward it to the next object chained to it. This design pattern also decouples the user from the object that is serving the request; the user is not aware which object is actually serving its request.

Before solving the problem, let’s first know more about the Chain of Responsibility design pattern.

2. What is the Chain of Responsibility Pattern

The intent of this pattern is to avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. We chain the receiving objects and pass the request along the chain until an object handles it.

This pattern is all about connecting objects in a chain of notification; as a notification travels down the chain, it’s handled by the first object that is set up to deal with the particular notification.

When there is more than one objects that can handle or fulfill a client request, the pattern recommends giving each of these objects a chance to process the request in some sequential order. Applying the pattern in such a case, each of these potential handlers can be arranged in the form of a chain, with each object having a reference to the next object in the chain. The first object in the chain receives the request and decides either to handle the request or to pass it on to the next object in the chain. The request flows through all objects in the chain one after the other until the request is handled by one of the handlers in the chain or the request reaches the end of the chain without getting processed.

Figure 1
Figure 1

Handler

  1. Defines an interface for handling requests.
  2. (Optionally) Implements the successor link.

ConcreteHandler

  1. Handles requests it is responsible for.
  2. Can access its successor.
  3. If the ConcreteHandler can handle the request, it does so; otherwise it forwards the request to its successor.

Client

  1. Initiates the request to a ConcreteHandler object on the chain.

When a client issues a request, the request propagates along the chain until a ConcreteHandler object takes responsibility for handling it.

3. Implementing Chain of Responsibility

To implement the Chain of Responsibility in order to solve the above problem, we will create an interface, Handler.

package com.javacodegeeks.patterns.chainofresponsibility;

public interface Handler {

	public void setHandler(Handler handler);
	public void process(File file);
	public String getHandlerName();
}

The above interface contains two main methods, the setHandler and the process methods. The setHandler is used to set the next handler in the chain, whereas; the process method is used to process the request, only if the handler can able process the request. Optionally, we have the getHandlerName method which is used to return the handler’s name.

The handlers are designed to process files which contain data. The concrete handler checks if it’s able to handle the file by checking the file type, otherwise forwards to the next handler in the chain.

The File class looks like this.

package com.javacodegeeks.patterns.chainofresponsibility;

public class File {

	private final String fileName;
	private final String fileType;
	private final String filePath;

	public File(String fileName, String fileType, String filePath){
		this.fileName = fileName;
		this.fileType = fileType;
		this.filePath = filePath;
	}

	public String getFileName() {
		return fileName;
	}

	public String getFileType() {
		return fileType;
	}

	public String getFilePath() {
		return filePath;
	}

}

The File class creates simple file objects which contain the file name, file type, and the file path. The file type would be used by the handler to check if the file can be handled by them or not. If a handler can, it will process and save it, or it will forward it to the next handler in the chain.

Let’s see some concrete handlers now.

package com.javacodegeeks.patterns.chainofresponsibility;

public class TextFileHandler implements Handler {

	private Handler handler;
	private String handlerName;

	public TextFileHandler(String handlerName){
		this.handlerName = handlerName;
	}

	@Override
	public void setHandler(Handler handler) {
		this.handler = handler;
	}

	@Override
	public void process(File file) {

		if(file.getFileType().equals("text")){
			System.out.println("Process and saving text file... by "+handlerName);
		}else if(handler!=null){
			System.out.println(handlerName+" fowards request to "+handler.getHandlerName());
			handler.process(file);
		}else{
			System.out.println("File not supported");
		}

	}

	@Override
	public String getHandlerName() {
		return handlerName;
	}
}

The TextFileHandler is used to handle text files. It implements the Handler interface and overrides its three methods. It holds a reference to the next handler in the chain. In the process method, it checks the file type if the file type is text, it processes it or it forwards it to the next handler.

The other handlers are similar to the above handler.

package com.javacodegeeks.patterns.chainofresponsibility;

public class DocFileHandler implements Handler{

	private Handler handler;
	private String handlerName;

	public DocFileHandler(String handlerName){
		this.handlerName = handlerName;
	}

	@Override
	public void setHandler(Handler handler) {
		this.handler = handler;
	}

	@Override
	public void process(File file) {

		if(file.getFileType().equals("doc")){
			System.out.println("Process and saving doc file... by "+handlerName);
		}else if(handler!=null){
			System.out.println(handlerName+" fowards request to "+handler.getHandlerName());
			handler.process(file);
		}else{
			System.out.println("File not supported");
		}

	}

	@Override
	public String getHandlerName() {
		return handlerName;
	}

}

package com.javacodegeeks.patterns.chainofresponsibility;

public class AudioFileHandler implements Handler {

	private Handler handler;
	private String handlerName;

	public AudioFileHandler(String handlerName){
		this.handlerName = handlerName;
	}

	@Override
	public void setHandler(Handler handler) {
		this.handler = handler;
	}

	@Override
	public void process(File file) {

		if(file.getFileType().equals("audio")){
			System.out.println("Process and saving audio file... by "+handlerName);
		}else if(handler!=null){
			System.out.println(handlerName+" fowards request to "+handler.getHandlerName());
			handler.process(file);
		}else{
			System.out.println("File not supported");
		}

	}

	@Override
	public String getHandlerName() {
		return handlerName;
	}

}

package com.javacodegeeks.patterns.chainofresponsibility;

public class ExcelFileHandler implements Handler{

	private Handler handler;
	private String handlerName;

	public ExcelFileHandler(String handlerName){
		this.handlerName = handlerName;
	}

	@Override
	public void setHandler(Handler handler) {
		this.handler = handler;
	}

	@Override
	public void process(File file) {

		if(file.getFileType().equals("excel")){
			System.out.println("Process and saving excel file... by "+handlerName);
		}else if(handler!=null){
			System.out.println(handlerName+" fowards request to "+handler.getHandlerName());
			handler.process(file);
		}else{
			System.out.println("File not supported");
		}

	}

	@Override
	public String getHandlerName() {
		return handlerName;
	}
}

package com.javacodegeeks.patterns.chainofresponsibility;

public class ImageFileHandler implements Handler {

	private Handler handler;
	private String handlerName;

	public ImageFileHandler(String handlerName){
		this.handlerName = handlerName;
	}

	@Override
	public void setHandler(Handler handler) {
		this.handler = handler;
	}

	@Override
	public void process(File file) {

		if(file.getFileType().equals("image")){
			System.out.println("Process and saving image file... by "+handlerName);
		}else if(handler!=null){
			System.out.println(handlerName+" fowards request to "+handler.getHandlerName());
			handler.process(file);
		}else{
			System.out.println("File not supported");
		}

	}

	@Override
	public String getHandlerName() {
		return handlerName;
	}

}

package com.javacodegeeks.patterns.chainofresponsibility;

public class VideoFileHandler implements Handler {

	private Handler handler;
	private String handlerName;

	public VideoFileHandler(String handlerName){
		this.handlerName = handlerName;
	}

	@Override
	public void setHandler(Handler handler) {
		this.handler = handler;
	}

	@Override
	public void process(File file) {

		if(file.getFileType().equals("video")){
			System.out.println("Process and saving video file... by "+handlerName);
		}else if(handler!=null){
			System.out.println(handlerName+" fowards request to "+handler.getHandlerName());
			handler.process(file);
		}else{
			System.out.println("File not supported");
		}

	}

	@Override
	public String getHandlerName() {
		return handlerName;
	}
}


 
Now, let’s test the code above.

package com.javacodegeeks.patterns.chainofresponsibility;

public class TestChainofResponsibility {

	public static void main(String[] args) {
		File file = null;
		Handler textHandler = new TextFileHandler("Text Handler");
		Handler docHandler = new DocFileHandler("Doc Handler");
		Handler excelHandler = new ExcelFileHandler("Excel Handler");
		Handler audioHandler = new AudioFileHandler("Audio Handler");
		Handler videoHandler = new VideoFileHandler("Video Handler");
		Handler imageHandler = new ImageFileHandler("Image Handler");

		textHandler.setHandler(docHandler);
		docHandler.setHandler(excelHandler);
		excelHandler.setHandler(audioHandler);
		audioHandler.setHandler(videoHandler);
		videoHandler.setHandler(imageHandler);

		file = new File("Abc.mp3", "audio", "C:");
		textHandler.process(file);

		System.out.println("---------------------------------");

		file = new File("Abc.jpg", "video", "C:");
		textHandler.process(file);

		System.out.println("---------------------------------");

		file = new File("Abc.doc", "doc", "C:");
		textHandler.process(file);

		System.out.println("---------------------------------");

		file = new File("Abc.bat", "bat", "C:");
		textHandler.process(file);
	}

}

The above program will have the following output.

Text Handler fowards request to Doc Handler
Doc Handler fowards request to Excel Handler
Excel Handler fowards request to Audio Handler
Process and saving audio file... by Audio Handler
---------------------------------
Text Handler fowards request to Doc Handler
Doc Handler fowards request to Excel Handler
Excel Handler fowards request to Audio Handler
Audio Handler fowards request to Video Handler
Process and saving video file... by Video Handler
---------------------------------
Text Handler fowards request to Doc Handler
Process and saving doc file... by Doc Handler
---------------------------------
Text Handler fowards request to Doc Handler
Doc Handler fowards request to Excel Handler
Excel Handler fowards request to Audio Handler
Audio Handler fowards request to Video Handler
Video Handler fowards request to Image Handler
File not supported

In the example above, first we created different handlers and chained them. The chain starts from the text handler, which is used to process text files, to the doc handler and so on, till the last handler, the image handler.

Then we created different file objects and passed it to the text handler. If the file can be processed by the text handler it does that, otherwise it forwards the file to the next chained handler. You can see in the output how the requested file was forwarded by the chained objects until it reached the appropriate handler.

Also, please note down, we have not created a handler to process a bat file. So, it passes through all the handlers and results in the output – “File not supported”.

The client code is decoupled from the served object. It only sends the request, and the request gets served by any one of the handlers in the chain or does not get processed in case there is support for it.

4. When to use the Chain of Responsibility Pattern

Use Chain of Responsibility when

  1. More than one objects may handle a request, and the handler isn’t known a priori. The handler should be ascertained automatically.
  2. You want to issue a request to one of several objects without specifying the receiver explicitly.
  3. The set of objects that can handle a request should be specified dynamically.

5. Chain of Responsibility in JDK

The following are the usages of the Chain of Responsibility Pattern in Java.

  1. java.util.logging.Logger#log()
  2. javax.servlet.Filter#doFilter()

6. Download the Source Code

This was a lesson on the Chain of Responsibility Pattern. You may download the source code here: ChainofResponsibility-Project

Rohit Joshi

Rohit Joshi is a Senior Software Engineer from India. He is a Sun Certified Java Programmer and has worked on projects related to different domains. His expertise is in Core Java and J2EE technologies, but he also has good experience with front-end technologies like Javascript, JQuery, HTML5, and JQWidgets.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button