Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】Untangle Your Code: Chain of Responsibility Design Pattern

2025年09月28日に「Dev.to」が公開したITニュース「Untangle Your Code: Chain of Responsibility Design Pattern」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

「責任の連鎖」デザインパターンは、肥大化した複雑な機能を小さな処理単位に分割し、これらを鎖のように繋げて順番に実行する手法だ。各処理は自分ができることを行い、できない場合は次の処理へ渡す。これにより、コードの独立性が高まり、読みやすく、機能追加や変更が容易になるため、保守性と拡張性が向上する。

ITニュース解説

プログラミングをしていると、一つの機能がたくさんの仕事を抱え込み、コードが読みにくく、変更しにくい状態になることはよくある。これは、最初はシンプルだったはずのロジックが、時間とともに複雑に絡み合い、まるで糸が絡まったかのような「スパゲッティコード」になってしまう現象だ。このような状況は、新しい機能を追加したり、既存の機能を修正したりする際に、予期せぬ問題を引き起こしやすくなる。

この問題を解決する設計パターンの一つに「Chain of Responsibility(責任の連鎖)」パターンがある。このパターンは、リクエスト(何かやってほしいという要求)を、複数の「ハンドラ」と呼ばれる処理担当者の鎖(チェーン)に沿って順番に渡していく仕組みだ。各ハンドラは、そのリクエストを自分が処理できるかどうかを判断し、処理できればそれを実行するか、あるいは次のハンドラにリクエストを渡すかを決める。この仕組みによって、リクエストを送る側(送信者)と、実際にリクエストを処理する側(受信者)が直接結びつくことなく、疎結合な関係を築けるのが大きな特徴だ。

具体的な例として、ユーザー登録フォームのバリデーション(入力値の検証)を考えてみよう。メールアドレス、名前、パスワードなど、複数の項目をチェックする必要がある。このバリデーションロジックを、一つの大きな関数の中にすべて書き込んでしまうとどうなるだろうか。最初に提示されたコードのように、メールアドレスのチェック、その中に名前のチェック、さらにその中にパスワードのチェックといった具合に、条件分岐が深くネスト(入れ子)したコードになりがちだ。このようなコードは、一見すると動作するが、新しいバリデーションルール(例えば、パスワードの強度チェック)を追加したい場合に、既存の複雑な構造の中に手を入れる必要があり、ミスを引き起こしやすい。また、どこで何がチェックされているのかが分かりにくく、コードを読むのも一苦労だ。

Chain of Responsibilityパターンを適用すると、このバリデーションロジックを、それぞれのチェック項目ごとに独立した小さな「ハンドラ」に分割できる。例えば、「メールアドレスを検証するハンドラ」、「名前を検証するハンドラ」、「パスワードを検証するハンドラ」といった具合だ。これらのハンドラは、互いに連結されて一つのチェーンを形成する。リクエスト(ユーザーが入力したデータ)がこのチェーンの最初のハンドラに渡されると、最初のハンドラが自分の責任範囲であるメールアドレスのチェックを行う。もし問題がなければ、リクエストを次のハンドラ(名前の検証ハンドラ)に渡す。これを繰り返し、チェーンの最後まで到達すれば全てのチェックが成功したことになる。もし途中のハンドラで問題が見つかれば、その時点でチェーンを中断し、エラーを返すことができる。

示されたTypeScriptのコードでは、まずHandlerというインターフェースを定義し、すべてのハンドラが持つべき共通の振る舞い(次のハンドラを設定するsetNextと、リクエストを処理するhandle)を定めている。次に、BaseHandlerという抽象クラスが、次のハンドラへリクエストを渡すための基本的なロジックを提供している。そして、IsEmailHandlerIsNameValidHandlerIsPasswordValidHandlerといった具体的なクラスが、それぞれのバリデーションロジックを実装している。これらのハンドラはそれぞれが単一の責任(例: メールアドレスの検証のみ)を持ち、互いに独立している。新しいバリデーションルールを追加したくなった場合でも、既存のハンドラを変更することなく、新しいハンドラを作成してチェーンに組み込むだけで済む。これは「オープン・クローズドの原則」(拡張には開いていても、修正には閉じているべき)という、ソフトウェア設計の重要な原則にも合致している。結果として、コードは非常に読みやすく、保守しやすく、拡張しやすいものとなる。

Chain of Responsibilityパターンは、API(アプリケーションプログラミングインターフェース)のミドルウェアにも広く利用されている。ウェブフレームワークにおけるミドルウェアは、まさにこのパターンの実例だ。ウェブサーバーがHTTPリクエストを受け取ると、そのリクエストは一連のミドルウェア関数を順番に通過していく。例えば、「認証ミドルウェア」は、リクエストに含まれるユーザーのトークンが有効かどうかをチェックする。「役割ミドルウェア」は、認証されたユーザーが特定のアクションを実行する権限を持っているかを確認する。そして、最後に「ログインハンドラ」が、実際のリクエストに応じたビジネスロジックを実行する。

各ミドルウェアは、自分の担当するチェックを行い、問題がなければリクエストを次のミドルウェアに渡す。もし認証失敗や権限不足といった問題が見つかれば、そのミドルウェアがリクエストの処理を中断し、エラーレスポンス(例えば「401 Unauthorized」)をクライアントに返す。この仕組みにより、認証や認可といった横断的な処理を、個々のビジネスロジックから分離し、きれいに保つことができる。TypeScriptのコード例では、ApiRequestインターフェースでAPIリクエストの構造を定義し、MiddlewareインターフェースとBaseMiddleware抽象クラスでミドルウェアの共通の振る舞いを定めている。AuthMiddlewareはトークンを検証し、リクエストにユーザー情報を追加する。RoleMiddlewareはユーザーの役割をチェックする。LoginHandlerは最終的なビジネスロジックを実行する。クライアントコードでは、これらのミドルウェアインスタンスを生成し、setNextメソッドを使ってチェーンを構築する。そして、最初のミドルウェアのhandleメソッドを呼び出すことで、リクエストがチェーンを流れ始める。テストケースでは、有効なリクエスト、認証失敗のリクエスト、権限不足のリクエストがどのように処理され、結果が返されるかが示されている。このミドルウェアの例からもわかるように、それぞれのハンドラが独立した責任を持つことで、コードの疎結合性が向上する。また、各ミドルウェアが独立しているため、それぞれを個別にテストしやすくなる点も大きなメリットだ。

では、このChain of Responsibilityパターンはどのような場面で役立つだろうか。 第一に、リクエストを送信するオブジェクトと、そのリクエストを処理するオブジェクトを明確に分離したい場合に有効だ。送信側は、誰がそのリクエストを処理するかを知る必要がなく、ただチェーンの先頭にリクエストを渡すだけでよい。 第二に、特定の順序で複数の処理を実行する必要がある場合に適している。チェーンの順序を定義することで、処理の流れを明確にコントロールできる。 第三に、実行時に処理担当者(ハンドラ)を動的に追加したり削除したりしたい場合に役立つ。既存のコードを変更することなく、チェーンの構成を柔軟に変更できるため、システムの拡張性や保守性が向上する。

まとめると、Chain of Responsibilityパターンは、一つの大きな処理を小さな責任単位に分割し、それらを鎖のように連結することで、コードの複雑さを軽減する非常に実用的な設計パターンだ。このパターンを適用することで、開発者は読みやすく、テストしやすく、そして将来の変更にも柔軟に対応できる、高品質なソフトウェアを構築するための強力なツールを手に入れることができる。

関連コンテンツ

関連IT用語