装饰器模式(Decorator Design Pattern)是指在不改变原有对象的基础上,附加新的功能到原对象上,使之具有增强的功能,装饰器模式的核心是功能的增强和扩展。
这种模式创建了一个装饰类,用来包装原有的类,并提供了额外的功能。
优缺点
优点
- 符合开闭原则,需要扩展时,不用修改原对象。
- 比继承更灵活,可以很方便地动态添加或减少装饰类,不容易导致继承爆炸和父子类耦合。
- 不同的装饰类可以相互自由排列组合,实现不同的效果。
缺点
- 多装饰器比较复杂,容易导致问题。
- 增加程序复杂性。
装饰器模式的构成
- 抽象类(主要实现抽象接口,规定了原始类的规范)
- 原始类(被装饰的类,继承抽象类)
- 装饰器类(用于装饰原始类增强功能,同样继承抽象类)
使用方式
- 定义抽象类,规定需要实现的接口规范。
- 实现原始类,满足原始类的功能需要。
- 实现抽象装饰类,继承自抽象类,规定装饰器需要实现的接口规范。
- 实现多种装饰器类,并用于增强原始类功能。
通过一个输出日志的例子来说明装饰器类。日志可以记录多种信息,基本的日志可以输出日志内容;可以通过增强功能,给日志添加输出时间、保存到文件、保存到数据库等功能。
抽象类
class LogABC:
@abstractmethod
def error(self):
pass
抽象类规定了,所有输出日志的日志类及日志类的功能增强类,都需要实现 error 这个方法。
原始类
class Log(LogABC):
def error(self):
print("log输出日志...")
原始类继承自抽象类,实现了基本的日志输出功能。
装饰器抽象类
class LogDeco(LogABC):
def error(self):
pass
装饰器抽象类继承自抽象类,定义了装饰器类需要实现的接口规范。
装饰器类
class TimeLog(LogDeco):
"""增加日志输出时间"""
def __init__(self, log: LogABC):
self.log = log
def error(self):
print("打印当前时间")
self.log.error()
class LevelLog(LogDeco):
"""增加日志输出级别"""
def __init__(self, log: LogABC, level):
self.log = log
self.level = level
def error(self):
print("日志级别为:{}".format(self.level))
self.log.error()
上面我们实现了两个装饰器类,分别用于增加输出时间以及输出级别。
可以看出,装饰器类需要接收原始类,继承自抽象类实现同样的接口,并且在接口中实现原对象方法,并进行增加功能的实现。
装饰使用
# 未增强装饰,直接使用
log = Log()
log.error()
print("===== 装饰输出时间 =====")
time_log = TimeLog(log)
time_log.error()
print("=====多重装饰 =====")
level_log = LevelLog(time_log, level="info")
level_log.error()
# ============= 输出 ===============
log输出日志...
===== 装饰输出时间 =====
打印当前时间
log输出日志...
=====多重装饰 =====
日志级别为:info
打印当前时间
log输出日志...
python 中的装饰器
在 Python 中,有更加高效和优雅实现装饰器的方式。Python本身实现了自己装饰器相关的协议,具体的装饰器使用法可以参考:装饰器详解
代理模式与装饰器模式的区别
代理模式与装饰器模式在实现上非常的相似,甚至可以写出一样的代码结构,对于两者的区别,看到过一句话总结的很好:
Decorator关注为对象动态的添加功能, Proxy关注对象的信息隐藏及访问控制。
Decorator体现多态性, Proxy体现封装性。