Chain of Responsibility Design Pattern

The Chain of Responsibility is known as a behavioral pattern. The main objective of this pattern is that it avoids coupling the sender of the request to the receiver, giving more than one object the opportunity to handle the request. The core logic defined by GoF is :

"Gives more than one object an opportunity to handle a request by linking receiving objects together."

Chain of Responsibility allows a number of classes to attempt to handle a request, independently of any other object along the chain. Once the request is handled, it completes it’s journey through the chain.

Extra handlers can be added or removed from chain without modifying the logic inside any of concrete handler.

Sections in this post:

Suggested usage
Participants in the solution
Sample problem to be solved
Proposed solution
Class diagram of participants
Sourcecode of participants
Test the application
Download sourecode link
Reference implementations in JDK

Suggested usage

This pattern is recommended when multiple objects can handle a request and the handler doesn’t have to be a specific object. Also, handler is determined at runtime. Please note that that a request not handled at all by any handler is a valid use case.

For example, event handling mechanism in windows OS where events can be generated from either mouse, keyboard or some automatic generated events. All such events can be handled by multiple handlers and correct handler is found on runtime.

More general exampe can be a service request to call center. This request can be handled at front desk level, supervisor level or any higher level. Correct handler of request is only known at runtime when request is traversing at various levels. We will solve this scenario in this post.

Participants in the solution

1) Handler : This can be an interface which will primarily recieve the request and dispatches the request to chain of handlers. It has reference of only first handler in the chain and does not know anything about rest of the handlers.

2) Concrete handlers : These are actual handlers of the request chained in some sequential order.

3) Client : Originator of request and this will access the handler to handle it.

participants_of_chain_of_responsibility-4978976
Participants in chain of responsibility

Sample problem to be solved

The problem statement is to design a system for support service system consisting of front desk, supervisor, manager and director. Any client can call to front desk and will ask for a solution. If front desk is able to solve the issue, it will; otherwise will pass to supervisor. Similarly, supervisor will try to solve the issue and if he is able to then he will solve; else pass to manager. Same way, manager will either solve the issue or pass to director. Director will either solve the issue or reject it.

Proposed solution

Above problem is a good candidate for using chain of responsibility pattern. We can define the handler at each level i.e. support desk, supervisor, manager and director. Then we can define a chain for handling the support request. This chain must follow the sequence:

Support desk > supervisor > manager > director

Above chain can be managed using programmatic solutions in java also, but in this tutorial i am using spring for injecting the dependencies and thus forming this chain. Also, System will first assign the request to front desk only.

Class diagram of participants

I have draw the structure of all entities involved in the solution as below.

chainofresponsibility_classdiagram-2377740
Support service system : class diagram

Sourcecode of participants

Below is the sourcecode of all participants involved in support service implementation using chain of responsibility design pattern:

ServiceLevel.java

package com.howtodoinjava;

public enum ServiceLevel
{
	LEVEL_ONE, LEVEL_TWO, LEVEL_THREE, LEVEL_FOUR, INVALID_REQUEST
}

ServiceRequest.java

package com.howtodoinjava.data;

import com.howtodoinjava.ServiceLevel;

public class ServiceRequest {

	private ServiceLevel type;
	private String conclusion = null;

	public ServiceLevel getType() {
		return type;
	}
	public void setType(ServiceLevel type) {
		this.type = type;
	}
	public String getConclusion() {
		return conclusion;
	}
	public void setConclusion(String conclusion) {
		this.conclusion = conclusion;
	}
}

SupportServiceItf.java

package com.howtodoinjava.handler;

import com.howtodoinjava.data.ServiceRequest;

public interface SupportServiceItf
{
	public void handleRequest(ServiceRequest request);
}

SupportService.java

package com.howtodoinjava.handler;

import com.howtodoinjava.data.ServiceRequest;

public class SupportService implements SupportServiceItf {

	private SupportServiceItf handler = null;

	public SupportServiceItf getHandler() {
		return handler;
	}

	public void setHandler(SupportServiceItf handler) {
		this.handler = handler;
	}

	@Override
	public void handleRequest(ServiceRequest request) {
		handler.handleRequest(request);
	}
}

FrontDeskSupport.java

package com.howtodoinjava.handler;

import com.howtodoinjava.ServiceLevel;
import com.howtodoinjava.data.ServiceRequest;

public class FrontDeskSupport implements SupportServiceItf {

	private SupportServiceItf next = null;
	public SupportServiceItf getNext() {
		return next;
	}
	public void setNext(SupportServiceItf next) {
		this.next = next;
	}

	@Override
	public void handleRequest(ServiceRequest service) {
		if(service.getType() == ServiceLevel.LEVEL_ONE)
		{
			service.setConclusion("Front desk solved level one reuqest !!");
		}
		else
		{
			if(next != null){
				next.handleRequest(service);
			}
			else
			{
				throw new IllegalArgumentException("No handler found for :: " + service.getType());
			}
		}
	}
}

SupervisorSupport.java

package com.howtodoinjava.handler;

import com.howtodoinjava.ServiceLevel;
import com.howtodoinjava.data.ServiceRequest;

public class SupervisorSupport implements SupportServiceItf {

	private SupportServiceItf next = null;
	public SupportServiceItf getNext() {
		return next;
	}
	public void setNext(SupportServiceItf next) {
		this.next = next;
	}

	@Override
	public void handleRequest(ServiceRequest request) {
		if(request.getType() == ServiceLevel.LEVEL_TWO)
		{
			request.setConclusion("Supervisor solved level two reuqest !!");
		}
		else
		{
			if(next != null){
				next.handleRequest(request);
			}
			else
			{
				throw new IllegalArgumentException("No handler found for :: " + request.getType());
			}
		}
	}
}

ManagerSupport.java

package com.howtodoinjava.handler;

import com.howtodoinjava.ServiceLevel;
import com.howtodoinjava.data.ServiceRequest;

public class ManagerSupport implements SupportServiceItf {

	private SupportServiceItf next = null;
	public SupportServiceItf getNext() {
		return next;
	}
	public void setNext(SupportServiceItf next) {
		this.next = next;
	}

	@Override
	public void handleRequest(ServiceRequest request) {
		if(request.getType() == ServiceLevel.LEVEL_THREE)
		{
			request.setConclusion("Manager solved level three reuqest !!");
		}
		else
		{
			if(next != null){
				next.handleRequest(request);
			}
			else
			{
				throw new IllegalArgumentException("No handler found for :: " + request.getType());
			}
		}
	}
}

DirectorSupport.java

package com.howtodoinjava.handler;

import com.howtodoinjava.ServiceLevel;
import com.howtodoinjava.data.ServiceRequest;

public class DirectorSupport implements SupportServiceItf {

	private SupportServiceItf next = null;
	public SupportServiceItf getNext() {
		return next;
	}
	public void setNext(SupportServiceItf next) {
		this.next = next;
	}

	@Override
	public void handleRequest(ServiceRequest request) {
		if(request.getType() == ServiceLevel.LEVEL_FOUR)
		{
			request.setConclusion("Director solved level four reuqest !!");
		}
		else
		{
			if(next != null){
				next.handleRequest(request);
			}
			else
			{
				request.setConclusion("You problem is none of our business");
				throw new IllegalArgumentException("You problem is none of our business :: " + request.getType());
			}
		}
	}
}

applicationConfig.xml

<?xml  version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:lang="http://www.springframework.org/schema/lang"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop/ http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context/ http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/jee/ http://www.springframework.org/schema/jee/spring-jee.xsd
        http://www.springframework.org/schema/lang/ http://www.springframework.org/schema/lang/spring-lang.xsd
        http://www.springframework.org/schema/tx/ http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/util/ http://www.springframework.org/schema/util/spring-util.xsd">

    <bean id="supportService" class="com.howtodoinjava.handler.SupportService">
        <property name="handler" ref="frontDeskSupport"></property>
    </bean>

    <bean id="frontDeskSupport" class="com.howtodoinjava.handler.FrontDeskSupport">
        <property name="next" ref="supervisorSupport"></property>
    </bean>
    <bean id="supervisorSupport" class="com.howtodoinjava.handler.SupervisorSupport">
        <property name="next" ref="managerSupport"></property>
    </bean>
    <bean id="managerSupport" class="com.howtodoinjava.handler.ManagerSupport">
        <property name="next" ref="directorSupport"></property>
    </bean>
    <bean id="directorSupport" class="com.howtodoinjava.handler.DirectorSupport"></bean>

</beans>

Test the application

I will pass various level of support requests down the chain and they will be handled by correct level. Any invalid request will be rejected as planned for.

package com.howtodoinjava;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.howtodoinjava.data.ServiceRequest;
import com.howtodoinjava.handler.SupportService;

public class TestChainOfResponsibility {
	public static void main(String[] args)
	{
		ApplicationContext context = new ClassPathXmlApplicationContext("application-config.xml");
		SupportService supportService = (SupportService) context.getBean("supportService");

		ServiceRequest request = new ServiceRequest();
		request.setType(ServiceLevel.LEVEL_ONE);
		supportService.handleRequest(request);
		System.out.println(request.getConclusion());

		request = new ServiceRequest();
		request.setType(ServiceLevel.LEVEL_THREE);
		supportService.handleRequest(request);
		System.out.println(request.getConclusion());

		request = new ServiceRequest();
		request.setType(ServiceLevel.INVALID_REQUEST);
		supportService.handleRequest(request);
		System.out.println(request.getConclusion());
	}
}

<strong>Output:</strong>

Front desk solved level one reuqest !!
Manager solved level three reuqest !!
Exception in thread "main" java.lang.IllegalArgumentException: You problem is none of our business :: INVALID_REQUEST

To download the sourecode of above example application, click on below link.

Sourcecode download

Reference implementations in JDK

The doFilter method of the Filter is called by the container each time a request/response pair is passed through the chain due to a client request for a resource at the end of the chain. The FilterChain passed in to this method allows the Filter to pass on the request and response to the next entity in the chain.

If the logger is currently enabled for the given message level then the given message is forwarded to all the registered output Handler objects.

I hope that this post post has added some knowledge in your understanding of chain of responsibility pattern. If you have any query, post a comment.

Happy Learning !!

Comments

Subscribe
Notify of
guest
19 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode