设计模式之组合模式

设计模式之组合模式(Composite Pattern),又称为“整体—部分”(Part-Whole)模式,是一种结构型设计模式。它通过将一组相似的对象组合成树形结构来表示“部分-整体”的层次关系,使得用户对单个对象和组合对象的使用具有一致性。以下是对组合模式的详细介绍:

一、定义与特点

  • 定义:组合模式将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
  • 特点
    • 提供了表示对象集合的抽象数据类型。
    • 组合具有表示单个对象和组合对象的类层次结构。
    • 组合对象可以包含并可以统一处理其组合成分。

二、主要角色与类结构

组合模式通常包含以下几个主要角色:

  1. 抽象构件(Component)
    • 定义了一个对象的接口,用于访问和管理组件的子对象。
    • 可以是接口或抽象类,声明了对象共有的方法,包括用于访问和管理子构件的方法(如增加子构件、删除子构件、获取子构件等)。
    • 在透明式组合模式中,还声明了所有子类中的全部方法。
  2. 叶子构件(Leaf)
    • 在组合中表示叶子节点对象,是组合中的叶节点对象,没有子节点。
    • 实现了抽象构件中定义的行为,但对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。
  3. 容器构件(Composite)/树枝构件(Composite)
    • 在组合中表示容器节点对象,是组合中的分支节点对象,包含子节点。
    • 其子节点可以是叶子节点,也可以是容器节点。
    • 提供一个集合用于存储子节点,并实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法。

三、实现方式

组合模式的实现根据所实现接口的区别分为两种形式,分别称为安全式组合模式透明式组合模式

  1. 安全式组合模式
    • 管理聚集的方法只出现在树枝构件类中,而不出现在树叶构件类中。
    • 抽象构件不定义出管理子对象的方法,这一定义由树枝构件对象给出。
    • 缺点是不够透明,因为树叶类和树枝类将具有不同的接口。
  2. 透明式组合模式
    • 要求所有的具体构件类,不论树枝构件还是树叶构件,均符合一个固定接口。
    • 客户端可以一致地处理所有的对象,无须区分是叶子节点还是容器节点。
    • 缺点是树叶构件本来没有管理子对象的方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。

四、优点

  1. 简化客户端代码:客户端可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象还是组合对象。
  2. 高扩展性:更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”。

五、缺点

  1. 设计较复杂:客户端需要花更多时间理清类之间的层次关系。
  2. 安全性问题:透明式组合模式中,树叶构件需要实现一些不必要的方法,可能带来安全性问题。
  3. 性能问题:由于组合模式中的对象可能被存储在多个地方,可能导致性能问题,特别是在树形结构复杂的情况下。

六、适用场景

组合模式适用于需要表示对象的部分-整体层次结构的场景,如:

  • 文件系统中的文件和文件夹。
  • 窗体程序中的简单控件与容器控件。
  • 图形界面中的菜单项、按钮和其他控件的树形结构。
  • 表示公司或组织的部门结构。
  • XML文档的层次结构解析等。

七、示例

在文件系统的例子中,文件和文件夹可以被看作是一个整体-部分的关系。使用组合模式,可以创建一个统一的接口来处理文件和文件夹,无论是对文件进行操作还是对文件夹及其子文件夹进行操作,都可以使用相同的代码逻辑。

// 抽象组件类  
abstract class FileSystemComponent {  
    private String name;  
  
    public FileSystemComponent(String name) {  
        this.name = name;  
    }  
  
    // 添加子组件的方法,默认为空实现(透明式组合模式)  
    public void add(FileSystemComponent component) {  
        throw new UnsupportedOperationException("This is a leaf.");  
    }  
  
    // 删除子组件的方法,默认为空实现  
    public void remove(FileSystemComponent component) {  
        throw new UnsupportedOperationException("This is a leaf.");  
    }  
  
    // 显示组件信息的方法  
    public void display(int depth) {  
        StringBuilder indent = new StringBuilder();  
        for (int i = 0; i < depth; i++) {  
            indent.append("--");  
        }  
        System.out.println(indent + name);  
    }  
  
    // 声明一个用于访问子组件的方法(可选,根据具体需求实现)  
    // ...  
  
    // 获取组件名称的方法  
    public String getName() {  
        return name;  
    }  
}  
  
// 叶子类(文件)  
class File extends FileSystemComponent {  
    public File(String name) {  
        super(name);  
    }  
  
    // 文件不需要添加或删除子组件,因此不需要重写这些方法  
}  
  
// 容器类(文件夹)  
class Folder extends FileSystemComponent {  
    private List<FileSystemComponent> children = new ArrayList<>();  
  
    public Folder(String name) {  
        super(name);  
    }  
  
    // 文件夹可以添加子组件  
    @Override  
    public void add(FileSystemComponent component) {  
        children.add(component);  
    }  
  
    // 文件夹可以删除子组件  
    @Override  
    public void remove(FileSystemComponent component) {  
        children.remove(component);  
    }  
  
    // 文件夹需要显示自己和所有子组件  
    @Override  
    public void display(int depth) {  
        super.display(depth);  
        for (FileSystemComponent child : children) {  
            child.display(depth + 1);  
        }  
    }  
}  
  
// 客户端代码  
public class CompositePatternDemo {  
    public static void main(String[] args) {  
        // 创建文件夹和文件  
        Folder root = new Folder("Root");  
        Folder folder1 = new Folder("Folder 1");  
        Folder folder2 = new Folder("Folder 2");  
        File file1 = new File("File 1.txt");  
        File file2 = new File("File 2.txt");  
  
        // 构建树结构  
        root.add(folder1);  
        root.add(folder2);  
        folder1.add(file1);  
        folder2.add(file2);  
  
        // 显示文件结构  
        root.display(0);  
    }  
}

在这个例子中,FileSystemComponent 是抽象组件类,它有一个 name 属性和一些基本方法(如 addremovedisplay)。注意,add 和 remove 方法在 FileSystemComponent 类中被声明为抛出 UnsupportedOperationException,因为叶子类(File)不应该包含这些方法。然而,在透明式组合模式中,我们更倾向于在叶子类中保留这些方法,并让它们空实现或抛出异常(如上所示),以保持接口的一致性。

File 类是叶子类,它继承自 FileSystemComponent 但不实现 add 和 remove 方法,因为这些方法对于文件来说没有意义。

Folder 类是容器类,它继承自 FileSystemComponent 并重写了 add 和 remove 方法以支持子组件的管理。同时,它还重写了 display 方法来递归地显示文件夹及其子组件。

最后,在 CompositePatternDemo 类的 main 方法中,我们创建了文件夹和文件对象,构建了树形结构,并调用了 display 方法来显示整个文件系统的结构。

八、结束语

组合模式是一种非常有用的设计模式,它提供了一种灵活的方式来处理对象之间的整体-部分关系,使得系统更加模块化和易于扩展。

如果对你有帮助,记得点赞收藏。

相关推荐

  1. 设计模式组合模式

    2024-07-13 23:42:05       23 阅读
  2. go设计模式组合设计模式

    2024-07-13 23:42:05       30 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-13 23:42:05       70 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-13 23:42:05       74 阅读
  3. 在Django里面运行非项目文件

    2024-07-13 23:42:05       62 阅读
  4. Python语言-面向对象

    2024-07-13 23:42:05       72 阅读

热门阅读

  1. SQL注入:原理及示例

    2024-07-13 23:42:05       20 阅读
  2. Qt/QML学习-动画元素

    2024-07-13 23:42:05       22 阅读
  3. 小程序自学教程

    2024-07-13 23:42:05       20 阅读
  4. C#的DllImport使用方法

    2024-07-13 23:42:05       19 阅读
  5. Elasticsearch-多边形范围查询(8.x)

    2024-07-13 23:42:05       21 阅读
  6. SpringBoot后端代码基本逻辑

    2024-07-13 23:42:05       19 阅读
  7. 响应式编程-数据劫持

    2024-07-13 23:42:05       21 阅读
  8. Vue-生命周期勾子函数

    2024-07-13 23:42:05       18 阅读
  9. 计算机如何学习

    2024-07-13 23:42:05       17 阅读
  10. 要修改已经推送到远程仓库的提交信息

    2024-07-13 23:42:05       18 阅读
  11. linux 设置nginx开机自启

    2024-07-13 23:42:05       23 阅读
  12. c++贪心算法

    2024-07-13 23:42:05       19 阅读
  13. ArcGIS Pro SDK (八)地理数据库 4 查询

    2024-07-13 23:42:05       16 阅读
  14. 文本语言的上升沿写法

    2024-07-13 23:42:05       17 阅读
  15. Aop实现后端数据重复提交

    2024-07-13 23:42:05       24 阅读