Python实现软件设计模式9:组合模式 Composite Pattern

动机

如何将容器和叶子进行递归组合,使得用户在使用时无须对它们进行区分,可以一致地对待容器和叶子?

典型案例

如:文件系统,在树形目录结构中,包含文件和文件夹两类不同的元素;在文件夹中可以继续包含文件或子文件夹,在文件中不能再包含子文件或者子文件夹。

概念

  • 组合多个对象形成树形结构,以表示“部分-整体”的结构层次。组合模型对单个对象(叶子对象)和组合对象(容器对象)的使用具有一致性。
  • 是一种对象结构型设计模式。
  • 将对象组织到树形结构中,可以用来描述整体与部分的关系。

主要角色

  • Component 抽象构件(父类),声明业务方法operation()
  • Leaf 叶子构件,继承抽象构件,实现业务方法operation()
  • Composite 容器构件,继承抽象构件,可以存储容器内的所有成员,实现业务方法operation(),这一步通常是递归实现容器内元素的业务方法

注意:Composite对象内含一个或多个Component对象(Component对象集合),即Composite对象可以包含任何类型为Component的子类对象(Leaf或Composite)

重要分类

透明组合模式

  1. 抽象构件Component中声明了所有用于管理成员对象的方法
  2. 在客户端眼里,Leaf和Composite提供的业务方法是一致的,客户端可以一致地对待所有的对象
  3. 缺点是不够安全,因为叶子对象和容器对象本质上是有区别的

    上图中的Leaf对象中不该包含抽象接口父类中的add、remove方法。

安全组合模式

  1. 抽象构件Component中不会声明任何用于管理成员对象的方法,而是在容器对象Composite中声明并实现这些方法
  2. 对于叶子对象Leaf,客户端不可能调用到这些方法
  3. 缺点是不够透明,客户端不能完全针对抽象编程,必须有区别的对待叶子构件和容器构件

区分

  • 透明组合模式中,客户端对叶子节点调用add()或remove()方法时会出现运行异常,比较不安全
  • 安全模式就不会出现这样的问题,因为在安全模式中add()或remove()这一类对集合操作的方法都只会出现在容器对象中,叶子对象没有这一类的方法

Python实现

案例:嵌套水果盘


from abc import ABC,abstractclassmethod

class MyElement(ABC):  
    @abstractclassmethod
    def eat(self):
        pass

class Apple(MyElement):
    def eat(self):
        print("吃苹果!")

class Peer(MyElement):
    def eat(self):
        print("吃梨子!")

class Banana(MyElement):
    def eat(self):
        print("吃香蕉!")

class Orange(MyElement):
    def eat(self):
        print("吃橘子!")

class Melon(MyElement):
    def eat(self):
        print("吃甜瓜!")

class Pineapple(MyElement):
    def eat(self):
        print("吃菠萝!")

class Strawberry(MyElement):
    def eat(self):
        print("吃草莓!")

class Grape(MyElement):
    def eat(self):
        print("吃葡萄!")

class Grapefruit(MyElement):
    def eat(self):
        print("吃西柚!")

class Pitaya(MyElement):
    def eat(self):
        print("吃火龙果!")

class Kiwifruit(MyElement):
    def eat(self):
        print("吃猕猴桃!")

class Cherry(MyElement):
    def eat(self):
        print("吃樱桃!")

class Litchi(MyElement):
    def eat(self):
        print("吃荔枝!")

# 盘子,容器构件,包含一个集合
class Plate(MyElement):
    def __init__(self):
        self.__list = []
    def add(self, c : MyElement):
        self.__list.append(c)
    
    def remove(self, c : MyElement):
        self.__list.remove(c)
    
    def eat(self):
        for c in self.__list:
            c.eat()

class Client:
    def run(self):
        f1 = Apple()
        f2 = Peer()
        p1 = Plate()
        p1.add(f1)
        p1.add(f2)

        f3 = Orange()
        f4 = Banana()
        p2 = Plate()
        p2.add(f3)
        p2.add(f4)

        f5 = Litchi()
        p3 = Plate()
        p3.add(p1)
        p3.add(p2)
        p3.add(f5)

        p3.eat()


if __name__ == '__main__':
    x = Client()
    x.run()

总结

优点

  • 可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,让客户端忽略了层次的差异,方便对整个层次结构进行控制
  • 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码
  • 增加新的容器构件和叶子构件都很方便,符合开闭原则
  • 为树形结构的面向对象实现提供了一种灵活的解决方案

缺点

  • 在增加新构件时很难对容器中的构件类型进行限制

使用场景

  • 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待他们
  • 采用面向对象的编程语言来处理树形结构
  • 分离叶子和容器对象,类型不固定,抽象接口固定、叶子的类型可轻松扩展
posted @ 2024-02-07 21:28  爱吃砂糖橘的白龙  阅读(10)  评论(0编辑  收藏  举报