Card 03/ 08

CodeDifficulty: Intermediate4 min

Chain of Responsibility Template in Python, Java and TypeScript

Three handlers linked in order, and requests that stop at different points.

The Chain of Responsibility template is the pattern in its pure form, with the participant names from the class diagram. Three handlers, A, B and C, each accept one request and pass every other one on. Three requests go to the first link: one A handles, one C handles, and one nobody handles. The steps build the Python version, and the tabs give the complete program in three languages, each with its output.

Building the template one part at a time

Handler

Holds the next link, and by default passes to it.

py
def handle(self, request: str) -> str | None:
    if self._next is not None:
        return self._next.handle(request)
    return None

Concrete handler

Handles its own request, and hands the rest to the base class.

py
def handle(self, request: str) -> str | None:
    if request == self._accepts:
        return f"{self._name} handled {request}"
    return super().handle(request)

Building the chain

set_next returns the handler it was given, so links can be added in one line.

py
chain = ConcreteHandler("A", "a")
chain.set_next(ConcreteHandler("B", "b")).set_next(ConcreteHandler("C", "c"))

The complete template

py
class Handler:
    def __init__(self) -> None:
        self._next: Handler | None = None

    def set_next(self, handler: "Handler") -> "Handler":
        self._next = handler
        return handler

    def handle(self, request: str) -> str | None:
        if self._next is not None:
            return self._next.handle(request)
        return None


class ConcreteHandler(Handler):
    def __init__(self, name: str, accepts: str) -> None:
        super().__init__()
        self._name = name
        self._accepts = accepts

    def handle(self, request: str) -> str | None:
        if request == self._accepts:
            return f"{self._name} handled {request}"
        print(f"  {self._name} passes {request} on")
        return super().handle(request)


chain = ConcreteHandler("A", "a")
chain.set_next(ConcreteHandler("B", "b")).set_next(ConcreteHandler("C", "c"))

for request in ("a", "c", "z"):
    print(f"request {request}:")
    print("  result:", chain.handle(request))
OutputRan with python:3.12-slim (Python 3.12.13)
request a:
  result: A handled a
request c:
  A passes c on
  B passes c on
  result: C handled c
request z:
  A passes z on
  B passes z on
  C passes z on
  result: None
The last link has no next handler, so the base class returns `None`.
java
public class CrTemplate {
    public static void main(String[] args) {
        Handler chain = new ConcreteHandler("A", "a");
        chain.setNext(new ConcreteHandler("B", "b")).setNext(new ConcreteHandler("C", "c"));

        for (String request : new String[] {"a", "c", "z"}) {
            System.out.println("request " + request + ":");
            System.out.println("  result: " + chain.handle(request));
        }
    }
}

abstract class Handler {
    private Handler next;

    Handler setNext(Handler handler) {
        this.next = handler;
        return handler;
    }

    String handle(String request) {
        return next == null ? null : next.handle(request);
    }
}

class ConcreteHandler extends Handler {
    private final String name;
    private final String accepts;

    ConcreteHandler(String name, String accepts) {
        this.name = name;
        this.accepts = accepts;
    }

    @Override
    String handle(String request) {
        if (request.equals(accepts)) {
            return name + " handled " + request;
        }
        System.out.println("  " + name + " passes " + request + " on");
        return super.handle(request);
    }
}
OutputRan with eclipse-temurin:21-jdk (Java 21.0.12)
request a:
  result: A handled a
request c:
  A passes c on
  B passes c on
  result: C handled c
request z:
  A passes z on
  B passes z on
  C passes z on
  result: null
`next == null` ends the chain, and `handle` returns `null`.
ts
abstract class Handler {
  private next: Handler | null = null;

  setNext(handler: Handler): Handler {
    this.next = handler;
    return handler;
  }

  handle(request: string): string | null {
    return this.next ? this.next.handle(request) : null;
  }
}

class ConcreteHandler extends Handler {
  constructor(private readonly name: string, private readonly accepts: string) {
    super();
  }

  override handle(request: string): string | null {
    if (request === this.accepts) {
      return `${this.name} handled ${request}`;
    }
    console.log(`  ${this.name} passes ${request} on`);
    return super.handle(request);
  }
}

const chain = new ConcreteHandler("A", "a");
chain.setNext(new ConcreteHandler("B", "b")).setNext(new ConcreteHandler("C", "c"));

for (const request of ["a", "c", "z"]) {
  console.log(`request ${request}:`);
  console.log("  result:", chain.handle(request));
}
OutputRan with node:22-slim (Node 22.23.1), TypeScript 5.9.3
request a:
  result: A handled a
request c:
  A passes c on
  B passes c on
  result: C handled c
request z:
  A passes z on
  B passes z on
  C passes z on
  result: null
`setNext` returns its argument, which is what makes the chained call work.

Request a stops at the first link. Request c is passed by A and B and handled by C, with each pass printed. Request z is passed by all three, and the result is None in Python and null in Java and TypeScript, because the last link had nowhere to send it.

The client sent all three requests to chain, the first link, and never named B or C. Inserting a new handler between A and B, or reordering the links, changes the line that builds the chain and nothing else.