组合模式

50 阅读4分钟

1. 组合模式简介

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

2. 用一个常见的例子来理解组合模式:文件系统

让我们以文件系统为例来演示组合模式。在文件系统中,目录和文件可以被看作是树形结构中的节点,目录可以包含其他目录和文件,因此可以使用组合模式来表示文件系统的层次结构。

下面是对应的 Python 代码:

from abc import ABC, abstractmethod

# Component: 抽象基类
class FileSystemComponent(ABC):
    @abstractmethod
    def display(self):
        pass

# Leaf: 叶子节点(文件)
class File(FileSystemComponent):
    def __init__(self, name):
        self.name = name

    def display(self):
        print(f"File: {self.name}")

# Composite: 组合节点(目录)
class Directory(FileSystemComponent):
    def __init__(self, name):
        self.name = name
        self.children = []

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

    def remove(self, component):
        self.children.remove(component)

    def display(self):
        print(f"Directory: {self.name}")
        for child in self.children:
            child.display()

# 客户端代码
if __name__ == "__main__":
    # 创建文件
    file1 = File("file1.txt")
    file2 = File("file2.txt")

    # 创建目录
    directory1 = Directory("dir1")
    directory2 = Directory("dir2")

    # 向目录添加文件
    directory1.add(file1)
    directory2.add(file2)

    # 创建更高层级的目录,并将之前的目录添加到其中
    directory = Directory("root")
    directory.add(directory1)
    directory.add(directory2)

    # 打印整个文件系统的层次结构
    directory.display()

在上面的示例中,FileSystemComponent 是抽象基类,定义了组合模式中的组件接口。File 是叶子节点类,表示文件。Directory 是组合节点类,表示目录,可以包含其他目录和文件。在客户端代码中,我们创建了文件和目录,并通过 add() 方法将它们组合在一起形成文件系统的层次结构,然后通过 display() 方法打印出整个文件系统的层次结构。

3. 组合模式的结构

组合模式的典型结构如下图所示:

组合模式.png

它包含以下几个实体:

  1. Component(组件) : 定义组合中的对象的接口,可以是抽象类或接口,并声明了组合中所有对象的默认行为。在组合模式中,所有的叶子节点和组合节点都必须实现这个接口。

  2. Leaf(叶子节点) : 表示组合中的叶子节点对象,它没有子节点。叶子节点实现了组件接口,并定义了叶子对象的行为。

  3. Composite(组合节点) : 表示组合中的组合节点对象,它有子节点。组合节点实现了组件接口,并提供了管理子节点的方法,如添加、删除子节点等。

  4. Client (客户端) : 通过组件接口与所有项目交互。 因此, 客户端能以相同方式与树状结构中的简单或复杂项目交互。

4. 简单练习:【设计模式专题之组合模式】11-公司组织架构

""" 
【设计模式专题之组合模式】11-公司组织架构
时间限制:1.000S  空间限制:256MB
题目描述
小明所在的公司内部有多个部门,每个部门下可能有不同的子部门或者员工。

请你设计一个组合模式来管理这些部门和员工,实现对公司组织结构的统一操作。部门和员工都具有一个通用的接口,可以获取他们的名称以及展示公司组织结构。

输入描述
第一行是一个整数 N(1 <= N <= 100),表示后面有 N 行输入。 

接下来的 N 行,每行描述一个部门或员工的信息。部门的信息格式为 D 部门名称,员工的信息格式为 E 员工名称,其中 D 或 E 表示部门或员工。

输出描述
输出公司的组织结构,展示每个部门下的子部门和员工

输入示例
MyCompany
8
D HR
E HRManager
D Finance
E AccountantA
E AccountantB
D IT
E DeveloperA
E DeveloperB
输出示例
Company Structure:
MyCompany
  HR
    HRManager
  Finance
    AccountantA
    AccountantB
  IT
    DeveloperA
    DeveloperB
"""


from abc import ABC, abstractmethod


class Member(ABC):
    @abstractmethod
    def show(self, level: int):
        raise NotImplemented
        
        
class Employee(Member):
    def __init__(self, name: str):
        self.name = name
        
    def show(self, level: int):
        indent = "  " * level
        print(indent + "  " + self.name)
        

class Department(Member):
    def __init__(self, name: str):
        self.name = name
        self.children = []
        
    def add(self, child: Member):
        self.children.append(child)
        
    def show(self, level: int):
        indent = "  " * level
        print(indent + self.name)
        for child in self.children:
            child.show(level + 1)
            

class Company(Member):
    def __init__(self, name: str):
        self.root = Department(name)
    
    def add(self, child: Member):
        self.root.add(child)
        
    def show(self, level: int):
        print("Company Structure:")
        self.root.show(level)
            
            
def client():
    name = input()
    company = Company(name)
    n = int(input())
    for _ in range(n):
        memType, memName = input().split()
        if memType == "D":
            mem = Department(memName)
        else:
            assert memType == "E"
            mem = Employee(memName)
        company.add(mem)
    company.show(0)
    
    
if __name__ == "__main__":
    client()