Skip to content
Sanfor2004
← All articles
Design PatternsDesign PatternsBehavioral PatternsPythonC++Software Engineering

Chain of Responsibility (Behavioral Pattern)

By Ahmed (Sanfor) · 11 Sept 2026 · 5 min read
A support request passes through a sequence of handlers.

Start with the design patterns overview · Part 13 of 23 · Behavioral patterns

Pass a request through an ordered chain until a handler processes it or the chain ends. This article follows the example in my 23 Design Patterns repository, connecting the problem, participating classes, Python and C++20 implementations, and the trade-offs that decide whether to use it.

The Problem

A request must pass authentication and spending checks, and different entry points need different policies.

Naive Solution

bool accept(Request r) {
    return r.authenticated && r.amount > 0 && r.amount <= 100;
}

Why It Becomes a Problem

One expression is fine initially; duplicating and editing it for several pipelines makes policy order and reuse difficult.

The Idea

Each Handler runs its own check and delegates only on success; the final successful handler accepts.

Reading the illustration

A support ticket moves from the front desk to a specialist and then a manager only when the current person cannot resolve it.

In the repository example, the same design idea addresses this software problem: A request must pass authentication and spending checks, and different entry points need different policies.

Structure

Diagram · Python source · C++20 source

Chain of Responsibility sketch map: Request leads through Auth to Limit.
Chain of Responsibility: trace the example from caller through the pattern boundary to its collaborator or result. The arrows show flow, not ownership. Open the full-size diagram.
Request  -->  Auth  -->  Limit

Participants

Handler owns its successor. Auth checks identity, Limit checks amount. The client chooses the chain order.

Canonical roles in this example:

  • Handler — A role that handles a request or passes it to its successor. Here: Handler.
  • Concrete Handler — A Handler implementing one particular processing rule. Here: Auth, Limit.
  • chain termination — The rule for stopping a chain and deciding what happens after the last handler. Here: Handler::handle.

Python Example

The complete Python source is shown first.

class Request:
    def __init__(self, authenticated, amount_cents):
        self.authenticated = authenticated
        self.amount_cents = amount_cents


class Handler:
    def __init__(self, next_handler=None):
        self.next_handler = next_handler

    def accepts(self, request):
        raise NotImplementedError

    def handle(self, request):
        if not self.accepts(request):
            return False
        if self.next_handler is None:
            return True
        return self.next_handler.handle(request)


class Auth(Handler):
    def accepts(self, request):
        return request.authenticated


class Limit(Handler):
    def accepts(self, request):
        return 0 < request.amount_cents <= 10000


if __name__ == "__main__":
    chain = Auth(Limit())
    for authenticated, amount_cents in [(False, 2000), (True, 20000),
                                       (True, 2000), (True, 0), (True, 10000)]:
        request = Request(authenticated, amount_cents)
        print("Accepted" if chain.handle(request) else "Rejected")

Python Output

Rejected
Rejected
Accepted
Rejected
Accepted

Code Walkthrough

Handler owns its successor. Auth checks identity, Limit checks amount. The client chooses the chain order.

Start at the final call in the Python example. Follow the middle role in the diagram and compare how the C++20 version handles the same responsibility.

C++20 Example

// Monetary amounts in this example are integer cents.
#include <initializer_list>
#include <iostream>
#include <memory>
#include <utility>

struct Request { bool authenticated; int amount_cents; };
class Handler {
    std::unique_ptr<Handler> next_;
protected:
    virtual bool accepts(const Request& request) const = 0;
public:
    explicit Handler(std::unique_ptr<Handler> next = {}) : next_(std::move(next)) {}
    virtual ~Handler() = default;
    bool handle(const Request& request) const {
        if (!accepts(request)) return false;
        return next_ ? next_->handle(request) : true;
    }
};
class Auth final : public Handler {
    bool accepts(const Request& request) const override { return request.authenticated; }
public:
    using Handler::Handler;
};
class Limit final : public Handler {
    bool accepts(const Request& request) const override { return request.amount_cents > 0 && request.amount_cents <= 100; }
public:
    using Handler::Handler;
};
int main() {
    const Auth chain{std::make_unique<Limit>()};
    for (const auto& request : {Request{false, 20}, Request{true, 200}, Request{true, 20}})
        std::cout << (chain.handle(request) ? "Accepted" : "Rejected") << '\n';
}

C++20 Output

Rejected
Rejected
Accepted

When to Use

Use it when request handling order or membership must be composed independently.

Use cases

Validation pipelines and request middleware fit. This variant requires every handler to approve, rather than stopping at the first successful handler.

When NOT to Use

Avoid it for two fixed checks in one place; the initial expression is then clearer.

Advantages

Checks can be reused and reordered without a giant conditional.

Trade-offs

Order affects behavior. A chain needs an explicit end policy; this example accepts after all checks, while other chains may reject unhandled requests.

Decorator · Command

Common Confusion

Decorator layers behavior around a component; this chain may terminate without reaching later handlers. Command represents the request as an object.

Terms to Remember

  • Chain of Responsibility — Pass a request along handlers that can stop or continue processing.
  • Handler — A role that handles a request or passes it to its successor. Example: Handler.
  • Concrete Handler — A Handler implementing one particular processing rule. Example: Auth, Limit.
  • chain termination — The rule for stopping a chain and deciding what happens after the last handler. Example: Handler::handle.

Interview Vocabulary

  • delegation — An object asks a collaborator to perform part of its work.
  • object composition — Connecting objects to form a larger behavior or structure.
  • loose coupling — Parts know only the small contracts needed to cooperate, limiting change propagation.

Interview Question

What happens to an unauthenticated request if Limit is expensive and placed first?

Mini Challenge

Add a maintenance-mode handler and verify that rejected requests never reach later checks.

Compare the two versions

Both versions use a validation chain: each Handler may reject, or pass onward; reaching the end means success. Other chains stop at the first Handler that can fulfill a request. Python holds successor references; C++ owns them with unique_ptr. The two examples express the same pattern responsibility; compare their setup and output before changing an input.

Check yourself

  1. What does reaching the end of this chain mean, and when does a check stop it?
  2. When would the naive solution on this page be easier to maintain? Give a concrete example.
  3. Change one input in the Python example. Predict the output and explain which responsibility handles the change.

Run and explore the example

The Python code comes from python/main.py, with expected output. The C++20 code comes from behavioral/chain-of-responsibility/cpp/main.cpp. Follow the repository’s C++20 build instructions to compile it and compare the result with expected.txt. The output demonstrates this example’s behavior; it does not cover every input or the challenge above.

The source example and adapted explanation are © 2026 Sanfor2004, provided under the MIT license. The sketchbook cover is an illustration preserved from this site’s original pattern lessons.

Continue the series

Ahmed (Sanfor)Backend and automation engineer in Alexandria. I build the systems businesses run on, and paint between deploys.

Keep reading

Design PatternsAbstract Factory (Creational Pattern)5 minDesign PatternsAdapter (Structural Pattern)4 minDesign PatternsBridge (Structural Pattern)5 min

Have manual work eating your week?

Book a free call