0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonで実装するChain of Responsibilityパターン:責任の流れを直列に構成する

Posted at

概要

Chain of Responsibility(責任の連鎖)パターンは、
複数の処理者(ハンドラ)を直列に構成し、要求を順に委譲していく設計パターンである。

各ハンドラは「自分が処理できるかどうか」を判断し、処理できなければ次のハンドラにバトンを渡す。これにより、処理フローの柔軟な拡張とカプセル化が可能になる。


1. なぜChain of Responsibilityが必要か?

❌ 条件分岐で処理ルートを管理するとメンテナンスが困難に

def handle(request):
    if request == "auth":
        ...
    elif request == "log":
        ...
    elif request == "cache":
        ...

条件が増えるたびにif/elifが複雑化していく


✅ 各処理をオブジェクトに分離し、責任の流れを構成する

auth.set_next(logger).set_next(cacher)
auth.handle("log")

処理を柔軟に組み換え可能で、単一責任原則も維持できる


2. 基本構造

✅ Handlerインターフェース

class Handler:
    def set_next(self, handler):
        self._next_handler = handler
        return handler

    def handle(self, request):
        if self._next_handler:
            return self._next_handler.handle(request)
        return None

✅ ConcreteHandler(具体的な処理者)

class AuthHandler(Handler):
    def handle(self, request):
        if request == "auth":
            print("認証処理を実行")
        else:
            super().handle(request)

class LogHandler(Handler):
    def handle(self, request):
        if request == "log":
            print("ログ記録処理を実行")
        else:
            super().handle(request)

class CacheHandler(Handler):
    def handle(self, request):
        if request == "cache":
            print("キャッシュ処理を実行")
        else:
            super().handle(request)

✅ 使用例

auth = AuthHandler()
logger = LogHandler()
cache = CacheHandler()

auth.set_next(logger).set_next(cache)

auth.handle("auth")   # → 認証処理
auth.handle("log")    # → ログ記録処理
auth.handle("cache")  # → キャッシュ処理
auth.handle("other")  # → 誰も処理しない

出力:

認証処理を実行  
ログ記録処理を実行  
キャッシュ処理を実行

3. Python的応用:デコレータやミドルウェアとしての連鎖設計

def middleware1(req, nxt): print("M1"); nxt(req)
def middleware2(req, nxt): print("M2"); nxt(req)
def middleware3(req, nxt): print("M3"); nxt(req)

def chain(middlewares, final):
    def execute(req):
        def call(index):
            if index < len(middlewares):
                middlewares[index](req, lambda r: call(index + 1))
            else:
                final(r)
        call(0)
    return execute

runner = chain([middleware1, middleware2, middleware3], lambda r: print("完了"))
runner("リクエスト")

ミドルウェアやイベントパイプラインにも応用可能


4. 実務ユースケース

✅ HTTPリクエスト処理のミドルウェア(Django / Flask)

認証 → ログ → キャッシュ → バリデーション の流れを構成可能


✅ ロギング処理の段階的適用(標準出力→ファイル→ネットワーク)

→ ログレベルに応じて適切なハンドラに分岐


✅ 入力バリデーションのルールチェーン

→ 順に検証を行い、失敗時に即中断、成功時は次へ


✅ セキュリティチェック、フィルタリング、サニタイジング

責任の分担によって、処理の独立性を保つ


5. よくある誤用と対策

❌ ハンドラが1つで全ての責任を背負ってしまう

→ ✅ ハンドラごとの単一責任を守る


❌ チェーン構成が静的で柔軟に変えられない

→ ✅ 実行時に組み替えられるようset_nextを提供する


❌ ハンドラが処理を流すかどうかを忘れて止まる

→ ✅ super().handle()を必ず呼ぶ構造を意識的に設計


結語

Chain of Responsibilityパターンとは、“責任の流れを構造化することで、処理を動的かつ柔軟に委譲する設計”である。

  • 各処理をモジュール化し、連鎖することで柔軟なフローを構成
  • 責任の範囲と処理順を分離し、保守性と拡張性を高める
  • Pythonではクラス連結・関数チェーン・ミドルウェア構造に自然に適用可能

Pythonicとは、“処理の責任を持ちすぎず、次に委ねること”。
Chain of Responsibilityパターンはその責任の知性を、設計の流動性として昇華する技法である。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?