KnowHow

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

Python基礎 オブジェクト指向14(Composite) 構造に関するデザインパターン

登録日 :2025/10/13 12:56
カテゴリ :Python基礎

Decorator

基本となるオブジェクトに対して、柔軟に機能追加をするパターン

  • 継承よりも柔軟で、動的に機能追加が可能
     (不要なサブクラスを追加しないなど)

  • 基本のオブジェクトを包むように見えるので、Wrapperパターンと呼ばれることもある。

構成要素

Component

  • 抽象クラスまたはインターフェース
  • 拡張される基本機能のAPIを定義

ConcreteComponent

  • Componentを継承(実装)する子クラス
  • 基本機能の具体的な実装を行う

Decorator

  • Componentを継承(実装)し、基本機能の拡張(装飾)を行う抽象クラス
  • 内部にComponentのAPIを保持
  • ComponentのAPI定義の実装は子クラスに任せる

ConcreteDecorator

  • Decoratorを継承する子クラス
  • Componentの基本機能に具体的な拡張(装飾)を行う

オブジェクト指向的要素

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

Component -> ConreteComponent
Component -> Decorator
Decorator -> ConcreteDecorator

  • 機能を「追加される側」のComponentと、「追加する側」のDecoratorが同じAPIを持っており、利用者はどちらを操作しているかを意識する必要がない

メリット・デメリット

メリット

  • 実行時の機能追加が容易にできる(継承しなくて良い)
  • 複数の機能を組み合わせることが可能

デメリット

  • 組み合わせた機能から特定の機能の削除は困難
  • 振る舞いがデコレーターの組み合わせの順序に依存すr

使い所

  • 追加したい機能のパターンが複数ある場合
  • 追加したい機能のパターンに順序がある場合
  • 継承を使ってオブジェクトの機能拡張が困難な場合
    (Pythonには存在しないが、finalキーワードが付いたクラスなど)

サンプルコード

ログの出力を拡張する例

import datetime
from abc import ABCMeta, abstractmethod


class Component(metaclass=ABCMeta):
    @abstractmethod
    def get_log_message(self, _msg: str) -> str:
        pass


class Logger(Component):
    def get_log_message(self, _msg: str) -> str:
        return _msg


class Decorator(Component):
    def __init__(self, _component: Component):
        self._component = _component

    @abstractmethod
    def get_log_message(self, _msg: str) -> str:
        pass


class TimestampDecorator(Decorator):
    def __init__(self, _component: Component):
        super().__init__(_component)

    def get_log_message(self, _msg: str) -> str:
        now = datetime.datetime.now()
        timestamp = now.strftime("%Y-%m-%d %H:%M:%S")
        return self._component.get_log_message(f"{timestamp}: {_msg}")


class LogLevelDecorator(Decorator):
    def __init__(self, _component: Component, _log_level: str):
        super().__init__(_component)
        self._log_level = _log_level

    def get_log_message(self, _msg: str) -> str:
        return self._component.get_log_message(f"[{self._log_level}]{_msg}")


if __name__ == "__main__":
    message="Test Decorator pattern"
    logger = Logger()
    log_level_logger = LogLevelDecorator(logger, "INFO")
    timestamp_logger = TimestampDecorator(log_level_logger)

    print(logger.get_log_message(message))
    print(log_level_logger.get_log_message(message))
    print(timestamp_logger.get_log_message(message))