Chain of Responsibility Pattern

Chain of responsibility pattern is a behavioral pattern consisting of a source of command objects and a series of processing objects. Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain.

In other words, in this pattern, normally each receiver contains reference to another receiver and if one object cannot handle the request then it passes the same to the next receiver and so on.

A mechanism also exists for adding new processing objects to the end of this chain.

The intent of this pattern, according to Design Patterns by Gamma et al, is to

  • Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.

This pattern decouples sender and receiver of a request based on type of request.

 

Structure

The following figure depicts the general working in chain of responsibility.

chain of responsibility general

Figure: General flow in Chain of Responsibility

Chain of Responsibility pattern 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.

The objects in the chain just need to know how to forward the request to other objects.  This decoupling is a huge advantage, as you can change the chain at runtime. 

The following UML diagram shows the structure of chain of responsibility pattern.

chain of responsibility structure

Figure: Chain of responsibility structure

Participants

  • Handler – defines an interface for handling requests
  • RequestHandler – handles the requests it is responsible for i.e. if it can handle the request it does so, otherwise it sends the request to its successor
  • Client – sends commands to the first object in the chain that may handle the command

If the ConcreteHandler cannot handle the request, it passes the request onto it's successor, which it maintains a link to. 

 

Example

To illustrate this pattern, consider a scenario where we have an order processing application that receives orders. In our application we have special logic for large orders with an amount larger than $30000. For small orders, we handle international orders differently than domestic orders.

The following UML diagram shows the class diagram for above application.

chain of responsibility example

Figure: Chain of Responsibility Example

 

Java Implementation

Let us start with domains.

/**
 * domain
 */
public class Customer {
    private String name;

    public Customer(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class Order {
    private int orderNumber;
    private double totalAmount;
    private boolean international;

    private Customer customer;

    public Order(int orderNumber, double totalAmount, boolean international, Customer customer) {
        this.orderNumber = orderNumber;
        this.totalAmount = totalAmount;
        this.international = international;
        this.customer = customer;
    }

    public int getOrderNumber() {
        return orderNumber;
    }

    public void setOrderNumber(int orderNumber) {
        this.orderNumber = orderNumber;
    }

    public double getTotalAmount() {
        return totalAmount;
    }

    public void setTotalAmount(double totalAmount) {
        this.totalAmount = totalAmount;
    }

    public boolean isInternational() {
        return international;
    }

    public void setInternational(boolean international) {
        this.international = international;
    }

    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }
}

Now, lets create a handler class.

/**
 * Handler
 */
public abstract class OrderReceiver {
    protected OrderReceiver successor;
    protected Order order;

    public void setSuccessor(OrderReceiver successor) {
        this.successor = successor;
    }

    abstract public void handleOrder(Order request);
}

The concrete classes for handler above are:

/**
 * Concrete handler
 */
public class LargeOrderHandler extends OrderReceiver {
    @Override
    public void handleOrder(Order request) {
        if(request.getTotalAmount() > 30000){
            System.out.println("Large order handle");
        }else if(successor != null){
            successor.handleOrder(request);
        }
    }
}
public class InternationalOrderHandler extends OrderReceiver {
    @Override
    public void handleOrder(Order order) {
        if(order.isInternational()){
            System.out.println("International Order");
        }else if(successor != null){
            successor.handleOrder(order);
        }
    }
}
public class DomesticOrderHandler extends OrderReceiver {
    @Override
    public void handleOrder(Order order) {
        if(!order.isInternational()){
            System.out.println("Domestic Order handler.");
        }
    }
}

Now, the client for our application is:

/**
 * Client
 */
public class Application {
    public static void main(String[] args) {
        OrderReceiver largeOrder =  new LargeOrderHandler();
        OrderReceiver international = new InternationalOrderHandler();
        OrderReceiver domestic = new DomesticOrderHandler();

        largeOrder.setSuccessor(international);
        international.setSuccessor(domestic);

        Customer customer1 = new Customer("Yogen");
        Customer customer2 = new Customer("Robert");

        Order order1 = new Order(1001,12300,false,customer1);
        Order order2 = new Order(1002,1300,true,customer2);

        largeOrder.handleOrder(order1);
        largeOrder.handleOrder(order2);
    }
}

The output of the program is:

Domestic Order handler.
International Order

Here, since the order is not large enough, the LargeOrderHandler passes the control to InternationalOrderHandler which in turn passes request to DomesticOrderHandler.

State Pattern
Factory Method Pattern