ちゃんなるぶろぐ

エンジニア5年生🧑‍💻 オライリーとにらめっこする毎日。

【5分でわかる⛓️】TypeScriptでDesign Pattern〜Chain-of-Responsibility Pattern〜

どうも、ちゃんなるです!

今回は、Chain-of-Responsibility Patternを紹介します🖐️

概要

Chain-of-Responsibilityパターンは、リクエストを処理するオブジェクトのチェーンを作成するデザインパターンです。

各オブジェクトはリクエストを処理できるか確認し、できなければ次のオブジェクトに渡します。

特徴をざっくりまとめると下記のようになります。

  • たらい回しのような処理の流れを辿る(適切な人にたどり着くまで、次の人、次の人、に自分の要求が送られていく)
  • 複数のオブジェクトを鎖のように繋いでおき、そのオブジェクトの鎖を順次渡り歩いて目的のオブジェクトを決定する
  • 要求する側と処理する側の結びつきを弱めることができ、それぞれを部品として独立させることができる
  • 状況によって、要求を処理するオブジェクトが変化するようなプログラムにも対応できる

Chain-of-Responsibility Patternに登場するクラス

役割 説明
Handler 要求を処理するインタフェースを定める
次の人を保持しておき、自分で処理ができない要求がきたら、次の人(これも Handler)にたらい回しする
ConcreteHandler 要求を処理する具体的な役
Client 最初のConcreteHandlerに要求を出すもの

今回示すプログラムの設計

顧客サポートに問い合わせが来たとき、最初にチャットボットが対応し、解決できない場合はサポート担当者に渡す仕組み

これを実装してみましょう🤖☎️

サンプルコード

この例では、AbstractHandlerがHandlerで、ChatBotHandlerとSupportAgentHandlerがConcreteHandlerになります。

チェーンの構築はsetNext()を使って行います。

interface Handler {
  setNext(handler: Handler): Handler;
  handle(request: string): string;
}

abstract class AbstractHandler implements Handler {
  private nextHandler: Handler;

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

  public handle(request: string): string {
    if (this.nextHandler) {
      return this.nextHandler.handle(request);
    }

    return null;
  }
}

class ChatBotHandler extends AbstractHandler {
  public handle(request: string): string {
    if (request === 'simple') {
      return `ChatBot: I can help with simple questions.`;
    }
    return super.handle(request);
  }
}

class SupportAgentHandler extends AbstractHandler {
  public handle(request: string): string {
    if (request === 'complex') {
      return `Support Agent: I'll handle complex questions.`;
    }
    return super.handle(request);
  }
}

では、実行してみましょう🖐️

const chatBot = new ChatBotHandler();
const supportAgent = new SupportAgentHandler();

chatBot.setNext(supportAgent);

console.log(chatBot.handle('simple')); // ChatBot: I can help with simple questions.
console.log(chatBot.handle('complex')); // Support Agent: I'll handle complex questions.

ConcreteHandlerを追加することで新しい処理(たらい回し先)が簡単に追加できます。

その際、たらい回し先の設定を追加しましょう👍

supportAgent.setNext(<新しい処理>);

クラス図

サンプルコードのクラス図:Mermaid Live Editorで作成

Chain-of-Responsibility Patternの使い道

  • 一連の処理を柔軟に変更できるようにしたい場合
  • オブジェクトが他のオブジェクトを直接参照せずにリクエストを処理する方法が必要な場合

組み合わせられるデザインパターン

  • Composite Pattern: Handlerとして使うことができます。複合構造を再帰的に走査して処理を実行する場合に、Chain-of-Responsibilityパターンと組み合わせて使用できます。

chan-naru.hatenablog.com

  • Command Pattern: Handlerに対して投げられる要求に使われることがあります。

※を準備中…

まとめ

Chain-of-Responsibilityパターンは、リクエストを処理するオブジェクトのチェーンを作成し、そのチェーンを通してリクエストが適切なオブジェクトに到達するまで処理が渡されるようにするデザインパターンです。

これにより、処理の流れを柔軟に変更し、オブジェクト間の疎結合を実現できます。

このパターンを適用すると、システムがスケーラブルでメンテナンスしやすくなり、各オブジェクトの責任範囲が明確化させられます。これは、アプリケーションが変更や拡張に対して適応しやすくなり、長期的な開発と運用が容易になることを意味します👍

参考文献

www.oreilly.com

en.wikipedia.org