秒懂设计模式--学习笔记(8)【结构型-组合模式】

目录

      • 7、组合模式
          • [7.1 组合模式(Composite)](#7.1 组合模式(Composite))
          • [7.2 叉树结构](#7.2 叉树结构)
          • [7.3 文件系统](#7.3 文件系统)
          • [7.4 目录树展示](#7.4 目录树展示)
          • [7.5 自相似性的涌现](#7.5 自相似性的涌现)
          • [7.6 组合模式的各角色定义](#7.6 组合模式的各角色定义)
          • [7.7 组合](#7.7 组合)

7、组合模式

7.1 组合模式(Composite)
  • 是针对由多个节点对象(部分)组成的树形结构的对象(整体)而发展出的一种结构型设计模式
  • 它能够使客户端在操作整体对象 或者其下的每个节点对象 时做出统一的响应,保证树形结构对象使用方法的一致性
  • 整体和部分,有类似结构
  • 测试类结构
7.2 叉树结构
  • 某些具有从属关系的事物 之间存在着一定的相似性
  • 不管从哪个层级,我们都会得到一个固定的结构
  • 这种结构类似于经典的"叉树"结构:无论数据元素是"根""枝",还是"叶",甚至是整体的树,都具有类似的结构
  • 我们可以用组合模式来表达"部分/整体"的层次结构提取并抽象其相同的部分特殊化其不同的部分,以提高系统的可复用性与可扩展性
7.3 文件系统
  • 以类似于树结构的文件系统的目录结构为例
  • 抽象节点类Node
    • 定义一个抽象的"节点"类来模糊"文件夹"与"文件"
    • 构造方法中接收并初始化已定义的节点名 ,否则不允许节点被创建,这也是可以固化下来的逻辑
    • 声明抽象方法,模糊行为并留给子类去实现
java 复制代码
package composite;

/**
 * 抽象节点类
 **/
public abstract class Node {
    // 节点命名
    protected  String name;

    /**
     * 构造方法,需传入节点名
     *  构造方法中接收并初始化已定义的节点名,否则不允许节点被创建,这也是可以【固化下来的逻辑】
     */
    public Node(String name) {
        this.name = name;
    }

    /**
     * 添加下级节点方法: 声明其为抽象方法,模糊此行为并留给子类去实现
     * @param child
     */
    protected abstract void add(Node child) throws Exception;
}
  • 节点实现类1:文件夹类Folder
    • 文件夹类继承了抽象节点类Node
    • 文件夹下级可以包含任意多个文件夹或者文件:次级节点列表List
java 复制代码
package composite;

import java.util.ArrayList;
import java.util.List;

/**
 * 文件夹类:继承了抽象节点类Node
 **/
public class Folder extends Node {
    /**
     * 文件夹可以包含子节点(子文件夹或文件)
     *  此处的泛型Node既可以是文件夹又可以是文件
     */
    List<Node> children = new ArrayList<>();

    /**
     * 调用父类构造方法:初始化其文件夹名
     * @param name
     */
    public Folder(String name) {
        super(name);
    }

    /**
     * 重写添加方法:可以添加子节点
     * @param child
     */
    @Override
    protected void add(Node child) {
        children.add(child);
    }
}
  • 节点实现类2:文件类File
    • 其作为末端节点,不应该具备添加子节点的功能
java 复制代码
package composite;

/**
 * 文件类:继承了抽象节点类Node
 **/
public class File extends Node {
    /**
     * 调用父类构造方法:初始化其文件夹名
     * @param name
     */
    public File(String name) {
        super(name);
    }

    /**
     * 文件不包含子节点: 告知用户"不能添加子节点"
     * 其实更好的方式是以抛出异常的形式来确保此处逻辑的正确性,外部如果捕获到该异常则可以做出相应的处理
     * @param child
     */
    @Override
    protected void add(Node child) {
        System.out.println("文件类型不能添加子节点");
    }
}
  • 客户端测试类:创建文件夹及文件
java 复制代码
package composite;

/**
 * 客户端测试类
 **/
public class Client {
    public static void main(String[] args) throws Exception {
        // 为根节点构建了目录树
        Node driveD = new Folder("测试盘");

        // "文档"和"音乐"两个文件夹
        Node doc = new Folder("文档");
        doc.add(new File("简历.doc"));
        doc.add(new File("项目介绍.ppt"));
        driveD.add(doc);

        // "文档"和"音乐"两个文件夹
        Node music = new Folder("音乐");
        // 一级文件夹来区分歌手
        Node jay = new Folder("周杰伦");
        jay.add(new File("双截棍.mp3"));
        jay.add(new File("告白气球.mp3"));
        jay.add(new File("听妈妈的话.mp3"));
        // 一级文件夹来区分歌手
        Node jack = new Folder("张学友");
        jack.add(new File("吻别.mp3"));
        jack.add(new File("一千个伤心的理由.mp3"));

        music.add(jay);
        music.add(jack);
        driveD.add(music);
        
    }
}
7.4 目录树展示
  • 要体现出组合模式的优势还在于如何运用这个树结构

  • 分级展示整棵目录树

    • 输出节点名称(文件夹名/文件名)之前加上数个空格以表示不同层级
    • 修改抽象节点类Node并加入展示方法tree()
    java 复制代码
        /**
         * 此处是抽象节点类的实体方法,所以要保持其通用性。
         *      我们抽离出所有节点"相同"的部分作为"公有"的代码块,
         *      而"不同"的行为部分则留给子类去实现
         * @param space 空格数(层级缩进展示)
         */
        protected void  tree(int space) {
            //先循环输出space个空格
            for (int i = 0; i < space; i++) {
                System.out.print("    ");
            }
    
            //接着再输出自己的名字
            System.out.println(name);
        }
    
        /**
         * 无参重载方法,默认从第0列开始展示(无层级缩进)
         */
        protected void tree() {
            this.tree(0);
        }
    • 节点实现类File和Folder重写展示方法,Folder还要展示其子级
    java 复制代码
        /**
         * Folder
         * 文件夹类就比较特殊了,它不仅要先输出自己的名字,还要换行再逐个输出子节点的名字,并且要保证空格逐级递增
         */
        @Override
        public void tree(int space) {
            // 调用父类通用的tree方法列出自己的名字
            super.tree(space);
    
            // 在循环的子节点前,空格数要加1
            space++;
            // 下一级的子节点我们需要依次输出
            for (Node child : children) {
                // 调用子节点的tree方法
                child.tree(space);
            }
        }
    java 复制代码
    /**
     * File
     * 文件类可以不做任何修改,而是直接继承父类的展示方法,
     * 此处是为了更清晰直观地看到这种继承关系,同时方便后续做出其他修改
     * @param space
     */
    @Override
    public void tree(int space){
        super.tree(space);
    }
    • 客户端在任何一级节点上只要调用其展示方法并传入当前目录所需的空格偏移量,就可出现树形列表了
    java 复制代码
    	// Client类的main方法最后添加调用目录树展示方法
        driveD.tree();
    • 空格偏移量这个必传参数,可以为抽象节点类添加一个无参的展示方法,默认为0
7.5 自相似性的涌现
  • 组合模式将树形结构的特点发挥得淋漓尽致
    • 作为最高层级抽象的抽象节点类 (接口)泛化了所有节点类使任何"整体"或"部分"达成统一
    • 枝(根)节点与叶节点的多态化实现以及组合关系进一步勾勒出的树形结构
7.6 组合模式的各角色定义
  • Component(组件接口):
    • 所有复合节点与叶节点的高层抽象 ,定义出需要对组件操作的接口标准
    • 如抽象节点类Node,具体使用接口还是抽象类需根据具体场景而定。
  • Composite(复合组件):
    • 包含多个子组件对象(可以是复合组件或叶端组件)的复合型组件,并实现组件接口中定义的操作方法。
    • 如:"根节点/枝节点"的文件夹类Folder
  • Leaf(叶端组件):
    • 不包含子组件的终端组件,同样实现组件接口中定义的操作方法。
    • 如: "叶节点"的文件类File
  • Client(客户端):
    • 按所需的层级关系部署相关对象并操作组件接口所定义的接口,即可遍历树结构上的所有组件
7.7 组合
  • 类似的结构总是在重复、迭代地显现出某种自似性
  • 其部分与整体一致的呈现与"组合模式"如出一辙
相关推荐
wrx繁星点点24 分钟前
状态模式(State Pattern)详解
java·开发语言·ui·设计模式·状态模式
cuisidong19972 小时前
5G学习笔记三之物理层、数据链路层、RRC层协议
笔记·学习·5g
乌恩大侠2 小时前
5G周边知识笔记
笔记·5g
南宫理的日知录2 小时前
99、Python并发编程:多线程的问题、临界资源以及同步机制
开发语言·python·学习·编程学习
金池尽干2 小时前
设计模式之——观察者模式
观察者模式·设计模式
也无晴也无风雨2 小时前
代码中的设计模式-策略模式
设计模式·bash·策略模式
数据与后端架构提升之路3 小时前
从神经元到神经网络:深度学习的进化之旅
人工智能·神经网络·学习
一行13 小时前
电脑蓝屏debug学习
学习·电脑
咔叽布吉3 小时前
【论文阅读笔记】CamoFormer: Masked Separable Attention for Camouflaged Object Detection
论文阅读·笔记·目标检测
johnny2333 小时前
《大模型应用开发极简入门》笔记
笔记·chatgpt