Chain of Responsibility pattern in Java and Python

Behavioral Design Patterns provide solution for the better interaction between objects and how to provide lose coupling and flexibility to extend easily. Following design patterns come under this category.

Chain of Responsibility pattern helps building a chain of objects. Request enters from one end and keeps going from object to object till it finds the suitable handler.

Chain of Responsibility pattern is used to achieve lose coupling in software design where a request from client is passed to a chain of objects to process them. Then the object in the chain will decide themselves who will be processing the request and whether the request is required to be sent to the next object in the chain or not.

The Chain of Responsibility pattern allows an object to send a command without knowing what object will receive and handle it.

In this pattern, normally each receiver contains reference to another receiver. If one object cannot handle the request then it passes the same to the next receiver and so on. 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.

dp_chain_of_responsability.gif

In the diagram above some explanations are needed on what is the role of every class:

  • Handler defines an interface for handling requests.
  • RequestHandler handles the requests it is responsible for. 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.

When to use the Chain of Responsibility pattern

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

Now, let's look at an example of the chain of responsibility pattern. Rather than an interface, I'll use an abstract base class as the handler so that subclasses can utilize the implemented setNextHandler() method. This abstract class is called PlanetHandler. Concrete handlers that subclass PlanetHandler need to implement the handleRequest() method.

Java

File PlanetHandler.java.

public abstract class PlanetHandler {
    PlanetHandler nextHandler;

    public void setNextHandler(PlanetHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    public abstract void handleRequest(PlanetEnum request);
}

This example will utilize an enum of the planets called PlanetEnum.

File PlanetEnum.java

public enum PlanetEnum {
    MERCURY, VENUS, EARTH, MARS, JUPITER, SATURN, URANUS, NEPTUNE;
}

MercuryHandler subclasses PlanetHandler and implements the handleRequest() method. If the request is a PlanetEnum.MERCURY, it will handle the request. Otherwise, the request is passed to next handler if the next handler exists.

public class MercuryHandler extends PlanetHandler {
    public void handleRequest(PlanetEnum request) {
        if (request == PlanetEnum.MERCURY) {
            System.out.println("MercuryHandler handles " + request);
            System.out.println("Mercury is hot.\n");
        } else {
            System.out.println("MercuryHandler doesn't handle " + request);
            if (nextHandler != null) {
                nextHandler.handleRequest(request);
            }
        }
    }
}

VenusHandler is similar to MercuryHandler, except it handles PlanetEnum.VENUS requests.

File VenusHandler.java.

public class VenusHandler extends PlanetHandler {
    public void handleRequest(PlanetEnum request) {
        if (request == PlanetEnum.VENUS) {
            System.out.println("VenusHandler handles " + request);
            System.out.println("Venus is poisonous.\n");
        } else {
            System.out.println("VenusHandler doesn't handle " + request);
            if (nextHandler != null) {
                nextHandler.handleRequest(request);
            }
        }
    }
}

EarthHandler similarly handles PlanetEnum.EARTH requests.

File EarthHandler.java.

public class EarthHandler extends PlanetHandler {
    public void handleRequest(PlanetEnum request) {
        if (request == PlanetEnum.EARTH) {
            System.out.println("EarthHandler handles " + request);
            System.out.println("Earth is comfortable.\n");
        } else {
            System.out.println("EarthHandler doesn't handle " + request);
            if (nextHandler != null) {
                nextHandler.handleRequest(request);
            }
        }
    }
}

The Demo class is the client class. It creates the chain of handlers, starting with MercuryHandler, then VenusHandler, and then EarthHandler. The setUpChain() method returns the chain to main() via a PlanetHandler reference. Four requests are made of the chain, where the requests are VENUS, MERCURY, EARTH, and JUPITER.

File Demo.java.

public class Demo {
    public static void main(String[] args) {
        PlanetHandler chain = setUpChain();

        chain.handleRequest(PlanetEnum.VENUS);
        chain.handleRequest(PlanetEnum.MERCURY);
        chain.handleRequest(PlanetEnum.EARTH);
        chain.handleRequest(PlanetEnum.JUPITER);
    }

    public static PlanetHandler setUpChain() {
        PlanetHandler mercuryHandler = new MercuryHandler();
        PlanetHandler venusHandler = new VenusHandler();
        PlanetHandler earthHandler = new EarthHandler();

        mercuryHandler.setNextHandler(venusHandler);
        venusHandler.setNextHandler(earthHandler);

        return mercuryHandler;
    }
}

Python 3

import abc
from enum import IntEnum

class PlanetEnum(IntEnum):
    MERCURY = 1
    VENUS = 2
    EARTH = 3
    MARS =4
    JUPITER = 5
    SATURN = 6
    URANUS = 7
    NEPTUNE = 8


class PlanetHandler(metaclass=abc.ABCMeta):
    def __init__(self):
        self.next_handler = None

    @abc.abstractmethod
    def handle_request(self, request):
        pass

    def set_next_handler(self, handler):
        self.next_handler = handler


class MercuryHandler(PlanetHandler):
    def handle_request(self, request):
        if request is PlanetEnum.MERCURY:
            print("MercuryHandler handles " + request.name)
            print("Mercury is hot.")
        else:
            print("MercuryHandler doesn't handle " + request.name)
            if self.next_handler is not None:
                self.next_handler.handle_request(request)


class VenusHandler(PlanetHandler):
    def handle_request(self, request):
        if request is PlanetEnum.VENUS:
            print("VenusHandler handles " + request.name)
            print("Venus is poisonous.")
        else:
            print("VenusHandler doesn't handle " + request.name)
            if self.next_handler is not None:
                self.next_handler.handle_request(request)


class EarthHandler(PlanetHandler):
    def handle_request(self, request):
        if request is PlanetEnum.EARTH:
            print("EarthHandler handles " + request.name)
            print("Earth is comfortable.")
        else:
            print("EarthHandler doesn't handle " + request.name)
            if self.next_handler is not None:
                self.next_handler.handle_request(request)


def set_up_chain():
    mercury_handler = MercuryHandler()
    venus_handler = VenusHandler()
    earth_handler = EarthHandler()

    mercury_handler.set_next_handler(venus_handler)
    venus_handler.set_next_handler(earth_handler)

    return mercury_handler


if __name__ == '__main__':
    chain = set_up_chain()

    chain.handle_request(PlanetEnum.VENUS)
    chain.handle_request(PlanetEnum.MERCURY)
    chain.handle_request(PlanetEnum.EARTH)
    chain.handle_request(PlanetEnum.JUPITER)
comments powered by Disqus