一、什么是装饰模式
故事
来到大城市李力和女朋友分居两地,饱受相思之苦。现在终于工作稳定下来,女朋友想要趁假期来见一面李力。见面当天李力心情激动,决定好好捯饬自己一番......
下身休闲裤配休闲皮鞋,加银色针扣头的黑色腰带;上身紫红色针织毛衣,内套一件白色衬衫;头戴一副方形黑框眼镜。
我们用程序模拟剧情
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)不能采用生成子类的方法进行扩充时,类的定义不能用于生成子类。
以上为个人学习笔记:
《人人都懂设计模式》作者:罗伟富
网友评论