KnowHow

技術的なメモを中心にまとめます。
検索にて調べることができます。

Python基礎 オブジェクト指向12(Chain of Responsibility) 振る舞いに関するデザインパターン

登録日 :2025/10/05 11:13
カテゴリ :Python基礎

Chain of Responsibility

クライアントからのリクエストを処理するオブジェクトを鎖のように繋げ、処理が可能なオブジェクトにリクエストを順番に渡していくパターン

  • バケツリレーのようなイメージ
  • リクエストが渡されたオブジェクト(ハンドラ)は自信で処理する必要があるかどうかを判断し、自分で対処できない場合は次のハンドラに渡す

構成要素

Handler

  • 抽象クラスもしくはインターフェース
  • クライアントからの要求を処理するAPIを定義
  • 内部に別の処理を行うオブジェクトを保持

ConcreteHandler

  • Handlerを継承(実装)するクラス
  • Handlerで定義されたAPIを満たすように自身が担当する処理を実装する

Client

  • Handlerにリクエストを送信する

オブジェクト指向的要素

「ポリモーフィズム」を利用したパターン

  • オブジェクトのチェーンは全てHandler型であり、ConcreteHandlerクラスを意識する必要がない
  • HandlerのAPIを使用することで、ConcreteHandlerの切り替えが可能になる

メリット・デメリット

メリット

  • リクエスト処理の順序を制御できる
  • リクエストの送信側と受信側の結びつきを弱くする
  • 新しい処理クラスを容易に追加できる

デメリット

  • 処理がたらい回しにされるので、パフォーマンスに影響が出る可能性がある

使い所

特定の順序で複数の処理を実行する必要がある場合
例)フォームの入力値バリデーション

Handlerの組み合わせと順序を実行時に変更したい場合
setterメソッドでnextをセットすることで、実行時に次の処理を決定できる

サンプルコード

入力された文字列のバリデーション

from __future__ import annotations
"""自身のクラスを戻り値に設定するため、annotationsが必要"""
import re
from abc import ABCMeta, abstractmethod


class ValidationHandler(metaclass=ABCMeta):
    def __init__(self):
        self.__next_handler = None

    def set_handler(self, _handler: ValidationHandler):
        self.__next_handler = _handler
        return _handler

    @abstractmethod
    def _exec_validation(self, _input: str) -> bool:
        pass

    @abstractmethod
    def _get_error_message(self):
        pass

    def validate(self, _input: str) -> bool:
        result = self._exec_validation(_input)
        if not result:
            self._get_error_message()
            return False
        elif self.__next_handler:
            return self.__next_handler.validate(_input)
        else:
            return True


class NotNullValidationHandler(ValidationHandler):
    def _exec_validation(self, _input: str) -> bool:
        result = False
        if _input:
            result = True

        print(f"[INFO]: NotNullValidation: {result}")
        return result

    def _get_error_message(self):
        print("[ERROR]: Input is Null.")


class AlphabetValidationHandler(ValidationHandler):
    def _exec_validation(self, _input: str) -> bool:
        reg = re.compile("^[a-zA-Z]+$")
        result = bool(re.search(reg, _input))
        print(f"[INFO]: AlphabetValidationHandler: {result}")
        return result

    def _get_error_message(self):
        print("[ERROR]: Please input Alphabet only.")


MIN_LENGTH = 8


class MinLengthValidationHandler(ValidationHandler):
    def _exec_validation(self, _input: str) -> bool:
        result = (len(_input) >= MIN_LENGTH)
        print(f"[INFO]: MinLengthValidationHandler: {result}")
        return result

    def _get_error_message(self):
        print(f"[ERROR]: Please input character which size are more than {MIN_LENGTH}")


if __name__ == '__main__':
    not_null_handler = NotNullValidationHandler()
    alphabet_handler = AlphabetValidationHandler()
    min_length_handler = MinLengthValidationHandler()

    # not null -> alphabet -> min length

    not_null_handler.set_handler(alphabet_handler).set_handler(min_length_handler)

    result = not_null_handler.validate('')
    if result:
        print("passed")

    print('')
    result = not_null_handler.validate('1')
    if result:
        print("passed")

    print('')
    result = not_null_handler.validate('a')
    if result:
        print("passed")

    print('--- success case ---')
    result = not_null_handler.validate('aaaaaaaaaaa')
    if result:
        print("passed")