【西瓜带你学设计模式 | 第十三期 - 组合模式】组合模式 —— 树形结构统一处理实现、优缺点与适用场景

文章目录

    • 前言
    • [1. 组合模式是什么?](#1. 组合模式是什么?)
    • [2. 组合模式解决什么问题?](#2. 组合模式解决什么问题?)
    • [3. 核心结构](#3. 核心结构)
      • [3.1 Component(抽象组件)](#3.1 Component(抽象组件))
      • [3.2 Leaf(叶子节点)](#3.2 Leaf(叶子节点))
      • [3.3 Composite(组合节点 / 容器节点)](#3.3 Composite(组合节点 / 容器节点))
      • [3.4 Client(客户端)](#3.4 Client(客户端))
    • [4. 实现思路:递归 + 统一接口](#4. 实现思路:递归 + 统一接口)
    • [5. 示例](#5. 示例)
      • [5.1 Component:文件系统项](#5.1 Component:文件系统项)
      • [5.2 Leaf:文件](#5.2 Leaf:文件)
      • [5.3 Composite:文件夹](#5.3 Composite:文件夹)
      • [5.4 Client:构建树并统一调用](#5.4 Client:构建树并统一调用)
    • [6. 优缺点](#6. 优缺点)
      • [6.1 优点](#6.1 优点)
      • [6.2 缺点](#6.2 缺点)
    • [7. 和其他设计模式区别](#7. 和其他设计模式区别)
      • [7.1 组合模式 vs 装饰器](#7.1 组合模式 vs 装饰器)
      • [7.2 组合模式 vs 外观](#7.2 组合模式 vs 外观)
      • [7.3 组合模式 vs 代理](#7.3 组合模式 vs 代理)
    • [8. 适用场景](#8. 适用场景)
    • [9. 总结](#9. 总结)

前言

很多业务天生就是"树形结构 / 部分-整体层次关系":

  • 文件系统:文件 + 文件夹(文件夹还能再装文件夹)
  • 组织架构:部门(可包含子部门)+ 员工
  • 菜单:主菜单 + 子菜单
  • GUI 组件树:面板容器里有按钮、面板里又有面板

如果用"分类型处理"的方式(叶子一个逻辑、容器另一个逻辑),客户端代码会越来越难维护。 组合模式正是为了解决这种"层次结构要统一处理"的问题:

让客户端把"单个对象"和"对象组合(容器)"当成同一种类型来用,从而统一操作整棵树。


1. 组合模式是什么?

组合模式(Composite Pattern) 是一种结构型设计模式。它允许你把对象组织成树形结构,表示"部分-整体"的层次结构,并让客户端以一致的方式处理:

  • 叶子节点(Leaf):最小单元
  • 组合节点(Composite):可以包含子节点(叶子或其它组合)

一句话定义(理解用):

像处理单个文件一样处理文件夹整棵结构。


2. 组合模式解决什么问题?

组合模式主要解决这类问题:

当系统存在复杂层次结构(例如文件夹/文件)时,如果你对"叶子"和"容器"分开写逻辑,会造成:

  • 重复代码、维护成本高
  • 客户端需要频繁判断类型(instanceof / 强转)
  • 难以扩展新的层次节点

组合模式的核心解决方案是:定义统一接口 Component,让叶子与容器都实现它。 客户端只依赖 Component,通过递归处理树形结构。


3. 核心结构

3.1 Component(抽象组件)

  • 定义"所有节点都要支持的公共接口"(例如:display() / operation()
  • 对于"管理子节点"的方法,可能有两种风格:
    • 透明模式 :Component 里也声明 add/remove,叶子节点可能抛异常或空实现
    • 安全模式:Component 只声明业务方法,子节点管理只放在 Composite

3.2 Leaf(叶子节点)

  • 树的末端节点
  • 没有子节点
  • 实现 Component 的业务方法

3.3 Composite(组合节点 / 容器节点)

  • 同样实现 Component
  • 内部持有 List<Component>(或其它集合)
  • 支持 add/remove
  • 业务方法里会递归调用子节点的业务方法

3.4 Client(客户端)

  • 面向 Component 编程
  • 不关心当前节点是 Leaf 还是 Composite
  • 直接对整棵树调用同一个接口方法

4. 实现思路:递归 + 统一接口

实现时一般遵循:

  1. 抽象出公共接口 Component(业务方法)
  2. Leaf:只处理自己的业务
  3. Composite:维护 children,并在业务方法里遍历递归调用
  4. 客户端只拿到一个 root Component,直接调用即可

5. 示例

下面用"文件系统"作为例子:

  • File 是叶子节点
  • Folder 是组合节点
  • 客户端只通过 display() 操作整棵树(不用管文件还是文件夹)

5.1 Component:文件系统项

java 复制代码
public interface FileSystemComponent {
    void display();
}

5.2 Leaf:文件

java 复制代码
public class File implements FileSystemComponent {
    private final String name;

    public File(String name) {
        this.name = name;
    }

    @Override
    public void display() {
        System.out.println("文件: " + name);
    }
}

5.3 Composite:文件夹

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class Folder implements FileSystemComponent {
    private final String name;
    private final List<FileSystemComponent> children = new ArrayList<>();

    public Folder(String name) {
        this.name = name;
    }

    public void add(FileSystemComponent component) {
        children.add(component);
    }

    @Override
    public void display() {
        System.out.println("文件夹: " + name);
        for (FileSystemComponent child : children) {
            child.display(); // 关键:递归统一处理
        }
    }
}

5.4 Client:构建树并统一调用

java 复制代码
public class Client {
    public static void main(String[] args) {
        FileSystemComponent doc = new Folder("文档目录");
        ((Folder) doc).add(new File("doc1.txt"));
        ((Folder) doc).add(new File("doc2.txt"));

        FileSystemComponent root = new Folder("根目录");
        ((Folder) root).add(doc);
        ((Folder) root).add(new File("readme.md"));

        root.display();
    }
}

运行效果类似:

  • 先打印根目录(组合节点)
  • 再递归打印其下的子节点(叶子/组合都一样走 display()

6. 优缺点

6.1 优点

  1. 统一接口:客户端对叶子/容器用同样的方式处理
  2. 结构清晰:天然表达"部分-整体"的层次关系
  3. 扩展性强:新增节点类型通常不会破坏客户端(继续实现 Component)
  4. 递归自然:遍历整棵树、计算总量、渲染结构都很顺畅

6.2 缺点

  1. 实现复杂度可能上升:尤其当树很深或节点类型很多时
  2. 透明模式风险 :若在 Component 放了 add/remove,Leaf 可能只能空实现或抛异常
  3. 性能/递归开销:遍历或计算可能带来额外开销(树很大时更明显)

7. 和其他设计模式区别

7.1 组合模式 vs 装饰器

  • 组合模式 :在"结构上"把对象组织成树,强调 整体-部分 与递归处理
  • 装饰器模式 :在"对象上"动态加行为,强调 包装叠加功能(通常是一条调用链)

一句话:

组合模式是"树结构的统一处理",装饰器是"包装增强同一对象"。

7.2 组合模式 vs 外观

  • 外观模式:提供一个简单入口,隐藏复杂子系统("封装入口")
  • 组合模式:表达层次结构,让客户端递归地对节点统一操作("表示结构")

7.3 组合模式 vs 代理

  • 代理模式:控制访问/转发(权限、远程对象、延迟加载等)
  • 组合模式:管理层级关系与统一遍历调用

8. 适用场景

组合模式特别适合:

  • 需要表达树形结构部分-整体层次
  • 希望客户端代码能统一处理叶子与容器
  • 需要对整棵结构做递归操作(遍历、渲染、计算汇总等)
  • 结构可能会动态变化(节点增删)

9. 总结

把对象组织成树形结构,让叶子和容器实现统一接口,客户端对它们用同一种方式处理,并通过递归完成整体操作。

相关推荐
翊谦10 小时前
Java Agent开发 Milvus 向量数据库安装
java·数据库·milvus
晓晓hh10 小时前
JavaSE学习——迭代器
java·开发语言·学习
查古穆10 小时前
栈-有效的括号
java·数据结构·算法
Java面试题总结10 小时前
Spring - Bean 生命周期
java·spring·rpc
硅基诗人10 小时前
每日一道面试题 10:synchronized 与 ReentrantLock 的核心区别及生产环境如何选型?
java
014-code10 小时前
String.intern() 到底干了什么
java·开发语言·面试
摇滚侠11 小时前
JAVA 项目教程《苍穹外卖-12》,微信小程序项目,前后端分离,从开发到部署
java·开发语言·vue.js·node.js
楚国的小隐士11 小时前
为什么说Rust是对自闭症谱系人士友好的编程语言?
java·rust·编程·对比·自闭症·自闭症谱系障碍·神经多样性
春花秋月夏海冬雪12 小时前
代码随想录刷题 - 贪心Part1
java·算法·贪心·代码随想录