Pythonにおける装飾パターンとデコレータの実用的活用

ソフトウェア開発において、システムが複雑化すると要件変更に伴うコード修正が頻繁に発生します。柔軟性と保守性を高めるために、装飾パターン(Decorator Pattern)が広く利用されています。この構造型デザインパターンは、オブジェクト自体を変更せずに動的に機能を追加できる仕組みを提供します。

装飾パターンの基本構成要素

装飾パターンは、共通インターフェース・ベースコンポーネント・抽象装飾クラス・具体的な装飾クラスの4つの要素で構成されます。

from abc import ABC, abstractmethod

class Beverage(ABC):
    @abstractmethod
    def get_price(self) -> float:
        pass

class BasicTea(Beverage):
    def get_price(self) -> float:
        return 4.0

class CondimentDecorator(Beverage):
    def __init__(self, beverage: Beverage):
        self._beverage = beverage

    def get_price(self) -> float:
        return self._beverage.get_price()

class LemonSlice(CondimentDecorator):
    def get_price(self) -> float:
        return self._beverage.get_price() + 0.8

class HoneySyrup(CondimentDecorator):
    def get_price(self) -> float:
        return self._beverage.get_price() + 1.2

使用例:カスタマイズ可能なドリンク注文

# 基本の紅茶
tea = BasicTea()
print(f"基本料金: ¥{tea.get_price()}")

# レモン追加
lemon_tea = LemonSlice(tea)
print(f"レモン入り: ¥{lemon_tea.get_price()}")

# さらにハチミツ追加
sweet_tea = HoneySyrup(lemon_tea)
print(f"レモン+ハチミツ: ¥{sweet_tea.get_price()}")

Pythonデコレータの実践的な応用

Pythonのデコレータは関数やメソッドの振る舞いを動的に拡張する強力な機能です。以下は実務でよく使われるパターンです。

認証チェックデコレータ

def require_admin(func):
    def wrapper(user_role, *args, **kwargs):
        if user_role != "admin":
            raise PermissionError("管理者権限が必要です")
        return func(user_role, *args, **kwargs)
    return wrapper

@require_admin
def delete_user(user_role, user_id):
    return f"ユーザーID {user_id} を削除しました"

# 使用例
try:
    result = delete_user("admin", 123)
    print(result)
except PermissionError as e:
    print(e)

キャッシュ機能付きデコレータ

import functools
import time

def cache_result(expiry_seconds=60):
    def decorator(func):
        cache = {}
        
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            key = str(args) + str(sorted(kwargs.items()))
            current_time = time.time()
            
            # キャッシュが有効かチェック
            if key in cache:
                value, timestamp = cache[key]
                if current_time - timestamp < expiry_seconds:
                    print("キャッシュから取得")
                    return value
            
            # 関数を実行して結果をキャッシュ
            result = func(*args, **kwargs)
            cache[key] = (result, current_time)
            print("新規計算結果をキャッシュ")
            return result
        
        return wrapper
    return decorator

@cache_result(expiry_seconds=30)
def expensive_calculation(x, y):
    time.sleep(2)  # 模擬的な重い処理
    return x ** y

# 使用例
print(expensive_calculation(2, 10))  # 新規計算
print(expensive_calculation(2, 10))  # キャッシュから取得

多重デコレータの実装例

def timing_logger(func):
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        print(f"関数 '{func.__name__}' の実行開始")
        result = func(*args, **kwargs)
        end = time.perf_counter()
        print(f"実行時間: {end - start:.4f}秒")
        return result
    return wrapper

def input_validator(min_val=0, max_val=100):
    def decorator(func):
        def wrapper(value, *args, **kwargs):
            if not min_val <= value <= max_val:
                raise ValueError(f"値は{min_val}〜{max_val}の範囲である必要があります")
            return func(value, *args, **kwargs)
        return wrapper
    return decorator

@timing_logger
@input_validator(min_val=1, max_val=50)
def process_data(amount):
    time.sleep(0.1)  # 処理の模擬
    return f"データ量 {amount} を処理完了"

# 使用例
try:
    result = process_data(25)
    print(result)
    process_data(100)  # 範囲外でエラー
except ValueError as e:
    print(f"バリデーションエラー: {e}")

クラスメソッドへのデコレータ適用

def audit_trail(action_name):
    def decorator(method):
        def wrapper(self, *args, **kwargs):
            timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
            user = getattr(self, 'current_user', 'anonymous')
            print(f"[{timestamp}] ユーザー '{user}' が '{action_name}' を実行")
            return method(self, *args, **kwargs)
        return wrapper
    return decorator

class DocumentManager:
    def __init__(self, user):
        self.current_user = user
        self.documents = {}
    
    @audit_trail("ドキュメント作成")
    def create_document(self, doc_id, content):
        self.documents[doc_id] = content
        return f"ドキュメント {doc_id} を作成"
    
    @audit_trail("ドキュメント削除")
    def delete_document(self, doc_id):
        if doc_id in self.documents:
            del self.documents[doc_id]
            return f"ドキュメント {doc_id} を削除"
        return f"ドキュメント {doc_id} は存在しません"

# 使用例
manager = DocumentManager("田中太郎")
print(manager.create_document("DOC-001", "重要文書"))
print(manager.delete_document("DOC-001"))

タグ: Python デザインパターン デコレータ 装飾パターン ソフトウェアアーキテクチャ

7月20日 20:54 投稿