装饰器模式(Decorator Pattern)

定义

“Attach additional responsibilities to an object dynamically keeping the same interface.Decorators provide a flexible alternative to subclassing for extending functionality.(动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。)”

Excerpt From: 秦小波 著. “设计模式之禅(第2版)(华章原创精品).”

UML 示例


@startuml
class  Component
class ConcreateComponent
class Decorator
class ConcreateDecorator

Component <|-- ConcreateComponent
Component <|-- Decorator
Decorator <-- ConcreateDecorator
@enduml

Component是基本组件,而ConreateComponent是具体的组件,但为了对具体的组件增加一些额外的配件,则需要用到ConreateDecorator。
例如:一把枪,就是基础组件,而对于消音器等就是他的配件。

代码实现

在精通python设计模式中,关于适配器模式的介绍,实现也是相当简单的,只要实现目标接口方法,

'''
本样例主要说明python装饰器模式的简单应用
首先明确:python的装饰器所处理的场景要大于装饰器模式,此处只做简单装饰器模式的样例
'''
from functools import wraps


def save_result(fn):
    know_result = dict()
    @wraps(fn)
    def decorator(*args):
        if args in know_result:
            return know_result[args]
        know_result[args] = fn(*args)
        return know_result[args]

    return decorator


@save_result
def fibonacci(n):
    '''
    执行斐波那切数列
    '''
    return n if n in (0, 1) else fibonacci(n-1) + fibonacci(n-2)


if __name__ == "__main__":
    print(fibonacci(8))
  • 上述例子中是如何使用的装饰器模式,在哪里体现了?

首先正常的fibonacci数列,每次执行都是从最高值一直算到最小值退出,为了减小运算时间成本,故引入一段代码,存储已经计算过的fibonacci值,判断如果值已经存在则返回存在的值。
而@符号这个语法糖就是python的装饰器,用来让save_result函数修饰fibonacci函数

知识点

  1. 重点理解闭包是什么
  • 闭包定义:闭包指延伸了作用域的函数,其中包含函数定义体中引用、但不是定义在定义体中的非全局变量

如何理解这句话?以上述例子为例,save_result中的decorator就是延伸了作用域的函数,known_result就是定义体中的引用,但不在定义体中定义的非全局变量。所以满足上述条件就称decorator是闭包函数。

  • 自由变量:不在闭包中定义但在闭包中引用的非全局变量,例如:know_result
  1. @语法糖
  2. 应用场景:
    数据校验
    事务处理(这里的事务类似于数据库事务,意味着要么所有步骤都成功完成,要么事务失败)
    缓存
    日志
    监控
    调试
    业务规则
    压缩
    加密

扩展阅读之python装饰的几种类型

我理解的装饰器类型包括:装饰函数的装饰器,带参数的装饰器,装饰类的装饰器,装饰类的带参数的装饰器,类形式的装饰器、类形式的带参数的装饰器

  1. 装饰函数的装饰器:最简单的一种,上述装饰器模式使用的就是此种类型。
  2. 带参数的装饰器:如果对闭包理解够深刻,应该不难发现,闭包函数通过访问外部函数的变量从而进行一些操作。那么带参数的装饰器无非就是再增加一层嵌套,而这层嵌套需要增加传参。
def logger(*args, **kwargs):
    if 'level' in kwargs:
        level = kwargs['level']

    def decorator_func(fn):
        @wraps(fn)
        def decorator(*args, **kwargs):
            logging.log(level, 'excute')
            return fn(*args, **kwargs)
        return decorator
    return decorator_func


@logger(level=logging.CRITICAL)
def add(a, b):
    return a+b
  1. 类装饰器

类装饰器顾名思义就是用来装饰类的,所以说类装饰器是通过@decorator装饰class的。


def single(cls):
    instance = None
    @wraps(cls)
    def decorator(*args, **kwargs):
        nonlocal instance
        if instance is None:
            instance = cls(*args, **kwargs)
            return instance
        else:
            return instance
    return decorator


total_count = 0


def count(cls):
    @wraps(cls)
    def decorator(*args, **kwargs):
        global total_count
        total_count += 1
        return cls(*args, **kwargs)

    return decorator


@count
@single
class Test:
    pass


if __name__ == "__main__":
    # print(fibonacci(8))

    t1 = Test()  # <__main__.Test object at 0x10e0e1400>
    t2 = Test()  # <__main__.Test object at 0x10e0e1400>
    t3 = Test()  # <__main__.Test object at 0x10e0e1400>
    print('test create count {}'.format(total_count)) #test create count 3
    print(t1)
    print(t2)
    print(t3)
    if t1 is t2 is t3:
        print('t1==t2==t3') # t1==t2==t3
  1. 带参数的装饰类的装饰器
# 带参数装饰器
def issingle(flag=True):
    def single(cls):
        instance = None
        @wraps(cls)
        def decorator(*args, **kwargs):
            nonlocal instance
            if flag is False:
                instance = cls(*args, **kwargs)
            else:
                if instance is None:
                    instance = cls(*args, **kwargs)
            return instance
        return decorator
    return single


@issingle(True)
class T1:
    pass


@issingle(False)
class T2:
    pass

if __name__ == "__main__":
    t4 = T1()
    t5 = T1()
    print(t4)   # <__main__.T1 object at 0x105482be0>
    print(t5)   # <__main__.T1 object at 0x105482be0>

    t6 = T2()   # <__main__.T2 object at 0x105482c18>
    t7 = T2()   # <__main__.T2 object at 0x105482c18>

    print(t6)
    print(t7)
    if t4 is t5:
        print('t4 == t5') ## t4 == t5

    if t6 is t7:
        print('t6 == t7')
  1. 类形式的装饰器

类形式的装饰器,重点要理解的就是函数也是类,而类也可以是函数,只要类实现了__call__方法,便是可调用的类。

class Single:
    def __init__(self):
        super().__init__()

    def __call__(self, cls):
        instance = None
        @wraps(cls)
        def decorator(*args, **kwargs):
            nonlocal instance
            if instance is None:
                instance = cls(*args, **kwargs)
                return instance
            else:
                return instance
        return decorator


@Single() # 创建一个类对象执行__call__方法
class T3:
    pass

t8 = T3()
t9 = T3()
if t8 == t9:
    print('t8 == t9')
  1. 类形式的带参数的装饰器

类形式的装饰器的带参数形式就没有想象的那么难啦,通过__init__函数传参,简洁

class Single:
    def __init__(self, flag):
        self.flag = flag
        super().__init__()

    def __call__(self, cls):
        instance = None
        @wraps(cls)
        def decorator(*args, **kwargs):
            nonlocal instance
            if self.flag is True:
                print('hello world!')
            if instance is None:
                instance = cls(*args, **kwargs)
                return instance
            else:
                return instance
        return decorator


@Single(True)
class T3:
    pass

t8 = T3()
t9 = T3()
if t8 == t9:
    print('t8 == t9')

# 输出两边helloworld!

下集预告 外观模式


neilliu
59 声望9 粉丝

coder is coding code snippet,coder change the world!