0
1

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で実装するDecoratorパターン:振る舞いの動的付加と再利用可能な拡張

Posted at

概要

Decorator(デコレータ)パターンは、
元のオブジェクトの構造を変えずに、動的に新しい振る舞いを付加する設計パターンである。

Pythonではこのパターンが文法としてサポートされており、関数・クラス・メソッドの柔軟な拡張に使われる。


1. なぜDecoratorが必要か?

❌ 振る舞いを追加するたびにサブクラスを量産

class LoggingComponent(Component): ...
class CachingComponent(Component): ...
class AuthLoggingComponent(Component): ...

組み合わせ爆発が起こりやすく、拡張が非効率


✅ 振る舞いをラップして段階的に拡張可能に

decorated = AuthDecorator(LoggingDecorator(component))
decorated.operation()

必要な機能だけを積層し、再利用性と柔軟性を両立


2. クラスベースの基本構造

✅ Component インターフェース

class Component:
    def operation(self):
        raise NotImplementedError

✅ ConcreteComponent(元の処理)

class Text(Component):
    def operation(self):
        return "Hello"

✅ Decorator 基底クラス

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

✅ 複数のDecorator実装

class BoldDecorator(Decorator):
    def operation(self):
        return f"<b>{self._component.operation()}</b>"

class ItalicDecorator(Decorator):
    def operation(self):
        return f"<i>{self._component.operation()}</i>"

✅ 使用例

text = Text()
decorated = ItalicDecorator(BoldDecorator(text))
print(decorated.operation())

出力:

<i><b>Hello</b></i>

見た目の機能をデコレーションとして積層できる


3. Python的応用:関数デコレータによる振る舞い付加

def log(func):
    def wrapper(*args, **kwargs):
        print(f"[LOG] calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log
def greet(name):
    print(f"Hello, {name}!")

greet("world")

出力:

[LOG] calling greet  
Hello, world!

関数に動的な振る舞いを加えるPythonの典型


4. 実務ユースケース

✅ ログ出力・タイミング計測・キャッシュなどの関数拡張

→ デコレータで共通処理を再利用可能に


✅ 認証・権限チェックをAPI関数に付加

@require_login, @admin_only などで条件付き動作を明示的に設計


✅ UIコンポーネントやHTMLテンプレートの動的スタイル適用

→ 構造は変えずに視覚・振る舞いを柔軟に追加


✅ テスト対象にモック/スタブ/トレースを挿入

@patch などで一時的な機能差し替えが可能


5. よくある誤用と対策

❌ デコレータがネストされすぎて挙動が不透明

→ ✅ ログやコメントで装飾順を可視化


❌ ラップ後の関数名やdocstringが消失

→ ✅ functools.wraps(func) を忘れずに使う

from functools import wraps

def log(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        ...
    return wrapper

❌ デコレータがグローバル状態に依存してしまう

→ ✅ クロージャ内に状態を閉じ込め、純粋関数性を意識する


結語

Decoratorパターンとは、“構造は保ちつつ、振る舞いだけを優雅に重ねていく技法”である。

  • 新しい機能を動的に追加可能で、オープン/クローズド原則に忠実
  • サブクラス化ではなく構造化された積層設計で拡張可能
  • Pythonでは構文としてサポートされ、OOP・関数型どちらにも適用可能

Pythonicとは、“変化を柔らかく包み込み、積み上げること”。
Decoratorパターンはその拡張性を、静かな重ね合わせの中に秘めた技法である。

0
1
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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?