A chain of responsibility
Tags: designpatterns, computerscience, oop, python
Published: 2020-07-30 10:26:48

Hi Everyone,

Today I got an impetus to write about a design pattern that I have been using quite often recently. It is called the Chain of Responsibility pattern which is one of the Behavioral design patterns.

I believe this pattern is easier to understand. Even then, it took me a while to get a good grip on it until I got a real scenario that fits the spectrum. Hence, I decided to share it here for the community to have a solid reference.

## Chain of Responsibility (CoR)

> Chain of Responsibility is a behavioral design pattern that lets you pass requests along a chain of handlers. Upon receiving a request, each handler decides either to process the request or to pass it to the next handler in the chain - [Refactoring Guru](https://refactoring.guru/design-patterns/chain-of-responsibility)

I always thought about implementing a set of operations using a simple `for` loop and couldn't get the point of CoR. Until I saw the below point in the definition.

> each handler decides either to process the request or to pass it to the next handler

This is all about it. The objects in the chain can decide whether to move forward or break the chain. However, in case of using for loops, it is always the client code that makes the decision. You can think about CoR as a traversal through a LinkedList data structure in search of a particular value.

## Problem/Analogy
Let us imagine that you are building a REST API and your APIs have various access control levels. Some of your APIs are exposed to certain users with a particular role, say `REVIEWER` whereas some others are public. On top of that, `ADMIN` must have access to all of them.

> I am using Python programming language to illustrate the examples. The logic applies to all programming languages that support OOP.

The immediate thought will be to use `if-else` conditionals to accomplish this inside your APIs.
```
if 'ADMIN' not in user.roles and 'REVIEWER' not in user.roles:
    raise HTTPForbidden("You do not have permission.")
```

However, as you can already infer, this approach will become unmaintainable when you add more roles that will lead to spaghetti code. Also, it violates Open/Closed principle. What if you need to add/remove a role check for an API?

## Solution

### Define a chain interface/base class

Let us define an interface that underlines the behaviors of our role checks.
```
# app/permissions.py
from flask import abort


class BaseRoleCheck(object):
    """
    If it walks like a duck and quacks like a duck then, IT IS A DUCK!!
    """

    def __init__(self):
        self.next = None

    def set_next(self, next_role_check):
        self.next = next_role_check

    def has_perm(self, user):
        if not self.check(user):
            if self.next is not None:
                return self.next.has_perm(user)
            abort(403)  # raise an HTTP Forbidden error.
        return True

    def check(self, user):
        """
        Must be overridden by child classes.
        """
        pass
```
The base role check class defines a set of methods.

1. The constructor `__init__` method defines a `next` variable.
2. The `set_next` method *assigns it to another object of the same type*. In other words, the `next` variable points to the role check that needs to be executed next in the chain. This is an important concept.
3. `has_perm` method runs the conditions in the `check` method and does the following,
    * If the condition fails.
        * If the current handler is the last one in the chain, raise an error. In other words, if there are no more checks to be made and the last one failed as well.
        * Otherwise, delegates the next handler in line.
    * Returns true if the condition is satisfied and breaks the chain.
4. `check` method is the simplest of all. It has to validate the condition(s) and return a boolean. This method must be overridden by concrete child classes to implement their responsibilities.

### Define concrete subclasses

Now, you know the structure, let us define our concrete child classes to check specific roles.
```
# app/permissions.py
class AllowAny(BaseRoleCheck):
    def check(self, user):
        return True


class IsAdmin(BaseRoleCheck):
    def check(self, user):
        if 'ADMIN' not in user.roles:
            return False
        return True


class IsReviewer(BaseRoleCheck):
    def check(self, user):
        if 'REVIEWER' not in user.roles:
            return False
        return True
```

### Chain the required checks

Then, you need a place to chain the checks as required. I would rather write this as a python decorator so that it is easier to integrate with my API endpoints. Don't worry if you don't understand python decorator syntax, the logic is to chain the checks like building a LinkedList from an Array.

```
# app/decorators.py
from functools import wraps
from flask import g


def authorize(permission_classes):
    def wrapper(func):
        @wraps(func)
        def wrapped(*args, **kwargs):
            start = permission_classes[0]()
            # chain permission checks.
            current_node = start
            for each_class in permission_classes[1:]:
                new_node = each_class()
                current_node.set_next(new_node)
                current_node = new_node
            # execute the chain of permission checks.
            start.has_perm(g.user)
            # control will only reach here if and only if
            # all the checks in the chain pass.
            return func(*args, **kwargs)
        return wrapped
    return wrapper
```

### Putting it all together

All set, now let us just integrate with our APIs. Below I am writing pseudocode using syntax and semantics of Flask framework. You can refer it and use it accordingly with your framework of choice.

```
# app/api.py
from app import app
from app.permissions import AllowAny, IsAdmin, IsReviewer
from app.decorators import authorize


@app.route('/healthcheck')
@authorize([AllowAny])
def healthcheck_api():
    ..

@app.route('/posts/<int:post_id>/reviews')
@authorize([IsAdmin, IsReviewer])
def post_review_api(post_id):
    ..
```

The major advantage of this approach is the ease of extensibility and separation of concern. If you want to add/remove role checks you do not have to edit your API logic. You just have to change the values in the array you pass to `authorize` function. Moreover, each handler has the power to decide whether it should continue further down the chain and the code looks neat and maintainable.

## Diagrams for better understanding
**Class diagram**
![Chain of Responsibility - OOP Design Pattern - Class Diagram](https://dev-to-uploads.s3.amazonaws.com/i/c8ejld5b0kxiqp2zu3q4.png)

**Control flow**
![Chain of Responsibility - OOP Design Pattern - Control Flow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sbxgy1joi1zkgudlu33r.jpg)

## Conclusion
To conclude, you can make use of the Chain of Responsibility design pattern whenever you need to run a set of handlers that can decide on itself whether to proceed or terminate. Another good use case for CoR is probably request/form validations.

*PS: You cannot copy-paste the code shown here and expect it to work. The idea here is to give you awareness about the pattern through a contrived example.*