π§© Chain of Responsibility Pattern
β Intentβ
- Chain multiple handler classes together and delegate processing based on conditions
- Each handler either processes the request or passes it along
β Motivationβ
- Convert branching logic into a sequential processing chain
- Ideal for situations where the processing flow should be traced step by step
β When to Useβ
- When each handler is independent and only one of them should process the request
- When you want to delegate to the one matching condition among many
β Code Exampleβ
- TypeScript
- PHP
- Python
// γγ³γγ©γ€γ³γΏγΌγγ§γΌγΉ
interface DiscountHandler {
setNext(handler: DiscountHandler): DiscountHandler;
handle(userType: string, basePrice: number): number;
}
// ζ½θ±‘γ―γ©γΉοΌε
±ιε¦ηοΌ
abstract class BaseDiscountHandler implements DiscountHandler {
private nextHandler: DiscountHandler | null = null;
setNext(handler: DiscountHandler): DiscountHandler {
this.nextHandler = handler;
return handler;
}
handle(userType: string, basePrice: number): number {
if (this.nextHandler) {
return this.nextHandler.handle(userType, basePrice);
}
return basePrice;
}
}
// εγγ³γγ©
class StudentHandler extends BaseDiscountHandler {
handle(userType: string, basePrice: number): number {
if (userType === "student") return basePrice * 0.8;
return super.handle(userType, basePrice);
}
}
class MemberHandler extends BaseDiscountHandler {
handle(userType: string, basePrice: number): number {
if (userType === "member") return basePrice * 0.9;
return super.handle(userType, basePrice);
}
}
class VIPHandler extends BaseDiscountHandler {
handle(userType: string, basePrice: number): number {
if (userType === "vip") return basePrice * 0.7;
return super.handle(userType, basePrice);
}
}
// ε©η¨δΎ
const student = new StudentHandler();
const member = new MemberHandler();
const vip = new VIPHandler();
student.setNext(member).setNext(vip);
console.log(student.handle("vip", 1000)); // 700
console.log(student.handle("student", 1000)); // 800
console.log(student.handle("guest", 1000)); // 1000
<?php
interface DiscountHandler {
public function setNext(DiscountHandler $handler): DiscountHandler;
public function handle(string $userType, float $basePrice): float;
}
abstract class BaseDiscountHandler implements DiscountHandler {
private ?DiscountHandler $nextHandler = null;
public function setNext(DiscountHandler $handler): DiscountHandler {
$this->nextHandler = $handler;
return $handler;
}
public function handle(string $userType, float $basePrice): float {
if ($this->nextHandler) {
return $this->nextHandler->handle($userType, $basePrice);
}
return $basePrice;
}
}
class StudentHandler extends BaseDiscountHandler {
public function handle(string $userType, float $basePrice): float {
if ($userType === "student") return $basePrice * 0.8;
return parent::handle($userType, $basePrice);
}
}
class MemberHandler extends BaseDiscountHandler {
public function handle(string $userType, float $basePrice): float {
if ($userType === "member") return $basePrice * 0.9;
return parent::handle($userType, $basePrice);
}
}
class VIPHandler extends BaseDiscountHandler {
public function handle(string $userType, float $basePrice): float {
if ($userType === "vip") return $basePrice * 0.7;
return parent::handle($userType, $basePrice);
}
}
// ε©η¨δΎ
$student = new StudentHandler();
$member = new MemberHandler();
$vip = new VIPHandler();
$student->setNext($member)->setNext($vip);
echo $student->handle("vip", 1000) . PHP_EOL; // 700
echo $student->handle("student", 1000) . PHP_EOL; // 800
echo $student->handle("guest", 1000) . PHP_EOL; // 1000
from abc import ABC, abstractmethod
class DiscountHandler(ABC):
@abstractmethod
def set_next(self, handler: "DiscountHandler") -> "DiscountHandler":
pass
@abstractmethod
def handle(self, user_type: str, base_price: float) -> float:
pass
class BaseDiscountHandler(DiscountHandler):
def __init__(self):
self._next_handler: DiscountHandler | None = None
def set_next(self, handler: DiscountHandler) -> DiscountHandler:
self._next_handler = handler
return handler
def handle(self, user_type: str, base_price: float) -> float:
if self._next_handler:
return self._next_handler.handle(user_type, base_price)
return base_price
class StudentHandler(BaseDiscountHandler):
def handle(self, user_type: str, base_price: float) -> float:
if user_type == "student":
return base_price * 0.8
return super().handle(user_type, base_price)
class MemberHandler(BaseDiscountHandler):
def handle(self, user_type: str, base_price: float) -> float:
if user_type == "member":
return base_price * 0.9
return super().handle(user_type, base_price)
class VIPHandler(BaseDiscountHandler):
def handle(self, user_type: str, base_price: float) -> float:
if user_type == "vip":
return base_price * 0.7
return super().handle(user_type, base_price)
# ε©η¨δΎ
student = StudentHandler()
member = MemberHandler()
vip = VIPHandler()
student.set_next(member).set_next(vip)
print(student.handle("vip", 1000)) # 700
print(student.handle("student", 1000)) # 800
print(student.handle("guest", 1000)) # 1000
β Explanationβ
This code applies the Chain of Responsibility (CoR)
pattern to divide discount logic into a series of handlers that process the request sequentially.
The CoR
pattern allows multiple objects to process a request in turn, passing it along the chain until an appropriate handler takes responsibility.
1. Overview of the Chain of Responsibility Patternβ
-
Handler: Defines the interface for handling requests
- Represented by
DiscountHandler
in this code
- Represented by
-
ConcreteHandler: Implements the
Handler
interface and handles specific types of requests- Implemented by
StudentHandler
,MemberHandler
, andVIPHandler
- Implemented by
-
Client: Builds the chain of handlers and passes the request to the first one
- Represented by
student.setNext(member).setNext(vip)
in this code
- Represented by
2. Key Classes and Their Rolesβ
-
DiscountHandler
- Common interface for all handlers
setNext(handler: DiscountHandler): DiscountHandler
sets the next handlerhandle(userType: string, basePrice: number): number
processes the request
-
BaseDiscountHandler
- Abstract class implementing
DiscountHandler
- Provides a default implementation of
setNext
and delegates to the next handler inhandle
- Abstract class implementing
-
StudentHandler
,MemberHandler
,VIPHandler
- Concrete handler classes extending
BaseDiscountHandler
- Each implements specific discount logic based on user type (
student
,member
,vip
)
- Concrete handler classes extending
-
Client Code
- Constructs the handler chain and sends the request to the first handler
3. UML Class Diagramβ
4. Benefits of the Chain of Responsibility Patternβ
- Flexible Processing Flow: The order of handlers can be changed dynamically
- Single Responsibility Principle: Each handler is responsible for only one kind of processing
- Extensibility: New handlers can be added without modifying existing code
This design enables flexible request handling and eliminates complex conditional logic. It is especially effective when processing needs to be dynamically restructured or when multiple conditional branches must be managed cleanly.