「聊设计模式」之组合模式(Composite)

2023年 9月 21日 42.3k 0

🏆本文收录于《聊设计模式》专栏,专门攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎持续关注&&收藏&&订阅!

前言

  在软件开发中,我们常常需要处理树形结构的问题。树形结构通常由树枝和叶子节点组成,每个树枝可以有多个子节点或叶子节点。而组合模式(Composite)就是一种处理树形数据结构的模式。

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

摘要

  组合模式是一种结构型设计模式。可以将对象组合成树形结构,以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

  在本文中,我们通过一个示例程序来阐释组合模式的实现方式、优缺点、使用场景及注意事项。

组合模式

概念

  组合模式是一种结构型设计模式,允许将对象组合成树形结构来表示部分-整体的层次结构。它使客户端可以像处理单个对象一样处理对象的组合。组合模式使用递归算法从而可以处理复杂的层次结构,同时保持简单的代码结构。在组合模式中,有两种基本类型的对象:叶子节点和组合节点。叶子节点表示树形结构中的单个元素,而组合节点则表示一个包含多个子节点的组合元素。通过这种方式,组合模式可以帮助我们在层次结构中自由地添加、删除和修改对象,同时不会破坏整个树形结构的完整性。

结构

组合模式包含以下角色:

  • Component(抽象构件):它可以是接口或抽象类,为叶子构件和组合构件对象声明接口,实现所有类共有的接口默认行为,用于访问和管理 Component 的子部件。
  • Leaf(叶子构件):在组合中表示子节点对象,叶子节点没有子节点。
  • Composite(组合构件):定义组合中的枝干节点行为,用于存储子部件,在 Component 接口中实现与子部件有关的操作。

  通过组合模式,我们可以将一个树形结构表示为一个对象,同时对于客户端而言,无论是访问树中的一个叶子节点还是一个枝干节点,都可以使用一致的方式进行操作。

下面是组合模式的 UML 类图:

在这里插入图片描述

优缺点

优点

  • 可以清晰地定义复杂对象的层次结构;
  • 客户端代码可以一致地处理单个对象和组合对象。
  • 缺点

  • 组合模式在增加新的构件时很难对系统的复杂度进行限制。
  • 使用场景

    组合模式通常在以下场景中使用:

  • 表示对象的部分-整体层次结构;
  • 让客户端统一处理单个对象和组合对象。
  • 注意事项

  • 不建议在组合模式中使用过多的类型判断(如 instanceof),否则会导致系统变得复杂;
  • 程序中每个节点最好都有父节点的引用,方便操作。
  • 示例程序

      本文以操作系统文件系统为例,来阐释组合模式的实现方式。文件系统由文件夹(Folder)和文件(File)组成,其中文件夹可以包含多个文件夹或文件,而文件没有子节点。

    我们先定义一个抽象的文件系统节点 Component

    package com.example.javaDesignPattern.composite;
    
    /**
     * @author bug菌
     * @version 1.0
     * @date 2023/9/19 14:32
     */
    public abstract class Component {
        protected String name;
    
        public Component(String name) {
            this.name = name;
        }
    
        public abstract void add(Component c);
    
        public abstract void remove(Component c);
    
        public abstract void display();
    }
    

      该类定义了文件系统中节点的公共属性和方法,其中 add()remove()用于添加/移除子节点,display() 用于展示当前节点信息。

    然后定义叶子节点 File:

    package com.example.javaDesignPattern.composite;
    
    /**
     * @author bug菌
     * @version 1.0
     * @date 2023/9/19 14:33
     */
    public class File extends Component {
        public File(String name) {
            super(name);
        }
    
        @Override
        public void add(Component c) {
            System.out.println("File cannot add any component");
        }
    
        @Override
        public void remove(Component c) {
            System.out.println("File cannot remove any component");
        }
    
        @Override
        public void display() {
            System.out.println("File Name: " + name);
        }
    }
    

      该类表示文件节点,它没有子节点,因此 add()remove() 操作无法实现。

    最后定义组合节点 Folder

    public class Folder extends Component {
        private List children = new ArrayList();
    
        public Folder(String name) {
            super(name);
        }
    
        @Override
        public void add(Component c) {
            children.add(c);
        }
    
        @Override
        public void remove(Component c) {
            children.remove(c);
        }
    
        @Override
        public void display() {
            System.out.println("Folder Name: " + name);
            for (Component c : children) {
                c.display();
            }
        }
    }
    

      该类表示文件夹节点,它可以包含多个子节点,因此实现了 add()remove() 操作,并在 display() 中递归展示子节点信息。

    我们可以使用如下方式测试程序:

    package com.example.javaDesignPattern.composite;
    
    /**
     * @author bug菌
     * @version 1.0
     * @date 2023/9/19 14:34
     */
    public class CompositeTest {
        public static void main(String[] args) {
            Component root = new Folder("root");
            Component folder1 = new Folder("folder1");
            Component folder2 = new Folder("folder2");
            Component folder3 = new Folder("folder3");
    
            Component file1 = new File("file1");
            Component file2 = new File("file2");
            Component file3 = new File("file3");
    
            folder1.add(file1);
            folder2.add(file2);
            folder3.add(file3);
    
            root.add(folder1);
            root.add(folder2);
            root.add(folder3);
    
            root.display();
        }
    }
    

    输出结果如下:

    在这里插入图片描述

    代码解读

      这是一个演示组合模式的 Java 代码。组合模式是一种结构型模式,它将对象组合成树形结构以表示“部分-整体”的层次关系。组合模式使得用户对单个对象和组合对象的使用具有一致性。

      在这个示例中,首先定义了一个 Component 接口,它包含了 add()remove()display() 方法,用于添加、移除和展示组件。然后定义了两个具体的组件类:FolderFile。其中 Folder 类具有一个 ArrayList 成员变量,用于存储它所包含的组件。

      在 main() 方法中,首先创建了一个根节点 root,然后创建了三个文件夹和三个文件,并将文件添加到对应的文件夹中。最后将三个文件夹添加到根节点下,并调用根节点的 display() 方法,展示整个组合结构。

    输出结果类似于,如下:

    root
    ---folder1
    ------file1
    ---folder2
    ------file2
    ---folder3
    ------file3
    

    其中 - - - 表示缩进,表示嵌套的层次关系。

    附录源码

      如上涉及代码均已上传同步在 GitHub,提供给同学们参考性学习。

    总结

      本文介绍了组合模式的实现方式、优缺点、使用场景及注意事项,并通过一个文件系统示例程序进行了阐述。组合模式提供了一种处理树形结构数据的方式,它使得客户端代码可以一致地处理单个对象和组合对象。同时,我们也需要注意在实现过程中避免过多的类型判断,同时保证每个节点都有父节点的引用。

    ☀️建议/推荐你

      如果想系统性的全面学习设计模式,建议小伙伴们直接毫无顾忌的关注这个专栏《聊设计模式》,无论你是想提升自己的编程技术,还是渴望更好地理解代码背后的设计思想,本专栏都会为你提供实用的知识和启发,帮助你更好地解决日常开发中的挑战,将代码变得更加优雅、灵活和可维护!

    📣关于我

    我是bug菌,CSDN | 掘金 | infoQ | 51CTO 等社区博客专家,历届博客之星Top30,掘金年度人气作者Top40,51CTO年度博主Top12,华为云 | 阿里云| 腾讯云等社区优质创作者,全网粉丝合计15w+ ;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板等海量资料。

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论