• 【设计模式】32.结构型模式-组合模式(Composite)


    一、描述

    首先,看一个数据结构:
    在这里插入图片描述
    在平时开发过程中,我们的菜单目录、文件夹目录等都有类似如上的实体结构,其中composite代表父级节点,leaf代表叶子节点,composite可以有子节点,但是leaf下没有节点。解析此结构时,循环解析每个节点,当解析到叶子节点时,当前循环结束。这种数据结构就是组合模式的代表结构。

    定义:

    组合模式将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。

    角色

    (1)Component(抽象构件):为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。定义了管理子构件的方法,如定义了新增构件、删除构件、获取构件的方法等。
    (2)Composite(中间构件):提供一个集合包含其他构件或者叶子节点,实现抽象构件方法,管理集合中的节点。
    (3)Leaf(叶子节点):实现抽象构件方法,但是其没有子节点。

    类图

    在这里插入图片描述

    二、优点

    (1)高层模块调用简单。一颗树形结构中的所有节点都是Component,局部和整体对调用者来说没有任何区别,高层模块不必关心自己处理的是单个对象还是整个组合接口。
    (2)节点自由添加

    三、缺点

    (1)使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒转原则

    四、使用场景

    (1)维护和展示部分-整体关系的场景(树形菜单、文件和文件夹管理)
    (2)一个整体中能够独立出部分模块或功能的场景

    五、示例

    以“电脑硬盘上的文件夹以及文件为例”,文件夹下有文件,文件夹是具体构件,文件是叶子节点。下面是代码:
    (1)IFile,定义文件公共方法

    public interface IFile {
        /**
         * 添加文件或者文件夹
         *
         * @param iFile
         */
        void add(IFile iFile);
    
        /**
         * 删除文件或者文件夹
         *
         * @param iFile
         */
        void delete(IFile iFile);
    
        /**
         * 获取文件或者文件夹
         *
         * @return
         */
        void operation();
    
        /**
         * 获取文件信息
         *
         * @param i
         * @return
         */
        IFile get(int i);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    (2)定义中间构件Folder

    public class Folder implements IFile {
        //存储文件集合
        private List<IFile> children = new ArrayList<>();
        //文件夹名称
        private String name;
    
        public Folder(String name) {
            this.name = name;
        }
    
        @Override
        public void add(IFile iFile) {
            children.add(iFile);
        }
    
        @Override
        public void delete(IFile iFile) {
            children.remove(iFile);
        }
    
        @Override
        public void operation() {
            System.out.println(name + "文件夹,其下有:");
            for (int i = 0; i < children.size(); i++) {
                System.out.print("-");
                children.get(i).operation();
            }
        }
    
        @Override
        public IFile get(int i) {
            return children.get(i);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    (3)叶子节点File,文件

    public class File implements IFile {
        private String name;
    
        public File(String name) {
            this.name = name;
        }
    
        @Override
        public void add(IFile iFile) {
            System.out.println("叶子节点无法添加文件");
        }
    
        @Override
        public void delete(IFile iFile) {
            System.out.println("叶子节点无法删除文件");
        }
    
        @Override
        public void operation() {
            System.out.println(name + "文件");
        }
    
        @Override
        public IFile get(int i) {
            return this;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    (4)Client,客户端
    文件夹结构如下:

    d:
    -code:
    --company:
    ---projectA:
    ----classA1
    ---projectB
    ----classB1
    -me
    --myproject:
    ---excelA.xls
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    public class Client {
        public static void main(String[] args) {
            //D盘下有code文件夹,code文件夹下有company和me文件夹,文件夹下是项目,项目下是代码文件
            Folder d = new Folder("d盘");
            Folder code = new Folder("code");
            Folder company = new Folder("company");
            Folder me = new Folder("me");
    
            d.add(code);
            code.add(company);
            code.add(me);
    
            Folder projectA = new Folder("projectA");
            File classA1 = new File("classA1");
            projectA.add(classA1);
            Folder projectB = new Folder("projectB");
            File classB1 = new File("classB1");
            projectB.add(classB1);
            company.add(projectA);
            company.add(projectB);
    
            Folder myProject = new Folder("myProject");
            File excelA = new File("excelA.xls");
            me.add(myProject);
            me.add(excelA);
    
            d.operation();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    实现效果,由于主要是体现组合模式,因此没有实现结构划分:
    在这里插入图片描述

  • 相关阅读:
    微信浏览器H5页面后退并刷新
    3天带你走向实战,阿里顶配版Spring全家桶面试进阶笔记有多强?
    Linux多线程
    怎么给图片加水印?四个操作步骤
    Python课设-学生信息管理系统
    9.14 c++基础
    第142篇:原生js实现响应式原理
    【无标题】
    linux总结10大危险命令
    JavaEE——Spring Boot + jwt
  • 原文地址:https://blog.csdn.net/CAT_cwds/article/details/128179199