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 :

Ads by Google
"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.

Ads by Google

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

Participants in chain of responsibility

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.

Chain of responsibility class diagram

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 !!

12 thoughts on “Chain of responsibility design pattern”

  1. Also, the chain of responsibility is sometimes better handled with the handleRequest(…) method returning a boolean: true if the request is handled, false otherwise.

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

    Then, a class possessing a Collection of the handlers, the call is simply

    boolean isHandled = false;

    for(SupportServiceItf handler : collectionOfHanders){
    isHandled = handler.handle(serviceRequest);
    if(isHandled){
    break;
    }
    }

    if(!isHandled){
    // something may need to be done because the request never got handled
    }

    This also avoids handlers being aware of each other in what is essentially a singly linked list. I believe that it is generally better that the handlers be unaware of each other’s existence and be isolated from each other.

    There is also the additional difficulty that the classes have paired getters and setters: getNext()/setNext(…) and getHandler)/setHandler(…). Once a field of a class is made read/write, the field is now public. For safety, I believe that this example would be made better if the classes were made immutable.

    However, if the approach of putting the handler in a Collection were used, then the handler code is simplified. Any re-arrangements can be done by a management class, which I believe to be a much more flexible approach.

      1. But if Management class will be delegating responsibility from one class t another then delegator design pattern or kind of factory design pattern

        Agree there is tight coupling in class that is not good.We have to put example that will has High cohesion and low Coupling.

        one more Question Struts 2 is based on this pattern??/

        1. I have agreed only on “immutable” part. It makes sense to me make handlers immutable. I also feel that Management class will not add any benefit here.

          Tight coupling? I will not agree easily. This design has lots of benefit e.g. directly passing request to certain handler and a request handled by even multiple handlers.(You may come across this situation also where request may need multiple handlers.) In code there is no references to other classes. Everything is done in configuration file. Where is tight coupling? You have configure your system somewhere, right?

          I really do not find struts 2 a good candidate for example of this pattern. Reason? CoR pattern has multiple handlers and one of them handle the request. In struts2, actual handler is Action class and interceptors actually works as decorators or observers and sometimes doing some more work. They are more file filters in servlet technology. They do not handle the request. So, I will prefer not to mention struts 2 as example of chain of responsibility pattern.

  2. The example would be better by reconsidering the design of the ServiceRequest class. By having both a get a set method for each of its private fields type and conclusion, its encapsulation is broken. The fields are effectively public. I believe that the ServiceRequest ought to be made invariant. The fields are final and private. They are set through the constructor. The set methods are removed, leaving only the get methods. As it stands, and handler could accidentally change the service request resulting in an error which requires debugging.

  3. I have couple of Questions.
    1 Who will define next level.
    2. What will be the flow of request. FrontDesk -> Manager or FrontDesk -> SupportService -> Manger ?

      1. Lets say in our application we have 5 different type of chains and those may got change on frequent basics so as per your implantation we have to update next level for all the supervisor each time… if we are not using Spring in that app…
        2. What will be implementations for 5 chains..

        1. How first chain will be different from other four chains. Sequence.. right?? But sequence doesn’t matter here. Concept is that every object in chain will get it’s chance to handle the request. Whoever is capable, will handle it. Otherwise just forward to next handler and let him try.

Note:- In comment box, please put your code inside [java] ... [/java] OR [xml] ... [/xml] tags otherwise it may not appear as intended.

Want to ask any question? Or suggest anything?