【ITニュース解説】My tryst with Rust - through the subject of Design Pattern - implementation of Chain of Responsibility...
2025年09月07日に「Dev.to」が公開したITニュース「My tryst with Rust - through the subject of Design Pattern - implementation of Chain of Responsibility...」について初心者にもわかりやすく解説しています。
ITニュース概要
RustでChain of Responsibilityパターンを実装する記事。このパターンは、要求を複数のハンドラーで順に処理する。各ハンドラーは要求を処理するか、次のハンドラーに渡す。これにより、要求の送信側と受信側の間の結合度が低くなり、柔軟な処理が可能になる。記事では、役職に応じた処理の連鎖をサンプルコードで示している。
ITニュース解説
この記事では、Rustというプログラミング言語を使って、Chain of Responsibility(責任の連鎖)と呼ばれるデザインパターンを実装する方法を解説している。デザインパターンとは、ソフトウェア開発でよく使われる問題に対する一般的な解決策のこと。Chain of Responsibilityは、ある要求を複数の処理オブジェクト(ハンドラ)に順番に送り、最終的にいずれかのハンドラがその要求を処理する、という仕組みを作るためのパターンだ。
まず、Chain of Responsibilityの基本的な考え方を説明する。このパターンでは、要求を出す側は、どのハンドラがその要求を処理するかを知らなくてもいい。要求は、あらかじめ決められた順序でハンドラに送られ、各ハンドラは「自分はこの要求を処理できるか?」を判断する。もし処理できなければ、次のハンドラに要求を渡す。この連鎖が続くことで、最終的に適切なハンドラが要求を処理する。
この記事では、このパターンを職場の承認フローを例に実装している。登場人物は、チームリーダー、プロジェクトマネージャー、部長(General Manager)、CTO(最高技術責任者)。それぞれの役職者が、特定のレベルの要求を処理できるという設定だ。例えば、チームリーダーは「チームリーダーレベル」の要求を処理し、それ以外の要求はプロジェクトマネージャーに渡す。プロジェクトマネージャーは「プロジェクトマネージャーレベル」の要求を処理し、それ以外の要求は部長に渡す、という具合だ。CTOは最終的な責任者として、すべての要求を処理する。
Rustのコードを見てみよう。まず、Requestという構造体が定義されている。これは、要求の種類を表すもので、request_typeという文字列型のフィールドを持っている。次に、Employeeというトレイト(他の言語におけるインターフェースのようなもの)が定義されている。このトレイトは、set_successorとhandle_requestという二つのメソッドを持つ。set_successorは、自分の次のハンドラ(後続の担当者)を設定するためのメソッド。handle_requestは、実際に要求を処理するためのメソッドだ。
次に、ProjectManager、TeamLeader、GeneralManager、CTOという4つの構造体が定義されている。これらの構造体は、それぞれプロジェクトマネージャー、チームリーダー、部長、CTOを表しており、Employeeトレイトを実装している。それぞれの構造体は、successorというOption<Box<dyn Employee>>型のフィールドを持っている。successorは、次のハンドラへの参照を保持するためのもの。Option型を使っているのは、最後のハンドラ(この例ではCTO)には後続の担当者がいない場合があるからだ。Box<dyn Employee>は、トレイトオブジェクトと呼ばれるもので、異なる型の構造体(例えば、GeneralManagerやCTO)を同じ型として扱えるようにするためのもの。
それぞれの構造体のhandle_requestメソッドの中身を見てみよう。例えば、ProjectManagerのhandle_requestメソッドは、まず要求の種類が「projectmanager_level」かどうかをチェックする。もしそうなら、「プロジェクトマネージャーレベルの要求を処理します」というメッセージを表示する。そうでなければ、successorがSome(つまり、次のハンドラが存在する)かどうかをチェックし、もしそうなら、次のハンドラのhandle_requestメソッドを呼び出して、要求を渡す。
main関数では、まずいくつかのRequestオブジェクトを作成する。そして、CTO、GeneralManager、ProjectManager、TeamLeaderのインスタンスを作成する。次に、set_successorメソッドを使って、ハンドラの連鎖を構築する。例えば、gm.set_successor(Box::new(cto))は、部長の後続の担当者をCTOに設定する。最後に、teamLeader.handle_request(request3)を呼び出して、連鎖の最初のハンドラであるチームリーダーに要求を渡す。
この記事のコード例では、チームリーダーが受け取った要求の種類が何であれ、連鎖を辿って最終的にCTOが要求を処理するようになっている。なぜなら、チームリーダー、プロジェクトマネージャー、部長のいずれもが、該当レベルの要求を処理する条件に合致せず、後続の担当者に要求を渡す処理を繰り返すためだ。
Chain of Responsibilityパターンを使うことで、要求の処理ロジックを各ハンドラに分散させ、それぞれのハンドラが自分の担当範囲だけを気にすればよくなる。また、ハンドラの順序や種類を動的に変更することも可能になるため、柔軟性の高いシステムを構築できる。