はじめに
今回は責任をたらい回しするChain of Responsibility
パターンを紹介したいと思います。
登場人物
冒頭でも述べた通り,Chain of Responsibility
パターンは責任(要求に応じて処理を行う人)をたらい回しにするためのデザインパターンになります。
名前 | 意味 |
---|---|
Handler | 要求を処理するインターフェイス(API)を定める役。次の人を保持しておく。 |
ConcreteHandler | 要求を処理する具体的な役 |
Handlerのコード
Handler
は処理を要求と次の依頼先を保持することを実装していきます。
/// <summary> /// 要求を処理するインターフェイスを定める抽象クラス /// </summary> public abstract class Handler { private Handler _next; public Handler SetHandler(Handler handler) { _next = handler; return handler; } public virtual void Request(SomeTask task) { if (Resolve(task)) { Console.WriteLine($"タスクが{this}によって処理されました。"); } else if(_next != null) { _next.Request(task); } else { Console.WriteLine($"タスクを処理することができませんでした。"); } } protected abstract bool Resolve(SomeTask task); } /// <summary> /// サンプルとして用意した,依頼する仕事を表現するクラス /// </summary> public class SomeTask { public string Name { get; } // 仕事の名前 public SomeTask(string name) => Name = name; }
ConcreteHandler
次にHandler
を継承した、具体的にどう処理するか・それとも次の人に任せるかを記述するConcreteHanler
を実装します。
public class ConcreteHandler1 : Handler { protected override bool Resolve(SomeTask task) { if (task.Name != "TaskA") return false; Console.WriteLine($"{task.Name}を処理します。"); return true; } } public class ConcreteHandler2 : Handler { protected override bool Resolve(SomeTask task) { if (task.Name != "TaskB") return false; Console.WriteLine($"{task.Name}を処理します。"); return true; } }
動作確認
実際にこれらが動作するか動作確認を試してみましょう。
class Client { private static void Main(string[] args) { // 初期化(本来であれば別の箇所で設定するのが望ましい) Handler handler1 = new ConcreteHandler1(); Handler handler2 = new ConcreteHandler2(); handler1.SetHandler(handler2); // 発生したタスクを処理してもらう // 「TaskBを処理します。」 // 「タスクがConcreteHandler2によって処理されました。」 handler1.Request(new SomeTask("TaskB")); } }
さいごに
実用するときはHandler
に次が誰かを設定する責務は別クラスにて定義した方がよいと思います。
Unity
ならScriptableObject
で記述して,インスペクターやエディタ拡張で作成した自作ウィンドウから設定できるようにするなんかしても面白そうですね(実際にやったことがあるわけではない)。
またランタイムで処理の順番が容易に変えられるのも結構魅力的な気もします。
しかし動作速度は愚直にswitch
する等の実装よりも遅くなってしまうのでそこだけは注意してください。
ではまた。