Snippets Computer Science / Design Patterns / Chain Of Responsibility

Chain Of Responsibility Design Pattern

By Marcelo Fernandes Sep 7, 2017

Check List

  1. The base class maintains a "next" pointer.
  2. Each derived class implements its contribution for handling the request.
  3. If the request needs to be "passed on", then the derived class "calls back" to the base class, which delegates to the "next" pointer.
  4. The client (or some third party) creates and links the chain (which may include a link from the last node to the root node).
  5. The client "launches and leaves" each request with the root of the chain.
  6. Recursive delegation produces the illusion of magic.

Rules of Thumb

  • Chain of Responsibility, Command, Mediator, and Observer, address how you can decouple senders and receivers, but with different trade-offs. Chain of Responsibility passes a sender request along a chain of potential receivers.
  • Chain of Responsibility can use Command to represent requests as objects.
  • Chain of Responsibility is often applied in conjunction with Composite. There, a component's parent can act as its successor.


"""
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.
"""

import abc


class Handler(metaclass=abc.ABCMeta):
    """
    Define an interface for handling requests.
    Implement the successor link.
    """

    def __init__(self, successor=None):
        self._successor = successor

    @abc.abstractmethod
    def handle_request(self):
        pass


class ConcreteHandler1(Handler):
    """
    Handle request, otherwise forward it to the successor.
    """

    def handle_request(self):
        if True:  # if can_handle:
            pass
        elif self._successor is not None:
            self._successor.handle_request()


class ConcreteHandler2(Handler):
    """
    Handle request, otherwise forward it to the successor.
    """

    def handle_request(self):
        if False:  # if can_handle:
            pass
        elif self._successor is not None:
            self._successor.handle_request()


def main():
    concrete_handler_1 = ConcreteHandler1()
    concrete_handler_2 = ConcreteHandler2(concrete_handler_1)
    concrete_handler_2.handle_request()


if __name__ == "__main__":
    main()


Notes


References:

link 1