Skip to content

设计模式之组合模式

一个人几乎可以在任何他怀有无限热忱的事情上成功。

Author:李东阳

1、什么是组合模式

内容

将对象 组合成 树形结构 以表示“部分-整体”的层次结构。组合模式使得用户对 单个对象 和 组合对象 的使用 具有一致性。

角色

  • 抽象组件(Component)

  • 叶子组件(Leaf)

  • 复合组件(Composite)

  • 客户端(Client)

实际应用场景

  • 组织结构:组合模式可以用于表示公司的组织结构,将公司分成多个部门,每个部门又包含多个小组,每个小组又包含多个员工。

  • 文件系统:组合模式可以用于表示文件系统的层次结构,将文件系统分成多个文件夹,每个文件夹又包含多个子文件夹和文件。

  • 图形用户界面:组合模式可以用于表示图形用户界面中的控件层次结构,将界面分成多个面板,每个面板又包含多个子控件。

  • 产品配置:组合模式可以用于表示产品配置的层次结构,将产品分成多个组件,每个组件又包含多个子组件。

  • 电子商务:组合模式可以用于表示电子商务网站中的商品分类,将商品分成多个分类,每个分类又包含多个子分类和商品。

2、组合模式的实现

from abc import ABCMeta, abstractmethod


# 抽象组件基类 接口
class Graphic(metaclass=ABCMeta):
    @abstractmethod
    def draw(self):
        pass


# 叶子节点:不可再分,最底层
class Point(Graphic):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f"点({self.x},{self.y})"

    def draw(self):
        print(str(self))


# 复合节点:两个点p1、p2是线的子节点
class Line(Graphic):
    def __init__(self, p1, p2):
        self.p1 = p1
        self.p2 = p2

    def __str__(self):
        return f"线段({self.p1},{self.p2})"

    def draw(self):
        print(str(self))


# 顶层复合节点:所有传入的iterable对象都是Picture对象子节点
class Picture(Graphic):
    def __init__(self, iterable):
        # 初始化对象时遍历iterable向Picture添加对象
        self.children = []
        for elem in iterable:
            self.add(elem)

    def add(self, graphic):
        self.children.append(graphic)

    def draw(self):
        # 遍历运行每个对象的draw方法
        for elem in self.children:
            elem.draw()


# 客户端
if __name__ == "__main__":
    # 组合pic1的树形对象组合
    p1 = Point(0, 0)
    l1 = Line(Point(1, 1), Point(2, 2))
    l2 = Line(Point(3, 3), Point(4, 4))
    pic1 = Picture([p1, l1, l2])

    # 组合pic2的树形对象组合
    p2 = Point(5, 5)
    l3 = Line(Point(6, 6), Point(7, 7))
    pic2 = Picture([p2, l3])

    # 组合pic树形对象组合
    pic = Picture([pic1, pic2])
    # 递归的调用各个子对象的draw方法
    pic.draw()
##########################################
# 输出:
0,0)
线段1,1),2,2))
线段3,3),4,4))
5,5)
线段6,6),7,7))

# 可以看出,虽然我们是写了三个类,但是由于这三个类都实现了同接口,所以用户在使用这些类时会体会到他们的一致性

3、总结

优点

  • 简化代码结构:通过将对象组合成树状结构,可以避免使用大量的if/else或switch语句来处理不同的对象类型,从而简化了代码结构。

  • 增强代码可扩展性和可维护性:当需要添加新的节点类型时,只需要创建新的类实现相同的接口即可,而不需要修改现有的代码。这样可以增强代码的可扩展性和可维护性。

  • 提高代码复用性:由于所有节点实现相同的接口,因此可以在不同的组合中重复使用相同的节点。

  • 可以透明地处理组合对象和单个对象:客户端代码可以像处理单个对象一样处理组合对象和单个对象,无需关心对象的具体类型,从而实现了透明性。

  • 可以提高代码的灵活性:由于组合模式中的对象都实现相同的接口,因此可以灵活地组合不同的对象实现不同的功能。

缺点

  • 可能会导致设计过度复杂:当对象层次结构较为复杂时,使用组合模式可能会导致设计过度复杂,从而增加代码的维护成本。

  • 可能会影响性能:由于组合模式需要遍历整个对象树才能执行某些操作,因此可能会影响程序的性能。

  • 可能会限制对象的类型:由于组合模式要求所有节点实现相同的接口,因此可能会限制对象的类型。如果某些节点需要特定的方法或属性,则需要在接口中定义这些方法或属性,从而增加了接口的复杂度。