開発
Chain of Responsibilityパターン
makuta
chainは「鎖」、 responsibility「責任」を意味します。したがってChain of Responsibilityは「責任の鎖」と訳することができます。Chain of Responsibilityパターンは「責任」を負ったものが、「鎖」状につながれた状態をイメージさせるパターンです。
例えば、入力された文字列を検証する場合を考えてみます。
条件によって行う処理を分岐させることはよくあります。たいていの場合、if文やswitch文を使って「この条件の場合はこう処理する」というコードを記述することになります。
入力値の検証には、文字列の長さのチェックやあるフォーマットにあっているかどうかのチェックがあります。一般的な検証処理の流れとしては、入力された文字列が所定のパターンにマッチするかどうかを判定しマッチしない場合は適切なメッセージを表示する。というものになるかと思います。
この処理をif文を使って実装する場合、マッチングをおこなうパターンの数だけif文をつなげることになるでしょう。しかし、パターンの数が多い場合やマッチングの条件が複雑な場合、コードの見通しが非常に悪くなってしまいがちです。また、コードの再利用がしにくい状態になります。
if文やswitch文を使った条件分岐は、「どの場合にどう処理をすべきか」が全て一箇所にまとめられることになります。つまり、条件とそれに対応する処理のしくみを知っておく必要があります。このため、条件が複雑になったり分岐が多くなればなるほど、「知っておかなければならないこと」が増えてしまいます。
この場合、分岐の条件とそれに対応する処理の組ごとに分解できると、組ごとにその条件や処理内容に集中することができます。その結果、コードの見通しも良くなり、保守性や再利用性を高めることができそうです。
しかし、条件と処理の組に分解した場合、それらをどうやって組み立てて利用するかが問題になってしまいますね。分解してしまった分、扱いが大変になってしまうと分解した意味がありません。
こうした問題を解決するためにパターンとしてChain of Responsibilityパターンがあります。
Chain of Responsibilityパターンの特徴である処理をおこなう「オブジェクトのチェーン」についてですが、このオブジェクトは、処理をおこなうための共通のAPIを持ち、それぞれ異なる処理を実装しています。この処理はif文などを使った場合に記述する「この条件の場合におこなう処理」になります。
また、内部に別の処理をおこなうオブジェクトを保持していて、自分が処理できないと判断した場合、そのオブジェクトに処理を依頼します。
一方、クライアント側は、処理オブジェクトのチェーンに要求を送信するだけです。あとは、その要求が処理オブジェクトのチェーンを伝わっていき、適切に処理可能なオブジェクトが処理を行います。
注意:Chain of Responsibilityパターンは、誰が要求を処理すべきかが前もって定まっていてその相手がすぐに処理をする場合と比較すると、処理は遅くなってしまいます。さらに、連鎖構造が深すぎると、実際どのオブジェクトが処理しているのかわからなくなったり(保守性が低下)処理速度の低下を招いてしまうので注意が必要です。
このChain of Responsibilityパターンのメリットは
- 要求の送信側と受信側の結びつきを緩くできる
- 新しい処理クラスを簡単に追加できる
- 動的に処理チェーン(オブジェクトをつなげたもの)を変更できる
等が挙げられます。
ということで、あるイベントが発生した時、構造を順番にアクセスしていき、それぞれにイベントを通知して処理を試みて、処理できるインスタンスに行き着くまで次々と進めていく、Chain of Responsibilityパターンについてでした。