2.6 Python 装饰器模式在类中的应用
引言:动态扩展对象功能
在面向对象编程中,我们经常面临一个设计难题:如何在不修改现有代码的情况下,动态地给对象添加新的功能?继承是解决方案之一,但它有时会导致类的爆炸性增长,而且编译时就已经确定了功能扩展,缺乏运行时灵活性。
装饰器模式(Decorator Pattern) 提供了一种更加优雅的解决方案。它允许我们通过将对象包装在装饰器对象中,动态地向对象添加新的行为。在Python中,装饰器模式可以通过多种方式实现,包括使用函数装饰器和类装饰器。
在本节中,我们将深入探讨装饰器模式在类中的应用,并通过银行账户管理系统的实战示例展示其强大威力。
第一部分:装饰器模式基础
1.1 什么是装饰器模式?
装饰器模式是一种结构型设计模式,它允许我们通过将对象包装在装饰器对象中,动态地向对象添加新的行为。装饰器对象与原始对象具有相同的接口,因此客户端代码无需知道它是在与装饰器还是原始对象交互。
1.2 装饰器模式的核心组件
- 组件接口(Component Interface):定义了被装饰对象和装饰器的共同接口
- 具体组件(Concrete Component):实现了组件接口的具体对象
- 装饰器基类(Decorator Base Class):实现了组件接口,并持有一个组件对象的引用
- 具体装饰器(Concrete Decorators):扩展装饰器基类,添加新的功能
1.3 装饰器模式 vs 继承
| 特性 | 继承 | 装饰器模式 |
|---|---|---|
| 扩展方式 | 编译时静态扩展 | 运行时动态扩展 |
| 灵活性 | 较低,类层次固定 | 较高,可任意组合装饰器 |
| 类数量 | 可能导致类爆炸 | 装饰器可复用,类数量可控 |
| 代码复用 | 通过继承复用 | 通过组合复用 |
第二部分:Python中的装饰器实现方式
2.1 函数装饰器
Python中最常见的装饰器形式是函数装饰器,它可以用于装饰类的方法:
def log_method_call(func):
"""记录方法调用的装饰器"""
def wrapper(self, *args, **kwargs):
print(f"调用方法: {func.__name__}, 参数: {args}, {kwargs}")
result = func(self, *args, **kwargs)
print(f"方法 {func.__name__} 执行完成, 结果: {result}")
return result
return wrapper
class BankAccount:
def __init__(self, account_holder, balance=0):
self.account_holder = account_holder
self.balance = balance
@log_method_call
def deposit(self, amount):
self.balance += amount
return self.balance
@log_method_call
def withdraw(self, amount):
if amount <= self.balance:
self.balance -= amount
return True
return False
# 使用示例
account = BankAccount("Alice", 1000)
account.deposit(500) # 会自动输出日志
2.2 类装饰器
Python也支持使用类作为装饰器:
class LogMethodCalls:
"""记录类中所有方法调用的类装饰器"""
def __init__(self, cls):
self.cls = cls
def __call__(self, *args, **kwargs):
# 创建原始类的实例
instance = self.cls(*args, **kwargs)
# 遍历类的所有方法
for attr_name in dir(instance):
attr = getattr(instance, attr_name)
if callable(attr) and not attr_name.startswith("__"):
# 为方法添加日志功能
setattr(instance, attr_name, self._create_logged_method(attr))
return instance
def _create_logged_method(self, method):
def logged_method(*args, **kwargs):
print(f"调用方法: {method.__name__}, 参数: {args[1:]}, {kwargs}")
result = method(*args, **kwargs)
print(f"方法 {method.__name__} 执行完成, 结果: {result}")
return result
return logged_method
@LogMethodCalls
class BankAccount:
def __init__(self, account_holder, balance=0):
self.account_holder = account_holder
self.balance = balance
def deposit(self, amount):
self.balance += amount
return self.balance
def withdraw(self, amount):
if amount <= self.balance:
self.balance -= amount
return True
return False
# 使用示例
account = BankAccount("Bob", 1000)
account.deposit(500) # 会自动输出日志
第三部分:经典的装饰器模式实现
3.1 定义组件接口
首先,我们定义一个银行账户的抽象基类作为组件接口:
from abc import ABC, abstractmethod
class BankAccount(ABC):
"""银行账户抽象基类(组件接口)"""
@abstractmethod
def get_balance(self):
pass
@abstractmethod
def deposit(self, amount):
pass
@abstractmethod
def withdraw(self, amount):
pass
@abstractmethod
def get_account_info(self):
pass
3.2 实现具体组件
接下来,我们实现一个具体的银行账户类:
class BasicBankAccount(BankAccount):
"""基本银行账户(具体组件)"""
def __init__(self, account_holder, initial_balance=0):
self.account_holder = account_holder
self._balance = initial_balance
self.account_number = f"ACC{random.randint(100000, 999999)}"
def get_balance(self):
return self._balance
def deposit(self, amount):
if amount <= 0:
raise ValueError("存款金额必须为正数")
self._balance += amount
return True
def withdraw(self, amount):
if amount <= 0:
raise ValueError("取款金额必须为正数")
if amount > self._balance:
return False
self._balance -= amount
return True
def get_account_info(self):
return f"{self.account_number} - {self.account_holder}: ${self._balance:.2f}"
3.3 实现装饰器基类
现在,我们实现装饰器基类:
class BankAccountDecorator(BankAccount):
"""银行账户装饰器基类"""
def __init__(self, wrapped_account):
self._wrapped_account = wrapped_account
def get_balance(self):
return self._wrapped_account.get_balance()
def deposit(self, amount):
return self._wrapped_account.deposit(amount)
def withdraw(self, amount):
return self._wrapped_account.withdraw(amount)
def get_account_info(self):
return self._wrapped_account.get_account_info()
3.4 实现具体装饰器
最后,我们实现几个具体装饰器来添加不同的功能:
class OverdraftProtectionDecorator(BankAccountDecorator):
"""透支保护装饰器"""
def __init__(self, wrapped_account, overdraft_limit=100):
super().__init__(wrapped_account)
self.overdraft_limit = overdraft_limit
def withdraw(self, amount):
available_balance = self.get_balance() + self.overdraft_limit
if amount <= available_balance:
# 这里简化处理,实际中可能需要更复杂的逻辑
if amount > self.get_balance():
print(f"警告: 使用透支保护,透支金额: ${amount - self.get_balance():.2f}")
return self._wrapped_account.withdraw(amount)
return False
def get_account_info(self):
base_info = super().get_account_info()
return f"{base_info} (透支保护: ${self.overdraft_limit:.2f})"
class InterestBearingDecorator(BankAccountDecorator):
"""计息装饰器"""
def __init__(self, wrapped_account, interest_rate=0.03):
super().__init__(wrapped_account)
self.interest_rate = interest_rate
def apply_interest(self):
interest = self.get_balance() * self.interest_rate
self.deposit(interest)
return interest
def get_account_info(self):
base_info = super().get_account_info()
return f"{base_info} (年利率: {self.interest_rate*100:.1f}%)"
class TransactionFeeDecorator(BankAccountDecorator):
"""交易手续费装饰器"""
def __init__(self, wrapped_account, fee_per_transaction=1.0):
super().__init__(wrapped_account)
self.fee_per_transaction = fee_per_transaction
def withdraw(self, amount):
total_amount = amount + self.fee_per_transaction
if self.get_balance() >= total_amount:
# 先扣手续费
self._wrapped_account.withdraw(self.fee_per_transaction)
# 再扣取款金额
return self._wrapped_account.withdraw(amount)
return False
def get_account_info(self):
base_info = super().get_account_info()
return f"{base_info} (每笔取款手续费: ${self.fee_per_transaction:.2f})"
第四部分:综合实战 —— 装饰银行账户系统
现在,让我们将这些装饰器组合起来,创建一个功能丰富的银行账户系统:
# 创建基本账户
basic_account = BasicBankAccount("Alice", 500)
print("基本账户:", basic_account.get_account_info())
# 添加透支保护
protected_account = OverdraftProtectionDecorator(basic_account, 200)
print("带透支保护的账户:", protected_account.get_account_info())
# 添加计息功能
interest_account = InterestBearingDecorator(protected_account, 0.04)
print("带计息的账户:", interest_account.get_account_info())
# 添加交易手续费
final_account = TransactionFeeDecorator(interest_account, 1.5)
print("最终账户:", final_account.get_account_info())
print("\n--- 进行一些操作 ---")
# 存款
final_account.deposit(300)
print("存款后余额:", final_account.get_balance())
# 取款(会扣手续费)
success = final_account.withdraw(400)
print(f"取款400 {'成功' if success else '失败'}, 余额: {final_account.get_balance()}")
# 应用利息
interest = interest_account.apply_interest()
print(f"应用利息: ${interest:.2f}, 新余额: {final_account.get_balance()}")
# 尝试透支取款
success = final_account.withdraw(600)
print(f"取款600 {'成功' if success else '失败'}, 余额: {final_account.get_balance()}")
输出结果:
基本账户: ACC123456 - Alice: $500.00
带透支保护的账户: ACC123456 - Alice: $500.00 (透支保护: $200.00)
带计息的账户: ACC123456 - Alice: $500.00 (透支保护: $200.00) (年利率: 4.0%)
最终账户: ACC123456 - Alice: $500.00 (透支保护: $200.00) (年利率: 4.0%) (每笔取款手续费: $1.50)
--- 进行一些操作 ---
存款后余额: 800.0
警告: 使用透支保护,透支金额: $1.50
取款400 成功, 余额: 398.5
应用利息: $15.94, 新余额: 414.44
取款600 失败, 余额: 414.44
4.1 动态组合装饰器
装饰器模式的强大之处在于可以动态组合不同的功能:
def create_account(account_holder, balance, features=None):
"""账户工厂函数,根据需求动态添加功能"""
account = BasicBankAccount(account_holder, balance)
if features:
if 'overdraft' in features:
account = OverdraftProtectionDecorator(account, features['overdraft'])
if 'interest' in features:
account = InterestBearingDecorator(account, features['interest'])
if 'transaction_fee' in features:
account = TransactionFeeDecorator(account, features['transaction_fee'])
return account
# 创建不同功能的账户
account1 = create_account("Bob", 1000, {'overdraft': 500})
account2 = create_account("Charlie", 1500, {'interest': 0.05, 'transaction_fee': 2.0})
account3 = create_account("Diana", 2000, {
'overdraft': 1000,
'interest': 0.06,
'transaction_fee': 1.0
})
print(account1.get_account_info())
print(account2.get_account_info())
print(account3.get_account_info())
第五部分:高级装饰器模式应用
5.1 使用装饰器实现权限控制
class AuthorizationDecorator(BankAccountDecorator):
"""权限控制装饰器"""
def __init__(self, wrapped_account, user_roles):
super().__init__(wrapped_account)
self.user_roles = user_roles
def withdraw(self, amount):
if 'withdraw' not in self.user_roles:
print("错误: 没有取款权限")
return False
return super().withdraw(amount)
def deposit(self, amount):
if 'deposit' not in self.user_roles:
print("错误: 没有存款权限")
return False
return super().deposit(amount)
def get_account_info(self):
if 'view' not in self.user_roles:
return "错误: 没有查看账户信息的权限"
return super().get_account_info()
# 使用示例
basic_account = BasicBankAccount("Alice", 1000)
restricted_account = AuthorizationDecorator(basic_account, ['view']) # 只有查看权限
print(restricted_account.get_account_info()) # 可以查看
restricted_account.deposit(500) # 错误: 没有存款权限
5.2 使用装饰器实现缓存
class CachingDecorator(BankAccountDecorator):
"""缓存装饰器"""
def __init__(self, wrapped_account):
super().__init__(wrapped_account)
self._balance_cache = None
self._info_cache = None
self._cache_valid = False
def get_balance(self):
if not self._cache_valid or self._balance_cache is None:
self._balance_cache = super().get_balance()
self._cache_valid = True
return self._balance_cache
def get_account_info(self):
if not self._cache_valid or self._info_cache is None:
self._info_cache = super().get_account_info()
self._cache_valid = True
return self._info_cache
def deposit(self, amount):
result = super().deposit(amount)
self._invalidate_cache()
return result
def withdraw(self, amount):
result = super().withdraw(amount)
self._invalidate_cache()
return result
def _invalidate_cache(self):
self._cache_valid = False
self._balance_cache = None
self._info_cache = None
5.3 使用装饰器实现日志记录
class LoggingDecorator(BankAccountDecorator):
"""日志记录装饰器"""
def __init__(self, wrapped_account, logger=None):
super().__init__(wrapped_account)
self.logger = logger or print
def deposit(self, amount):
self.logger(f"尝试存款: ${amount:.2f}")
result = super().deposit(amount)
self.logger(f"存款{'成功' if result else '失败'}, 新余额: ${self.get_balance():.2f}")
return result
def withdraw(self, amount):
self.logger(f"尝试取款: ${amount:.2f}")
result = super().withdraw(amount)
self.logger(f"取款{'成功' if result else '失败'}, 新余额: ${self.get_balance():.2f}")
return result
def get_balance(self):
balance = super().get_balance()
self.logger(f"查询余额: ${balance:.2f}")
return balance
第六部分:装饰器模式与Python特性结合
6.1 使用描述符增强装饰器
class Validated:
"""使用描述符实现验证装饰器"""
def __init__(self, validator):
self.validator = validator
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__.get(self.name)
def __set__(self, instance, value):
if not self.validator(value):
raise ValueError(f"无效的值: {value}")
instance.__dict__[self.name] = value
def positive_number(value):
return isinstance(value, (int, float)) and value >= 0
class BankAccount:
balance = Validated(positive_number)
def __init__(self, account_holder, balance=0):
self.account_holder = account_holder
self.balance = balance # 会自动验证
# 使用示例
account = BankAccount("Alice", 1000)
# account.balance = -500 # 会抛出 ValueError
6.2 使用元类自动添加装饰器
class LoggingMeta(type):
"""元类,自动为所有方法添加日志"""
def __new__(cls, name, bases, attrs):
# 遍历类的属性
for attr_name, attr_value in attrs.items():
if callable(attr_value) and not attr_name.startswith("__"):
# 为方法添加日志功能
attrs[attr_name] = cls._add_logging(attr_value, attr_name)
return super().__new__(cls, name, bases, attrs)
@staticmethod
def _add_logging(method, method_name):
def logged_method(self, *args, **kwargs):
print(f"调用方法: {method_name}, 参数: {args}, {kwargs}")
result = method(self, *args, **kwargs)
print(f"方法 {method_name} 执行完成, 结果: {result}")
return result
return logged_method
class BankAccount(metaclass=LoggingMeta):
def __init__(self, account_holder, balance=0):
self.account_holder = account_holder
self.balance = balance
def deposit(self, amount):
self.balance += amount
return self.balance
def withdraw(self, amount):
if amount <= self.balance:
self.balance -= amount
return True
return False
# 使用示例
account = BankAccount("Bob", 1000)
account.deposit(500) # 会自动输出日志
第七部分:常见面试题深度解析
7.1 Q:装饰器模式和继承有什么区别?
A:装饰器模式通过组合而非继承来扩展功能,提供了更大的灵活性:
- 继承是静态的,编译时确定;装饰器是动态的,运行时确定
- 装饰器可以在运行时动态添加和移除功能
- 装饰器避免了类爆炸问题,可以混合匹配多个装饰器
- 继承破坏封装性,子类知道父类的实现细节;装饰器保持更好的封装性
7.2 Q:装饰器模式的缺点是什么?
A:
- 会增加许多小对象,可能增加系统复杂度
- 装饰器栈可能变得很深,难以理解和调试
- 对于不需要装饰的对象,也会引入装饰器的开销
- 装饰器顺序很重要,不同的顺序可能导致不同的行为
7.3 Q:Python中的@decorator语法和装饰器模式有什么关系?
A:Python的@decorator语法是装饰器模式的一种语法糖实现。它本质上仍然是装饰器模式,但提供了更简洁的语法。@decorator可以用于函数和类,分别实现函数装饰器和类装饰器。
7.4 Q:什么时候应该使用装饰器模式?
A:
- 当需要在不影响其他对象的情况下,动态地给单个对象添加职责
- 当不能使用继承来扩展功能时(例如,final类)
- 当职责的组合会产生大量子类时
- 当需要随时添加或移除职责时
结语与思考
装饰器模式是Python中非常强大和灵活的设计模式,它允许我们动态地扩展对象的功能,而无需修改现有代码或使用继承。通过装饰器模式,我们可以实现关注点分离,使代码更加模块化和可维护。
在我们的银行账户系统实战中,我们看到了如何通过装饰器模式逐步添加透支保护、计息、交易手续费、权限控制、缓存和日志记录等功能。这些功能可以任意组合,提供了极大的灵活性。
思考题:
- 如何实现一个可以动态移除的装饰器?
- 装饰器模式与代理模式有什么区别和联系?
- 如果装饰器需要修改被装饰对象的方法签名,应该如何处理?
- 如何避免装饰器栈过深导致的性能问题?
更多推荐



所有评论(0)