组合模式组合多个对象形成树形结构以表示“整体-部分”的结构层次;使得用户对单个对象和组合对象的使用具有一致性;
容器角色(Container) 或 组合角色(Composite): 是包含叶节点或其他容器等子项目的单位。 容器不知道其子项目所属的具体类, 它只通过通用的组件接口与其子项目交互。容器接收到请求后会将工作分配给自己的子项目, 处理中间结果, 然后将最终结果返回给客户端。 包含 添加 和 删除 子元素的功能; 存储和管理子元素的功能;
叶节点角色(Leaf):是树的基本结构, 它不包含子项目。一般情况下, 叶节点最终会完成大部分的实际工作, 因为它们无法将工作指派给其他部分。
确保应用的核心模型能够以树状结构表示。 尝试将其分解为简单元素和容器。 记住, 容器必须能够同时包含简单元素和其他容器。
声明组件接口及其一系列方法, 这些方法对简单和复杂元素都有意义。
创建一个叶节点类表示简单元素。 程序中可以有多个不同的叶节点类。
创建一个容器类表示复杂元素。 在该类中, 创建一个数组成员变量来存储对于其子元素的引用。 该数组必须能够同时保存叶节点和容器, 因此请确保将其声明为组合接口类型。
实现组件接口方法时, 记住容器应该将大部分工作交给其子元素来完成。
最后, 在容器中定义添加和删除子元素的方法。
优点:
缺点:
如下示例中 族谱树形图如下:
代码如下:
# -*- 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