メインコンテンツまでスキップ

🧩 Chain of Resp. パターン

✅ 設計意図

  • 複数の処理クラスを連鎖させ、条件に応じて誰が処理するかを委譲
  • 各処理クラスは「処理する or 次に渡す」

✅ 適用理由

  • 分岐処理を直列の処理チェーンに変換
  • 処理の流れを順に確認したいときに最適

✅ 向いているシーン

  • 各処理が独立していて、どこかで処理が止まる
  • 複数条件の中で「該当する 1 つ」に任せたい

✅ コード例

// ハンドラインターフェース
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

✅ 解説

このコードは 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 パターンの利点

  • 柔軟な処理フロー: ハンドラの順序を動的に変更可能
  • 単一責任の原則: 各ハンドラが特定の処理にのみ責任を持つ
  • 拡張性: 新しいハンドラを追加する場合も、既存のコードに影響を与えずに対応可能

この設計は、リクエストを柔軟に処理し、条件分岐を排除する。特に、処理の流れを動的に変更したい場合や、 複数の条件に基づく処理が必要な場面で有効に機能する。