概要
Chain of Responsibilityパターンでは、プログラマがオブジェクトの再帰的なチェーンを動的に作成することができる。それぞれのオブジェクトは、通常はメソッドコールで表現される「責任」を果たそうとします。チェーン内のオブジェクトが責任を果たすことができない場合、リクエストは責任を果たすことができるまでチェーンの次のレベルに伝搬する。このチェーンは通常、リンクのリストとして実装される(ただし他の反復可能な構造を用いて実装することもできる)。
以下の例では、複数の候補者から求職者を検索するサービスをシミュレーションする。プールは地理的なクラスタ(ローカル/リージョン/グローバル)によって分類され、このサービスのユーザは、すべての要件を満たす最も近い候補者(最小のクラスタ)を探したいと想定している。そのため、責任の連鎖はプールのリンクリストとなり、ユーザーは良い候補者を見つけるために再帰的に(小さいものから大きいものへ)チェックする。
実装例
AbstractPoolクラスは、プールクラスのインターフェースです。すべてのプールはこれを継承する。
各プールオブジェクトが successor_pool へのポインタを格納できることに注意必要。そのようなポインタが割り当てられていない場合は、そのプールがチェーンの最後のプールであると仮定する。
get_match関数では、候補者のプールにマッチするものが見つかった場合は、その候補者が返され、そうでない場合は、責任がチェーンの次のプールに伝播される。
_find関数の実装は、プール内の最初の候補を返す。パターンの概念とは関係のない実装で、正式な実装はプールごとに異なることもあり、ケースにより様々。
class AbstractPool:
candidates = []
def __init__(self, successor_pool=None):
self._successor = successor_pool
def get_match(self, params):
match = self._find(params)
if match:
return match
elif self._successor:
return self._successor.get_match(params)
def _find(self, params):
for candidate in self.__class__.candidates:
if all(key in candidate.items() for key in params.items()):
print("found ! :", self.__class__.__name__)
return candidate
print("Not found in {}.".format(self.__class__.__name__))
class LocalPool(AbstractPool):
candidates = [
{"id": 1, "type": "developer", "level": "junior"},
{"id": 2, "type": "designer", "level": "intermediate"}
]
class RegionalPool(AbstractPool):
candidates = [
{"id": 13, "type": "developer", "level": "junior"},
{"id": 21, "type": "designer", "level": "senior"}
]
class GlobalPool(AbstractPool):
candidates = [
{"id": 124, "type": "developer", "level": "senior"},
{"id": 489, "type": "developer", "level": "intermediate"}
]
if __name__ == "__main__":
global_pool = GlobalPool()
regional_pool = RegionalPool(global_pool)
local_pool = LocalPool(regional_pool)
print(local_pool.get_match({"type": "developer", "level": "senior"}))