Python

Python デザインパターンを学ぶ Chain of Responsibilityパターン

概要

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"}))