在前文 设计模式 - 装饰模式 中比较全面地介绍了什么是装饰模式,介绍了设计模式 - 装饰模式优缺点、适用范围。 现在简单实现一下,加深学习印象。

我们可以这样理解:

装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

装饰模式特点

  1. 装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。
  2. 装饰对象包含一个真实对象的引用(reference)
  3. 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。
  4. 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。

真实对象区别于抽象对象(Component)。抽象对象不应该被具体实现;同时修饰类引用和继承 Component 类,具体扩展类重写父类方法。

设计原则

  1. 应当多组合,少继承

利用继承设计子类的行为,是在编译时静态决定的,而且所有的子类都会继承到相同的行为。然而,如果能够利用组合的做法扩展对象的行为,就可以在运行时动态地进行扩展。

  1. 类的设计应该对扩展开放,对修改关闭

样例实现

在这个样例中,主要体会对子类的组合实现,以及深入体会如何动态加载子类

from functools import wraps


def login_check(func):
    """
    Decorator
    check whether user is logged in
    :param func
    :return wrapper
    """
    @wraps(func)
    def wrapper(*args, **kwargs):
        if args[0].status != "login":
            print("User hasn't logged in yet!")
        else:
            return func(*args, **kwargs)
    return wrapper


class Visitor(object):
    def __init__(self, status) -> None:
        self.status = status

    # decorate this method
    @login_check
    def access_allow(self):
        print("User now has login. Access is allowed")


if __name__ == "__main__":
    print('>>> set status login,  is result')
    visitor = Visitor('login')
    visitor.access_allow()
    print('>>> set status not login, below is result')
    visitor = Visitor('visitor')
    visitor.access_allow()

Console Output : > set status login, is result [LOGIN RESULT] User now has login. Access is allowed >set status not login, below is result [LOGIN RESULT] User hasn’t logged in yet!

接下来新增一个函数,命名为logger,目的是显示日志,当他装饰的函数被调用时,就会记录日志,内容包括函数名以及函数参数。

from functools import wraps


def login_check(func):
    ...


def logger(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"{func.__name__} was called. Parmas: {str(args)}, {str(kwargs)}")
        return func(*args, **kwargs)
    return wrapper


class Visitor(object):
    def __init__(self, status) -> None:
        self.status = status

    # decorate this method
    @logger
    @login_check
    def access_allow(self):
        print("[LOGIN RESULT] User now has login. Access is allowed")


if __name__ == "__main__":
    print('> set status login,  is result')
    visitor = Visitor('login')
    visitor.access_allow()
    print('> set status not login, below is result')
    visitor = Visitor('visitor')
    visitor.access_allow()

Console Output : > set status login, is result access_allow was called. Parmas: (<main.Visitor object at 0x0000019BCE620F70>,), {} [LOGIN RESULT] User now has login. Access is allowed >set status not login, below is result access_allow was called. Parmas: (<main.Visitor object at 0x0000019BCE620730>,), {} [LOGIN RESULT] User hasn’t logged in yet!