引言:动态扩展对象功能

面向对象编程中,我们经常面临一个设计难题:如何在不修改现有代码的情况下,动态地给对象添加新的功能?继承是解决方案之一,但它有时会导致类的爆炸性增长,而且编译时就已经确定了功能扩展,缺乏运行时灵活性。

装饰器模式(Decorator Pattern) 提供了一种更加优雅的解决方案。它允许我们通过将对象包装在装饰器对象中,动态地向对象添加新的行为。在Python中,装饰器模式可以通过多种方式实现,包括使用函数装饰器和类装饰器。

在本节中,我们将深入探讨装饰器模式在类中的应用,并通过银行账户管理系统的实战示例展示其强大威力。


第一部分:装饰器模式基础

1.1 什么是装饰器模式?

装饰器模式是一种结构型设计模式,它允许我们通过将对象包装在装饰器对象中,动态地向对象添加新的行为。装饰器对象与原始对象具有相同的接口,因此客户端代码无需知道它是在与装饰器还是原始对象交互。

1.2 装饰器模式的核心组件

  1. 组件接口(Component Interface):定义了被装饰对象和装饰器的共同接口
  2. 具体组件(Concrete Component):实现了组件接口的具体对象
  3. 装饰器基类(Decorator Base Class):实现了组件接口,并持有一个组件对象的引用
  4. 具体装饰器(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中非常强大和灵活的设计模式,它允许我们动态地扩展对象的功能,而无需修改现有代码或使用继承。通过装饰器模式,我们可以实现关注点分离,使代码更加模块化和可维护。

在我们的银行账户系统实战中,我们看到了如何通过装饰器模式逐步添加透支保护、计息、交易手续费、权限控制、缓存和日志记录等功能。这些功能可以任意组合,提供了极大的灵活性。

思考题:

  1. 如何实现一个可以动态移除的装饰器?
  2. 装饰器模式与代理模式有什么区别和联系?
  3. 如果装饰器需要修改被装饰对象的方法签名,应该如何处理?
  4. 如何避免装饰器栈过深导致的性能问题?

更多推荐