组合模式(Composite Design Pattern)及代码实现

模式定义:

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

 

何时使用此模式:

  • 如果你需要实现树状对象结构, 可以使用组合模式。组合模式为你提供了两种共享公共接口的基本元素类型: 简单叶节点和复杂容器。 容器中可以包含叶节点和其他容器。 这使得你可以构建树状嵌套递归对象结构
  • 如果 在处理复杂对象时 可以使用递归的方式 遍历每个子对象,则可以考虑使用此模式;
  • 希望用户忽略组合对象与单个对象的不同,统一的使用组合结构中的所有对象时

 

生活或代码中的例子:

  • 文件系统 目录层级结构,文件夹里可以有不同文件,文件夹里可以有子文件夹;
  • 公司组织架构;
  • 树的结构;

 

该模式关键的角色:

  • 组件角色(Component): 描述了树中简单项目和复杂项目所共有的操作。
  • 容器角色(Container) 或 组合角色(Composite): 是包含叶节点或其他容器等子项目的单位。 容器不知道其子项目所属的具体类, 它只通过通用的组件接口与其子项目交互。容器接收到请求后会将工作分配给自己的子项目, 处理中间结果, 然后将最终结果返回给客户端。 包含 添加 和 删除 子元素的功能; 存储和管理子元素的功能;

  • 叶节点角色(Leaf):是树的基本结构, 它不包含子项目。一般情况下, 叶节点最终会完成大部分的实际工作, 因为它们无法将工作指派给其他部分。

 

实现方式或步骤:

  1. 确保应用的核心模型能够以树状结构表示。 尝试将其分解为简单元素和容器。 记住, 容器必须能够同时包含简单元素和其他容器。

  2. 声明组件接口及其一系列方法, 这些方法对简单和复杂元素都有意义。

  3. 创建一个叶节点类表示简单元素。 程序中可以有多个不同的叶节点类。

  4. 创建一个容器类表示复杂元素。 在该类中, 创建一个数组成员变量来存储对于其子元素的引用。 该数组必须能够同时保存叶节点和容器, 因此请确保将其声明为组合接口类型。

    实现组件接口方法时, 记住容器应该将大部分工作交给其子元素来完成。

  5. 最后, 在容器中定义添加和删除子元素的方法。

 

该模式的主要优缺点如下:

优点:

  • 客户端代码 访问 复杂的组合对象 和 访问单个对象 没有任何区别; 不用关注 处理细节,对客户端进行一定的封装;
  • 开闭原则; 无需更改现有代码, 你就可以在应用中添加新元素, 使其成为对象树的一部分。

缺点:

  • 层次结构过多时,容易引起 客户端构建 层次混乱;

 

和 其他模式 的 比较:

  • 你可以使用 享元模式 实现 组合树 的共享叶节点以节省内存。
  • 大量使用 组合模式 通常可配合 原型模式 一起使用。 你可以通过 原型模式 来复制 容器对象或 叶子对象 然后进行参数修改, 而非从零开始重新构造。

 

示例部分

如下示例中 族谱树形图如下:

代码如下:

# -*- coding: utf-8 -*-
"""
(C) Rgc
All rights reserved
create time '2020/11/12 18:58'

Usage:
组合模式的示例代码
实现 族谱的树形结构,并对其进行各种处理
如 广度遍历,深度遍历 等等
"""
from copy import copy
from queue import Queue


class Forefathers(object):
    """
    组件 或 抽象构件 角色 主要 声明 公共方法

    """
    queue = Queue()

    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

    def __str__(self):
        """"""
        return self.name

    def add(self, someone):
        pass

    def remove(self, someone):
        pass

    def introduce(self):
        """
        自我介绍
        :return:
        """
        print(f'My information is {self.name, self.age, self.gender}')

    def show(self, deep):
        """
        显示 层级关系
        :param deep:
        :return:
        """
        print('-' * deep, self.name)

    def count_parent(self, count):
        pass


class Parents(Forefathers):
    """
    容器 或 组合 角色
    父母 类 可以有后代
    """

    def __init__(self, name, age, gender):
        """

        :param name:
        :param age:
        :param gender:
        """
        super(Parents, self).__init__(name, age, gender)
        self.children = []

    def add(self, *someone):
        """
        新增 子孙
        :param someone:
        :return:
        """
        self.children += someone

    def remove(self, someone):
        """
        移出 祖籍
        :param someone:
        :return:
        """
        self.children.remove(someone)

    def introduce(self):
        """
        自我介绍
        :return:
        """
        super(Parents, self).introduce()
        print('I am parents')

        for item in self.children:
            item.introduce()

    def show(self, deep):
        """

        :param deep:
        :return:
        """
        super(Parents, self).show(deep)
        for item in self.children:
            item.show(deep + 2)

    def count_parent(self, count):
        """

        :param count:
        :return:
        """
        print(f'祖宗个数为:{count},{self.name}')
        count += 1
        for item in self.children:
            item.count_parent(count)
        return count

    def dfs_lf(self):
        """
        深度优先遍历 用迭代实现
        思路:比较简单,用的 栈的后入先出特点(用list类型代替);
        前序优先
        :return:
        """
        print(self.name)
        for item in self.children:
            item.dfs_lf()

    def dfs_rf(self):
        """
        深度优先遍历 用迭代实现
        后续优先
        :return:
        """
        print(self.name)
        children = copy(self.children)
        children.reverse()
        for item in children:
            item.dfs_rf()

    def bfs(self):
        """
        广度优先遍历
        思路:使用队列的先入先出特点,在每取出一个数据后,都要把这个数据的 所有子节点放入队列中; 从而保证 横向遍历(广度优先遍历)
        :return:
        """
        # 先给出自己
        print(self.name)
        # 把自己的子节点放到 队列里
        for item in self.children:
            self.queue.put(item)
        # 取出队列里的第一个数据(先入先出规则)
        node = self.queue.get()
        # 对第一个数据执行迭代
        node.bfs()


class Child(Forefathers):
    """
    叶节点 角色
    孩童 类 没有后代
    """

    def __init__(self, name, age, gender):
        """

        :param name:
        :param age:
        :param gender:
        """
        super(Child, self).__init__(name, age, gender)

    def introduce(self):
        """

        :return:
        """
        super(Child, self).introduce()
        print('I am child')

    def count_parent(self, count):
        """

        :param count:
        :return:
        """
        print(f'祖宗个数为:{count},{self.name}')
        count += 1
        return count

    def dfs_rf(self):
        """
        深度优先遍历
        后续优先
        :return:
        """
        print(self.name)

    def dfs_lf(self):
        """
        深度优先遍历
        前序优先
        :return:
        """
        print(self.name)

    def bfs(self):
        """
        广度优先遍历
        :return:
        """
        # 先给出自己
        print(self.name)
        # 因为是最底部的节点,所以 只要队列有数据,则直接取数据,继续迭代即可
        if not self.queue.empty():
            node = self.queue.get()
            node.bfs()
        else:
            print('广度遍历结束')


if __name__ == '__main__':
    """
    """
    root = Parents('第一代祖宗', 1800, 'man')

    sec_man = Parents('第二代男祖宗', 1840, 'man')
    sec_man1 = Parents('第二代男1祖宗', 1840, 'man')
    sec_woman = Parents('第二代女祖宗', 1840, 'woman')
    root.add(sec_man, sec_woman, sec_man1)

    last_one = Child('最早灭绝一代', 1860, 'man')
    sec_man.add(last_one)

    third_woman = Parents('第三代女祖宗', 1860, 'woman')
    sec_woman.add(third_woman)
    third_man = Parents('第三代男祖宗', 1860, 'man')
    sec_woman.add(third_man)

    four_man = Parents('第四代男祖宗', 1890, 'man')
    third_woman.add(four_man)

    last_one = Child('最后一代', 1890, 'man')
    third_woman.add(last_one)

    # 采用 前序遍历 方法
    print('显示 关系层级')
    root.show(2)
    print('*' * 30)

    print('计算 祖宗个数(树深度问题)')
    # 采用 前序遍历(根-左-右) 方法
    root.count_parent(0)
    print('*' * 30)

    print('前序遍历')
    root.dfs_lf()
    print('*' * 30)

    print('后续遍历')
    root.dfs_rf()
    print('*' * 30)

    print('广度遍历')
    root.bfs()

运行结果:

 

总结: 

要想使用此模式,要先 考虑 代码或需求 是否满足 通过 递归算法实现对应功能,并且 容器角色 和 叶子角色 的 功能差异不大,要保证 客户端在调用时,无论是复制的 树结构对象 或者简单的 容器或 叶子对象,都能使用相同的接口实现(一致性);

 

相关链接:

https://refactoringguru.cn/design-patterns/composite

http://c.biancheng.net/view/1373.html

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值