Chain of Responsibility – Python Design Patterns

Chain of Responsibility method is Behavioral design pattern and it is the object-oriented version of if … elif … elif … else and make us capable to rearrange the condition-action blocks dynamically at the run-time. It allows us to pass the requests along the chain of handlers. The processing is simple, whenever any handler received the request it has two choices either to process it or pass it to the next handler in the chain.
This pattern aims to decouple the senders of a request from its receivers by allowing the request to move through chained receivers until it is handled.

chain-of-responsibility-method

Problem without using Chain of Responsibility Method

Imagine you are building a simple website that takes input strings and tells about the various properties of the strings such as is the string palindrome? is string upperCase? is string lowerCase? and many other properties too. After the complete planning, you decide that these checks for the input string should be performed sequentially. So, here the problem arises for the developer that he/she has to implement such an application that can decide on run-time which action should be performed next.

Solution using Chain of Responsibility Method

Chain of Responsibility Method provides the solution for the above-described problem. It creates a separate Abstract handler which is used to handle the sequential operations which should be performed dynamically. For eg., we create four handlers named as FirstConcreteHandler, SecondConcreteHandler, ThirdConcreteHandler, and Defaulthandler and calls them sequentially from the user class.

filter_none

edit
close

play_arrow

link
brightness_4
code

class AbstractHandler(object):
  
    """Parent class of all concrete handlers"""
  
    def __init__(self, nxt):
  
        """change or increase the local variable using nxt"""
  
        self._nxt = nxt
  
    def handle(self, request):
  
        """It calls the processRequest through given request"""
  
        handled = self.processRequest(request)
  
        """case when it is not handled"""
  
        if not handled:
            self._nxt.handle(request)
  
    def processRequest(self, request):
  
        """throws a NotImplementedError"""
  
        raise NotImplementedError('First implement it !')
  
  
class FirstConcreteHandler(AbstractHandler):
  
    """Concrete Handler # 1: Child class of AbstractHandler"""
  
    def processRequest(self, request):
  
        '''return True if request is handled '''
  
        if 'a' < request <= 'e':
            print("This is {} handling request '{}'".format(self.__class__.__name__, request))
            return True
  
  
class SecondConcreteHandler(AbstractHandler):
  
    """Concrete Handler # 2: Child class of AbstractHandler"""
  
    def processRequest(self, request):
  
        '''return True if the request is handled'''
  
        if 'e' < request <= 'l':
            print("This is {} handling request '{}'".format(self.__class__.__name__, request))
            return True
  
class ThirdConcreteHandler(AbstractHandler):
  
    """Concrete Handler # 3: Child class of AbstractHandler"""
  
    def processRequest(self, request):
  
        '''return True if the request is handled'''
  
        if 'l' < request <= 'z':
            print("This is {} handling request '{}'".format(self.__class__.__name__, request))
            return True
  
class DefaultHandler(AbstractHandler):
  
    """Default Handler: child class from AbstractHandler"""
  
    def processRequest(self, request):
  
        """Gives the message that th request is not handled and returns true"""
  
        print("This is {} telling you that request '{}' has no handler right now.".format(self.__class__.__name__,
                                                                                          request))
        return True
  
  
class User:
  
    """User Class"""
  
    def __init__(self):
  
        """Provides the sequence of handles for the users"""
  
        initial = None
  
        self.handler = FirstConcreteHandler(SecondConcreteHandler(ThirdConcreteHandler(DefaultHandler(initial))))
  
    def agent(self, user_request):
  
        """Iterates over each request and sends them to specific handles"""
  
        for request in user_request:
            self.handler.handle(request)
  
"""main method"""
  
if __name__ == "__main__":
  
    """Create a client object"""
    user = User()
  
    """Create requests to be processed"""
  
    string = "GeeksforGeeks"
    requests = list(string)
  
    """Send the requests one by one, to handlers as per the sequence of handlers defined in the Client class"""
    user.agent(requests)

chevron_right


Output

This is DefaultHandler telling you that request 'G' has no handler right now.
This is FirstConcreteHandler handling request 'e'
This is FirstConcreteHandler handling request 'e'
This is SecondConcreteHandler handling request 'k'
This is ThirdConcreteHandler handling request 's'
This is SecondConcreteHandler handling request 'f'
This is ThirdConcreteHandler handling request 'o'
This is ThirdConcreteHandler handling request 'r'
This is DefaultHandler telling you that request 'G' has no handler right now.
This is FirstConcreteHandler handling request 'e'
This is FirstConcreteHandler handling request 'e'
This is SecondConcreteHandler handling request 'k'
This is ThirdConcreteHandler handling request 's'

Class Diagram

Class Diagram for Chain of Responibility Method



Chain-Of-Responsibility_class_diagram

Advantages

  • Single Responsibility Principle: It’s easy to decouple the classes here that invoke operations from classes that perform operations.
  • Open/Closed principle: We can introduce the new code classes without breaking th existing client code.
  • Increases Flexibility: While giving the responsibilities to the objects, it increases the flexibility of the code.

Disadvantages

  • Unassured about request: This method doesn’t provide any assurance whether the object will be recieved or not.
  • Spotting characteristics: Due to debugging, it becomes difficult task to observe the characteristics of operations.
  • Depriciated System Performance: It might affect the system’s performance due to continuous cycle calls

Applicability

  • Processing several handlers in order: Chain of responsibilty method helps very much when it is required to process several handlers in a particular order because the linking is possible in any order
  • Decoupling requests: This method is generally used when you want to decouple the request’s sender and receiver.
  • Unspecified handlers: When you don’t want to specify handlers in the code, it is always preferred to use the Chain of Responibility.

Further Read – Chain of Responibility in Java

Attention geek! Strengthen your foundations with the Python Programming Foundation Course and learn the basics.

To begin with, your interview preparations Enhance your Data Structures concepts with the Python DS Course.




My Personal Notes arrow_drop_up

Engineering student who loves competitive programming too much

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.



Improved By : Akanksha_Rai

Article Tags :

1


Please write to us at contribute@geeksforgeeks.org to report any issue with the above content.