ソフトウェア開発において、システムが複雑化すると要件変更に伴うコード修正が頻繁に発生します。柔軟性と保守性を高めるために、装飾パターン(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"))