Skip to content

Chain of Responsibility Pattern

Quality Score

Overall Score: 9.4/10 ⭐ Outstanding

  • Technical Accuracy: 33/35
  • Code Quality: 25/25
  • Educational Value: 23/25
  • Documentation: 14/15

Last reviewed: July 7, 2026

Chain of responsibility is a behavioral design pattern that allows passing request along a series of handlers. When receiving a request, each handler decides to handle it with its own logic and/or pass it to the next handler in the chain. Once the last handler is reached, the request is completed.

Each handler in the chain has a reference to the next handler, allowing for the request to keep moving along the chain to the end. At any point, the request can be handled and passed to the next handler, or it can be handled and the chain can be terminated. You can use the Chain of Responsibility pattern when multiple objects can handle a request, but the handler isn't known in advance or when you need to process a request through multiple stages or validations. The main benefits of this pattern are:

  • Decoupling: The sender of a request is decoupled from the receiver, allowing for more flexible and maintainable code.
  • Dynamic Handling: Handlers can be added or removed at runtime, allowing for dynamic handling of requests.
  • Responsibility Sharing: Multiple handlers can share the responsibility of handling a request, allowing for more complex processing.
  • User journey: The pattern can be used to create a user journey where each handler represents a step in the process, making a clear and organized flow of actions.

The chain of responsibility pattern is composed of three main components:

Handler Interface

Defines the common interface for all supported handlers. The responsibility is to know how to handle a request, not when to use the handler. The interface is usually implemented as an abstract class.

src.advanced.chain_of_responsibility_pattern.Handler

Bases: ABC

Abstract base class for handlers in the chain of responsibility.

This class defines the interface that all concrete handlers must implement. Each handler has a reference to the next handler in the chain.

Source code in src/advanced/chain_of_responsibility_pattern/chain_of_responsibility.py
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Handler(ABC):
    """Abstract base class for handlers in the chain of responsibility.

    This class defines the interface that all concrete handlers must implement.
    Each handler has a reference to the next handler in the chain.
    """

    def __init__(self, next: Optional["Handler"] = None) -> None:
        """Initialize the handler with an optional next handler.

        Args:
            next: The next handler in the chain. If None, this is the last
                handler in the chain. Defaults to None.
        """
        self.next = next

    @abstractmethod
    def handle(self, line: str) -> None:
        """Handle the line or pass it to the successor.

        Args:
            line: The line to be handled.

        Raises:
            NotImplementedError: Must be implemented by concrete handlers.
        """
        pass

Concrete Handler

Implements the behavior associated with a handler of the chain. The responsibility is to know how to handle a request and where to pass it next, not when to use the handler.

src.advanced.chain_of_responsibility_pattern.LengthValidationHandler

Bases: Handler

Concrete handler for line length validation.

Validates that the line does not exceed a maximum length. If the line is valid, passes it to the next handler in the chain.

Source code in src/advanced/chain_of_responsibility_pattern/chain_of_responsibility.py
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
class LengthValidationHandler(Handler):
    """Concrete handler for line length validation.

    Validates that the line does not exceed a maximum length.
    If the line is valid, passes it to the next handler in the chain.
    """

    MAX_LENGTH = 80

    def handle(self, line: str) -> None:
        """Validate line length and pass to the next handler.

        Args:
            line: The line to be validated.

        Raises:
            ValueError: If the line length exceeds MAX_LENGTH characters.
        """
        if len(line) > self.MAX_LENGTH:
            raise ValueError(
                f"Line length exceeds {self.MAX_LENGTH} characters.",
            )
        if self.next:
            self.next.handle(line)

src.advanced.chain_of_responsibility_pattern.PrefixValidationHandler

Bases: Handler

Concrete handler for line prefix validation.

Validates that the line starts with a specific prefix. If the line is valid, passes it to the next handler in the chain.

Source code in src/advanced/chain_of_responsibility_pattern/chain_of_responsibility.py
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
class PrefixValidationHandler(Handler):
    """Concrete handler for line prefix validation.

    Validates that the line starts with a specific prefix.
    If the line is valid, passes it to the next handler in the chain.
    """

    PREFIX = "INFO"

    def handle(self, line: str) -> None:
        """Validate line prefix and pass to the next handler.

        Args:
            line: The line to be validated.

        Raises:
            ValueError: If the line does not start with PREFIX.
        """
        if not line.startswith(self.PREFIX):
            raise ValueError(f"Line does not start with '{self.PREFIX}'.")
        if self.next:
            self.next.handle(line)

src.advanced.chain_of_responsibility_pattern.SuffixValidationHandler

Bases: Handler

Concrete handler for line suffix validation.

Validates that the line ends with a specific suffix. If the line is valid, passes it to the next handler in the chain.

Source code in src/advanced/chain_of_responsibility_pattern/chain_of_responsibility.py
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
class SuffixValidationHandler(Handler):
    """Concrete handler for line suffix validation.

    Validates that the line ends with a specific suffix.
    If the line is valid, passes it to the next handler in the chain.
    """

    SUFFIX = "END"

    def handle(self, line: str) -> None:
        """Validate line suffix and pass to the next handler.

        Args:
            line: The line to be validated.

        Raises:
            ValueError: If the line does not end with SUFFIX.
        """
        if not line.endswith(self.SUFFIX):
            raise ValueError(f"Line does not end with '{self.SUFFIX}'.")
        if self.next:
            self.next.handle(line)

src.advanced.chain_of_responsibility_pattern.ContentValidationHandler

Bases: Handler

Concrete handler for line content validation.

Validates that the line contains exactly the required number of parts separated by a specific separator. If the line is valid, passes it to the next handler in the chain.

Source code in src/advanced/chain_of_responsibility_pattern/chain_of_responsibility.py
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
class ContentValidationHandler(Handler):
    """Concrete handler for line content validation.

    Validates that the line contains exactly the required number of
    parts separated by a specific separator.
    If the line is valid, passes it to the next handler in the chain.
    """

    SEPARATOR = "|"
    TOTAL_PARTS = 5

    def handle(self, line: str) -> None:
        """Validate line content structure and pass to the next handler.

        Args:
            line: The line to be validated.

        Raises:
            ValueError: If the line does not contain exactly TOTAL_PARTS
                parts separated by SEPARATOR.
        """
        parts = line.split(self.SEPARATOR)
        if len(parts) != self.TOTAL_PARTS:
            raise ValueError(
                f"Line does not contain {self.TOTAL_PARTS} parts "
                f"separated by '{self.SEPARATOR}'.",
            )
        if self.next:
            self.next.handle(line)

Chain of Responsibility

Initializes an instance of the class that defines the current handler and the order of execution. The responsibility is to know when to use the handler, not how to handle it.

src.advanced.chain_of_responsibility_pattern.ChainOfResponsibility

Class to manage the chain of responsibility.

This class initializes and manages a chain of validation handlers. The chain processes lines in the following order: 1. Length validation 2. Prefix validation 3. Suffix validation 4. Content structure validation

Source code in src/advanced/chain_of_responsibility_pattern/chain_of_responsibility.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
class ChainOfResponsibility:
    """Class to manage the chain of responsibility.

    This class initializes and manages a chain of validation handlers.
    The chain processes lines in the following order:
    1. Length validation
    2. Prefix validation
    3. Suffix validation
    4. Content structure validation
    """

    def __init__(self) -> None:
        """Initialize the chain with all validation handlers in order."""
        self.chain = LengthValidationHandler(
            PrefixValidationHandler(
                SuffixValidationHandler(
                    ContentValidationHandler(),
                ),
            ),
        )

    def process(self, line: str) -> None:
        """Process the line through the entire validation chain.

        Args:
            line: The line to be processed and validated.

        Raises:
            ValueError: If any validation in the chain fails.
        """
        self.chain.handle(line)

Some real world applications of the chain of responsibility pattern are: User journeys, logging systems, request processing, authentication/authorization, data validation...

Common Pitfalls

  • Broken Chain: Forgetting to call the next handler breaks the chain. Always call the next handler unless explicitly terminating the chain.
class GoodHandler(Handler):
    def handle(self, line: str) -> None:
        if some_condition:
            process(line)
        if self.next:  # Check next exists
            self.next.handle(line)
  • Circular References: Creating a circular chain causes infinite recursion. Design your chain as a directed acyclic graph (DAG). Test for cycles if handlers can be dynamically configured.

  • Exception Handling: Exceptions in one handler can prevent other handlers from executing. Plan your exception strategy properly. Should exceptions stop the chain or should handlers catch and pass them along?

  • Order Dependencies: The order of handlers can affect the outcome. Document the expected order by defining and documenting clearly the flow.

  • Tight Coupling: Use dependency injection to pass handlers, keeping them loosely coupled. Handlers that directly reference specific next handler types become tightly coupled.