美文网首页
第四章:装饰模式

第四章:装饰模式

作者: Benedict清水 | 来源:发表于2020-08-18 09:02 被阅读0次

    一、什么是装饰模式

    故事
    来到大城市李力和女朋友分居两地,饱受相思之苦。现在终于工作稳定下来,女朋友想要趁假期来见一面李力。见面当天李力心情激动,决定好好捯饬自己一番......
    下身休闲裤配休闲皮鞋,加银色针扣头的黑色腰带;上身紫红色针织毛衣,内套一件白色衬衫;头戴一副方形黑框眼镜。
    我们用程序模拟剧情

    from abc import ABCMeta, abstractmethod
    
    
    class Person(metaclass=ABCMeta):
        """人"""
    
        def __init__(self, name):
            self._name = name
    
        @abstractmethod
        def wear(self):
            print("着装:")
    
    
    class Engineer(Person):
        """工程师"""
    
        def __init__(self, name, skill):
            super().__init__(name)
            self.__skill = skill
    
        def getSkill(self):
            return self.__skill
    
        def wear(self):
            print("我是 " + self.getSkill() + "工程师 " + self._name, end=", ")
            super().wear()
    
    
    class Teacher(Person):
        """教师"""
    
        def __init__(self, name, title):
            super().__init__(name)
            self.__title = title
    
        def getTitle(self):
            return self.__title
    
        def wear(self):
            print("我是 " + self._name + self.getTitle(), end=", ")
            super().wear()
    
    
    class ClothingDecorator:
        """服装装饰器的基类"""
    
        def __init__(self, person):
            self._decorated = person
    
        def wear(self):
            self._decorated.wear()
            self.decorate()
    
        @abstractmethod
        def decorate(self):
            pass
    
    
    class CasualPantDecorator(ClothingDecorator):
        """休闲裤装饰器"""
    
        def __init__(self, person):
            super().__init__(person)
    
        def decorate(self):
            print("一条卡其色休闲裤")
    
    
    class BeltDecorator(ClothingDecorator):
        """腰带装饰器"""
    
        def __init__(self, person):
            super().__init__(person)
    
        def decorate(self):
            print("一条银色针扣头的黑色腰带")
    
    
    class LeatherShoesDecorator(ClothingDecorator):
        """皮鞋装饰器"""
    
        def __init__(self, person):
            super().__init__(person)
    
        def decorate(self):
            print("一双深色休闲皮鞋")
    
    
    class KnittedSweaterDecorator(ClothingDecorator):
        """针织毛衣装饰器"""
    
        def __init__(self, person):
            super().__init__(person)
    
        def decorate(self):
            print("一件紫红色针织毛衣")
    
    
    class WhiteShirtDecorator(ClothingDecorator):
        """白色衬衣装饰器"""
    
        def __init__(self, person):
            super().__init__(person)
    
        def decorate(self):
            print("一件白色衬衣")
    
    
    class GlassesDecorator(ClothingDecorator):
        """眼镜装饰器"""
    
        def __init__(self, person):
            super().__init__(person)
    
        def decorate(self):
            print("一副方形黑框眼镜")
    
    
    def testDecorator():
        tony = Engineer("Tony", "python")
        pant = CasualPantDecorator(tony)
        belt = BeltDecorator(pant)
        shoes = LeatherShoesDecorator(belt)
        shirt = WhiteShirtDecorator(shoes)
        sweater = KnittedSweaterDecorator(shirt)
        glasses = GlassesDecorator(sweater)
        glasses.wear()
        print()
        decorateTeacher = GlassesDecorator(WhiteShirtDecorator(LeatherShoesDecorator(Teacher("wells", "教授"))))
        decorateTeacher.wear()
    
    
    if __name__ == "__main__":
        testDecorator()
    

    % python decorator.py

    我是 python工程师 Tony, 着装:
    一条卡其色休闲裤
    一条银色针扣头的黑色腰带
    一双深色休闲皮鞋
    一件白色衬衣
    一件紫红色针织毛衣
    一副方形黑框眼镜
    
    我是 wells教授, 着装:
    一双深色休闲皮鞋
    一件白色衬衣
    一副方形黑框眼镜
    

    上面的 decorateTeacher = GlassesDecorator(WhiteShirtDecorator(LeatherShoesDecorator(Teacher("wells", "教授"))))是将多个对象的创建过程合在一起,是一种优雅的写法。

    二、装饰模式的定义

    动态的给一个对象增加一些额外的职责,就扩展对象功能来说,装饰模式比生成子类的方式更为灵活。

    2.1 装饰模式设计思想

    在程序中,我们希望动态的给一个类增加额外的功能,而不改动原有的代码,就可以用装饰模式进行拓展。

    2.2 python中的装饰器

    在Python中一切都是对象:一个实例是对象,一个函数也是一个对象,甚至类本身也是一个对象。在python中,可以将一个函数作为参数传递给另一个函数,也可以将一个类作为一个参数传递给一个函数。

    2.2.1 python 中函数的特殊功能

    在python中,函数可以作为一个参数传递给另外一个函数,也可以在函数中返回一个函数,还可以在函数内部再定义函数。

    def func(num):
        """定义内部函数并返回"""
    
        def firstInnerFunc():
            return "这是第一个内部函数"
    
        def secondInnerFunc():
            return "这是第二个内部函数"
    
        if num == 1:
            return firstInnerFunc
        else:
            return secondInnerFunc
    
    
    print(func(1))
    print(func(2))
    print(func(1)())
    print(func(2)())
    
    firstFunc = func(1)
    secondFunc = func(2)
    print(firstFunc)
    print(secondFunc)
    print(firstFunc())
    print(secondFunc())
    

    输出结果:

    <function func.<locals>.firstInnerFunc at 0x10b92d310>
    <function func.<locals>.secondInnerFunc at 0x10b92d430>
    这是第一个内部函数
    这是第二个内部函数
    <function func.<locals>.firstInnerFunc at 0x10b92d310>
    <function func.<locals>.secondInnerFunc at 0x10b92d4c0>
    这是第一个内部函数
    这是第二个内部函数
    

    上面两种调用方式是一样的结果。

    2.2.2 装饰器修饰函数

    装饰器的作用,包装一个函数,并改变(拓展)它的行为。
    例:我们希望每一个函数在被调用之前和调用之后,记录一条日志。

    import logging
    logging.basicConfig(level=logging.INFO)
    
    
    def loggingDecorator(func):
        """记录日志的装饰器"""
    
        def wrapperLogging(*args, **kwargs):
            logging.info("开始执行%s()..." % func.__name__)
            func(*args, **kwargs)
            logging.info("%s()执行完成!" % func.__name__)
    
        return wrapperLogging
    
    
    def showInfo(*args, **kwargs):
        print("这是一个测试函数, 参数:", args, kwargs)
    
    
    showInfo = loggingDecorator(showInfo)
    showInfo('arg1', 'arg2', kwarg1=1, kwarg2=2)
    

    输出结果:

    INFO:root:开始执行showInfo()...
    这是一个测试函数, 参数: ('arg1', 'arg2') {'kwarg1': 1, 'kwarg2': 2}
    INFO:root:showInfo()执行完成!
    

    我们在logginDecorator中定义了一个内部函数wrapperLogging,用于在传入的函数中执行前后记录日志,一般称这个函数为包装函数,并在最后返回这个函数。我们称logginDecorator为装饰器。Python也提供了更优雅的语法糖。把showInfo = loggingDecorator(showInfo)换成@loggingDecorator

    import logging
    
    logging.basicConfig(level=logging.INFO)
    
    
    def loggingDecorator(func):
        """记录日志的装饰器"""
    
        def wrapperLogging(*args, **kwargs):
            logging.info("开始执行%s()..." % func.__name__)
            func(*args, **kwargs)
            logging.info("%s()执行完成!" % func.__name__)
    
        return wrapperLogging
    
    
    @loggingDecorator
    def showInfo(*args, **kwargs):
        print("这是一个测试函数, 参数:", args, kwargs)
    
    
    showInfo('arg1', 'arg2', kwarg1=1, kwarg2=2)
    

    输出结果:

    INFO:root:开始执行showInfo()...
    这是一个测试函数, 参数: ('arg1', 'arg2') {'kwarg1': 1, 'kwarg2': 2}
    INFO:root:showInfo()执行完成!
    

    升级版本:我们希望我们的日志记录等级不仅是info,也可以使debug。

    def loggingDecorator(prefix):
        def loggingDecoratorfunc(func):
            """记录日志的装饰器"""
            def wrapperLogging(*args, **kwargs):
                if prefix == 'info':
                    logging.info("%s开始执行%s()..." %(prefix, func.__name__))
                    func(*args, **kwargs)
                    logging.info("%s执行结束%s()..." %(prefix, func.__name__))
                else:
                    logging.debug("%s开始执行%s()..." % (prefix, func.__name__))
                    func(*args, **kwargs)
                    logging.debug("%s执行结束%s()..." % (prefix, func.__name__))
            return wrapperLogging
        return loggingDecoratorfunc
    
    
    @loggingDecorator('info')
    def showInfo(*args, **kwargs):
        print("这是一个测试函数, 参数:", args, kwargs)
    
    
    showInfo('arg1', 'arg2', kwarg1=1, kwarg2=2)
    

    把上面的定义翻译成高阶函数的调用,就是:

    showInfo = loggingDecorator('DEBUG')(showInfo)
    showInfo('arg1', 'arg2', kwarg1=1, kwarg2=2)
    

    上面的语句看上去还是比较绕,再展开一下:

    log_decorator = loggingDecorator('info')
    showInfo = log_decorator(showInfo)
    showInfo('arg1', 'arg2', kwarg1=1, kwarg2=2)
    

    2.2.3装饰器修饰类
    装饰器可以是一个函数,也可以是一个类(必须要实现__call__)方法,使其是callable的)。同时装饰器不仅可以修改一个函数,还可以修饰一个类,示例如下:

    class ClassDecorator:
        """类装饰器,记录一个类被实例化的次数"""
    
        def __init__(self, func):
            self.__numOfCall = 0
            self.__func = func
    
        def __call__(self, *args, **kwargs):
            self.__numOfCall += 1
            obj = self.__func(*args, *kwargs)
            print("创建%s的第%d个实例:%s" % (self.__func.__name__, self.__numOfCall, id(obj)))
            return obj
    
    
    @ClassDecorator
    class MyClass:
        def __init__(self, name):
            self.__name = name
    
        def getName(self):
            return self.__name
    
    
    if __name__ == "__main__":
        tony = MyClass("Tony")
        karry = MyClass("Karry")
        print(id(tony))
        print(id(karry))
    

    输出结果:

    创建MyClass的第1个实例:4457689680
    创建MyClass的第2个实例:4457691120
    4457689680
    4457691120
    

    这里ClassDecorator是类装饰器,记录一个类被实例化的次数。其修饰一个类和修饰一个函数的用法是一样的,只需在定义类时@ClassDECORATOR即可。

    三、总结

    3.1装饰模式的优缺点

    优点
    (1) 使用装饰模式来实现扩展比使用继承更加灵活,它可以在不创造更多子类的情况下,使对象的功能加以扩展。
    (2) 可以动态地给一个对象附加更多的功能。
    (3) 可以用不同的装饰器进行多重装饰,装饰的顺序不同,可能产生不同的效果。
    (4) 装饰类和被装饰类可以独立发展,不会相互耦合;装饰模式相当于继承的一个替代模式。
    缺点
    与继承相比,用装饰的方式扩展功能容易出错,拍错也更困难。

    3.2 应用场景

    (1)有大量独立的扩展,为支持每一种组合将产生大量的子类,使的子类数目呈爆炸性增长。
    (2)需要动态地增加或撤销功能时
    (3)不能采用生成子类的方法进行扩充时,类的定义不能用于生成子类。

    以上为个人学习笔记:
    《人人都懂设计模式》作者:罗伟富

    相关文章

      网友评论

          本文标题:第四章:装饰模式

          本文链接:https://www.haomeiwen.com/subject/hwofdktx.html