Card 03/ 08
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.
def handle(self, request: str) -> str | None:
if self._next is not None:
return self._next.handle(request)
return NoneConcrete handler
Handles its own request, and hands the rest to the base class.
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.
chain = ConcreteHandler("A", "a")
chain.set_next(ConcreteHandler("B", "b")).set_next(ConcreteHandler("C", "c"))The complete template
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))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: Nonepublic 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);
}
}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: nullabstract 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));
}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: nullRequest 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.
← → move through sections and cards