组合模式和装饰器模式 7/30 | Python 主题月
写在前面
本文正在参加「Python主题月」,详情查看活动链接
这个月是 Python 活动月,我决定尝试用 Python 来刷这 30 天的每日一题和随机一题。然后如果周末有精力,我想捣鼓捣鼓这个python-patterns
设计模式对我来说更多的是学习而不是我的个人经验总结,所以我很可能理解偏,如果有大佬见到了请及时指出,我之所以选择在掘金来写一些个人的东西是因为这里的文章质量更高,我不希望后来者看到了这些文章被误导。
组合模式
组合模式描述了一组对象,它们的处理方式与同一类型对象的单一实例相同。组合的目的是将对象“组合”成树状结构,以表示部分-整体的层次结构。实现组合模式让客户统一对待每个“对象间组合”。正是这种层次关系的形成,组合模式也被称作部分整体模式。
举个简单的例子,文件目录树中的文件夹和文件就是这样的树状结构。然后决策树的节点枝丫信息和决策信息也是这样的树状结构。
下面看看例子代码中部分和整体如何形成层次结构。
from abc import ABC, abstractmethod
from typing import List
class Graphic(ABC):
@abstractmethod
def render(self) -> None:
raise NotImplementedError("You should implement this!")
class CompositeGraphic(Graphic):
def __init__(self) -> None:
self.graphics: List[Graphic] = []
def render(self) -> None:
for graphic in self.graphics:
graphic.render()
def add(self, graphic: Graphic) -> None:
self.graphics.append(graphic)
def remove(self, graphic: Graphic) -> None:
self.graphics.remove(graphic)
class Ellipse(Graphic):
def __init__(self, name: str) -> None:
self.name = name
def render(self) -> None:
print(f"Ellipse: {self.name}")
def main():
"""
>>> ellipse1 = Ellipse("1")
>>> ellipse2 = Ellipse("2")
>>> ellipse3 = Ellipse("3")
>>> ellipse4 = Ellipse("4")
>>> graphic1 = CompositeGraphic()
>>> graphic2 = CompositeGraphic()
>>> graphic1.add(ellipse1)
>>> graphic1.add(ellipse2)
>>> graphic1.add(ellipse3)
>>> graphic2.add(ellipse4)
>>> graphic = CompositeGraphic()
>>> graphic.add(graphic1)
>>> graphic.add(graphic2)
>>> graphic.render()
Ellipse: 1
Ellipse: 2
Ellipse: 3
Ellipse: 4
"""
if __name__ == "__main__":
import doctest
doctest.testmod()
复制代码
这个例子实现了一个图形类,它可以是一个椭圆或几个图形的组合,每个图形都可以被打印出来。在图形编辑器中,形状可以是基本的也可以是复杂的。简单形状的例子是一条线段,复杂形状是一个矩形,它是由四个线段对象组成的。由于形状有许多共同的操作,如将形状渲染到屏幕上,而且形状遵循部分-整体的层次结构,因此可以使用复合模式来使程序统一处理所有形状。
装饰器模式
这个模式是 cv 程序员最常用的模式之一,而且不知不觉就用到了。装饰器模式用于在不改变对象的实现的情况下,动态地将一个新特性添加到对象中。(说难听点就是给自己的代码屎山上多浇一瓢屎,所以给代码好好写注释,认真写文档以及及时重构是很重要的)它与继承不同,因为新特性只被添加到那个特定的对象,而不是整个子类。
class TextTag:
"""Represents a base text tag"""
def __init__(self, text):
self._text = text
def render(self):
return self._text
class BoldWrapper(TextTag):
"""Wraps a tag in <b>"""
def __init__(self, wrapped):
self._wrapped = wrapped
def render(self):
return f"<b>{self._wrapped.render()}</b>"
class ItalicWrapper(TextTag):
"""Wraps a tag in <i>"""
def __init__(self, wrapped):
self._wrapped = wrapped
def render(self):
return f"<i>{self._wrapped.render()}</i>"
def main():
"""
>>> simple_hello = TextTag("hello, world!")
>>> special_hello = ItalicWrapper(BoldWrapper(simple_hello))
>>> print("before:", simple_hello.render())
before: hello, world!
>>> print("after:", special_hello.render())
after: <i><b>hello, world!</b></i>
"""
if __name__ == "__main__":
import doctest
doctest.testmod()
复制代码
这个例子展示了一种通过附加相应的标签(<b>
和 <i>
)来为文本添加格式化选项(黑体和斜体)的方法。另外,我们可以看到装饰器可以一个接一个地应用,因为原始文本被传递给黑体包装器,而黑体包装器又被传递给斜体包装器。
实际的例子也有,Grok 框架使用装饰器来为方法添加功能,如权限或对事件的订阅。
小结
组合模式描述了一组被视为单一实例的对象。装饰器模式在不影响对象类别的情况下为其添加行为。