🧩 Chain of Resp. パターン
✅ 設計意図
- 複数の処理クラスを連鎖させ、条件に応じて誰が処理するかを委譲
- 各処理クラスは「処理する or 次に渡す」
✅ 適用理由
- 分岐処理を直列の処理チェーンに変換
- 処理の流れを順に確認したいときに最適
✅ 向いているシーン
- 各処理が独立していて、どこかで処理が止まる
- 複数条件の中で「該当する 1 つ」に任せたい
✅ コード例
- 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
✅ 解説
このコードは Chain of Responsibility (CoR)
パターン を使用して、割引計算のロジックを一連のハンドラに分割し、
リクエストを順に処理する設計を実現している。CoR
パターンは、複数のオブジェクトが連鎖的にリクエストを処理し、
適切なオブジェクトが処理を担当するデザインパターン。
1. Chain of Responsibility パターンの概要
- Handler: リクエストを処理するためのインターフェースを定義
- このコードでは
DiscountHandler
が該当
- このコードでは
- ConcreteHandler:
Handler
を実装し、特定のリクエストを処理するクラス- このコードでは
StudentHandler
,MemberHandler
,VIPHandler
が該当
- このコードでは
- Client: ハンドラチェーンを構築し、リクエストを最初のハンドラに渡す
- このコードでは
student.setNext(member).setNext(vip)
の部分が該当
- このコードでは
2. 主なクラスとその役割
DiscountHandler
- ハンドラの共通インターフェース
setNext(handler: DiscountHandler): DiscountHandler
メソッドで次のハンドラを設定handle(userType: string, basePrice: number): number
メソッドでリクエストを処理
BaseDiscountHandler
DiscountHandler
を実装した抽象クラス- 共通の
setNext
メソッドと、次のハンドラに処理を委譲するhandle
メソッドを提供
StudentHandler
,MemberHandler
,VIPHandler
BaseDiscountHandler
を継承した具体的なハンドラクラス- 各クラスで特定のユーザータイプ(
student
,member
,vip
)に応じた割引ロジックを実装
- クライアントコード
- ハンドラチェーンを構築し、最初のハンドラにリクエストを渡す
3. UML クラス図
4. Chain of Responsibility パターンの利点
- 柔軟な処理フロー: ハンドラの順序を動的に変更可能
- 単一責任の原則: 各ハンドラが特定の処理にのみ責任を持つ
- 拡張性: 新しいハンドラを追加する場合も、既存のコードに影響を与えずに対応可能
この設計は、リクエストを柔軟に処理し、条件分岐を排除する。特に、処理の流れを動的に変更したい場合や、 複数の条件に基づく処理が必要な場面で有効に機能する。