当前位置: 首页 > news >正文

【设计模式】 组合模式(Composite)大白话讲解

组合模式(Composite)大白话讲解

一句话概括

就像文件夹和文件的关系:文件夹可以包含文件,也可以包含其他文件夹,但你对它们的操作是统一的
在这里插入图片描述


现实生活比喻

场景1:公司组织架构

  • 员工:普通员工(叶子节点)
  • 经理:可以管理多个员工(组合节点)
  • CEO:可以管理多个经理(组合节点)
  • 操作:计算总工资、发布通知等操作对所有人都一样

场景2:菜单系统

  • 菜单项:具体的菜(叶子节点)
  • 子菜单:包含多个菜单项(组合节点)
  • 主菜单:包含多个子菜单(组合节点)
  • 操作:显示菜单、计算总价等操作对各级都一样

完整代码示例

场景:文件系统

/*** 组合模式 - 文件系统示例*/
public class Main {public static void main(String[] args) {System.out.println("=== 构建文件系统 ===");// 创建文件(叶子节点)FileSystemComponent file1 = new File("简历.doc", 200);FileSystemComponent file2 = new File("照片.jpg", 1500);FileSystemComponent file3 = new File("报告.pdf", 500);FileSystemComponent file4 = new File("代码.java", 100);FileSystemComponent file5 = new File("数据.txt", 50);// 创建文件夹(组合节点)FileSystemComponent workFolder = new Folder("工作资料");FileSystemComponent personalFolder = new Folder("个人文件");FileSystemComponent projectFolder = new Folder("项目文档");FileSystemComponent rootFolder = new Folder("我的电脑");// 构建树形结构workFolder.add(file1);workFolder.add(file3);personalFolder.add(file2);personalFolder.add(file4);projectFolder.add(file5);workFolder.add(projectFolder);  // 文件夹中可以包含文件夹rootFolder.add(workFolder);rootFolder.add(personalFolder);// 统一操作 - 显示整个文件系统System.out.println("\n=== 文件系统结构 ===");rootFolder.display();// 统一操作 - 计算总大小System.out.println("\n=== 统计信息 ===");System.out.println("工作资料大小: " + workFolder.getSize() + "KB");System.out.println("个人文件大小: " + personalFolder.getSize() + "KB");System.out.println("整个电脑大小: " + rootFolder.getSize() + "KB");// 统一操作 - 搜索文件System.out.println("\n=== 搜索文件 ===");searchFile(rootFolder, "代码.java");searchFile(rootFolder, "不存在的文件.xyz");}// 统一的搜索方法,对文件和文件夹都适用public static void searchFile(FileSystemComponent component, String fileName) {if (component.search(fileName)) {System.out.println("找到文件: " + fileName);} else {System.out.println("未找到文件: " + fileName);}}
}/*** 抽象组件 - 定义文件和文件夹的共同接口*/
interface FileSystemComponent {void display();          // 显示int getSize();           // 获取大小boolean search(String fileName);  // 搜索文件void add(FileSystemComponent component);      // 添加(对文件无效)void remove(FileSystemComponent component);   // 删除(对文件无效)
}/*** 叶子节点 - 文件*/
class File implements FileSystemComponent {private String name;private int size;  // 文件大小,单位KBpublic File(String name, int size) {this.name = name;this.size = size;}@Overridepublic void display() {System.out.println("文件: " + name + " (" + size + "KB)");}@Overridepublic int getSize() {return size;}@Overridepublic boolean search(String fileName) {return this.name.equals(fileName);}@Overridepublic void add(FileSystemComponent component) {// 文件不能添加子组件,抛出异常或忽略throw new UnsupportedOperationException("文件不支持添加操作");}@Overridepublic void remove(FileSystemComponent component) {// 文件不能删除子组件,抛出异常或忽略throw new UnsupportedOperationException("文件不支持删除操作");}
}/*** 组合节点 - 文件夹*/
class Folder implements FileSystemComponent {private String name;private List<FileSystemComponent> children = new ArrayList<>();public Folder(String name) {this.name = name;}@Overridepublic void display() {System.out.println("文件夹: " + name);// 递归显示所有子组件for (FileSystemComponent child : children) {System.out.print("  ");child.display();}}@Overridepublic int getSize() {int totalSize = 0;// 递归计算所有子组件的大小for (FileSystemComponent child : children) {totalSize += child.getSize();}return totalSize;}@Overridepublic boolean search(String fileName) {// 先检查当前文件夹是否匹配if (this.name.equals(fileName)) {return true;}// 递归搜索所有子组件for (FileSystemComponent child : children) {if (child.search(fileName)) {return true;}}return false;}@Overridepublic void add(FileSystemComponent component) {children.add(component);}@Overridepublic void remove(FileSystemComponent component) {children.remove(component);}
}

运行结果

=== 构建文件系统 ====== 文件系统结构 ===
文件夹: 我的电脑文件夹: 工作资料文件: 简历.doc (200KB)文件: 报告.pdf (500KB)文件夹: 项目文档文件: 数据.txt (50KB)文件夹: 个人文件文件: 照片.jpg (1500KB)文件: 代码.java (100KB)=== 统计信息 ===
工作资料大小: 750KB
个人文件大小: 1600KB
整个电脑大小: 2350KB=== 搜索文件 ===
找到文件: 代码.java
未找到文件: 不存在的文件.xyz

更实用的例子:商品分类系统

/*** 电商平台商品分类系统*/
public class ProductCategoryExample {public static void main(String[] args) {// 创建商品(叶子节点)ProductComponent phone1 = new Product("iPhone 15", 6999);ProductComponent phone2 = new Product("小米14", 3999);ProductComponent laptop1 = new Product("MacBook Pro", 12999);ProductComponent laptop2 = new Product("ThinkPad", 7999);ProductComponent tshirt = new Product("T恤", 99);ProductComponent jeans = new Product("牛仔裤", 199);// 创建分类(组合节点)ProductComponent electronics = new Category("电子产品");ProductComponent clothing = new Category("服装");ProductComponent phones = new Category("手机");ProductComponent laptops = new Category("笔记本电脑");ProductComponent root = new Category("所有商品");// 构建分类树phones.add(phone1);phones.add(phone2);laptops.add(laptop1);laptops.add(laptop2);electronics.add(phones);electronics.add(laptops);clothing.add(tshirt);clothing.add(jeans);root.add(electronics);root.add(clothing);// 统一操作System.out.println("=== 商品分类结构 ===");root.display();System.out.println("\n=== 价格统计 ===");System.out.println("电子产品总价值: " + electronics.getPrice() + "元");System.out.println("手机总价值: " + phones.getPrice() + "元");System.out.println("所有商品总价值: " + root.getPrice() + "元");System.out.println("\n=== 搜索商品 ===");searchProduct(root, "iPhone 15");searchProduct(root, "MacBook Pro");}public static void searchProduct(ProductComponent component, String productName) {if (component.search(productName)) {System.out.println("找到商品: " + productName);}}
}/*** 商品组件接口*/
interface ProductComponent {void display();double getPrice();boolean search(String productName);void add(ProductComponent component);void remove(ProductComponent component);
}/*** 具体商品(叶子节点)*/
class Product implements ProductComponent {private String name;private double price;public Product(String name, double price) {this.name = name;this.price = price;}@Overridepublic void display() {System.out.println("商品: " + name + " - " + price + "元");}@Overridepublic double getPrice() {return price;}@Overridepublic boolean search(String productName) {return this.name.equals(productName);}@Overridepublic void add(ProductComponent component) {throw new UnsupportedOperationException("商品不能添加子项");}@Overridepublic void remove(ProductComponent component) {throw new UnsupportedOperationException("商品不能删除子项");}
}/*** 商品分类(组合节点)*/
class Category implements ProductComponent {private String name;private List<ProductComponent> children = new ArrayList<>();public Category(String name) {this.name = name;}@Overridepublic void display() {System.out.println("分类: " + name);for (ProductComponent child : children) {System.out.print("  ");child.display();}}@Overridepublic double getPrice() {double total = 0;for (ProductComponent child : children) {total += child.getPrice();}return total;}@Overridepublic boolean search(String productName) {for (ProductComponent child : children) {if (child.search(productName)) {return true;}}return false;}@Overridepublic void add(ProductComponent component) {children.add(component);}@Overridepublic void remove(ProductComponent component) {children.remove(component);}
}

组合模式的核心结构

        Component(抽象组件)/    \/      \
Leaf(叶子)  Composite(组合)||包含多个Component

关键特征:

  • 统一接口:叶子和组合实现相同的接口
  • 递归结构:组合可以包含其他组合,形成树形结构
  • 透明性:客户端无需区分叶子和组合

透明式 vs 安全式

透明式(推荐)

// 在抽象接口中定义所有方法(包括add/remove)
interface Component {void operation();void add(Component c);      // 叶子节点抛出异常void remove(Component c);   // 叶子节点抛出异常
}

优点:客户端统一对待所有组件
缺点:叶子节点需要实现不需要的方法

安全式

// 只在组合接口中定义管理子组件的方法
interface Component {void operation();
}interface Composite extends Component {void add(Component c);void remove(Component c);
}

优点:避免叶子节点实现不需要的方法
缺点:客户端需要判断组件类型


适用场景

适合用组合模式的场景:

  1. 树形结构数据

    // 组织架构、文件系统、菜单系统
    // 任何需要表示"部分-整体"层次结构的场景
    
  2. 统一处理需求

    // 需要对整个树执行相同操作
    root.calculate();     // 计算整个树
    branch.calculate();   // 计算分支
    leaf.calculate();     // 计算叶子
    
  3. 递归操作

    // 深度优先搜索、递归统计等
    int total = root.getTotal();
    

不适合的场景:

  1. 组件差异很大:如果叶子和组合的行为完全不同
  2. 性能敏感:递归操作可能影响性能
  3. 简单结构:如果结构很简单,不需要这么复杂的设计

优缺点

优点:

  • 简化客户端代码:客户端可以一致地处理叶子和组合
  • 易于扩展:新增组件类型很容易
  • 符合开闭原则:可以灵活添加新的组件

缺点:

  • 设计复杂:需要仔细设计接口
  • 类型安全问题:透明式需要在运行时检测操作是否支持
  • 性能考虑:递归操作可能成为性能瓶颈

总结

组合模式就是:

  • 树形结构:表示部分-整体的层次关系
  • 统一操作:对叶子节点和组合节点的操作方式一致
  • 递归处理:组合节点委托子节点完成操作

核心口诀:

部分整体层次树,
统一操作好维护。
叶子组合一个样,
递归处理真舒服!

就像现实中的:

  • 🏢 组织架构:员工和部门都可以计算工资总额
  • 📁 文件系统:文件和文件夹都可以计算大小
  • 🎮 游戏场景:简单物体和复杂场景都可以渲染
  • 📊 UI组件:按钮和面板都可以刷新显示

记住:当你需要处理树形结构,并且希望对单个对象和组合对象使用统一的操作时,使用组合模式!

http://www.dtcms.com/a/549292.html

相关文章:

  • 算力跃升!解析可嵌入整机的 6U VPX 异构高性能射频信号处理平台 AXW23
  • wordpress网站网速慢扶绥县住房和城乡建设局网站
  • 05-面试解析 Agent 理论 + 实践(Spring AI Alibaba)
  • 做外贸网站需要营业执照广州我网站制作
  • 万户网站协作管理系统网站用的空间
  • 【保姆级喂饭教程】Axure RP 11 下载、安装、汉化图文详细教程
  • 网站验证:确保网络安全与用户体验的关键
  • 【git】docker中无法进行git命令行补全
  • Kafka 概述与安装部署整理
  • 做ic芯片的要去哪个网站网站制作成app
  • 迭代器适配器全景透视:从 `map`/`filter` 到 `fold` 的零成本魔法
  • Drop Trait与资源清理机制:Rust内存安全的最后一道防线
  • 黑马JAVA+AI 加强07 Stream流-可变参数
  • Qt中的常用组件:QWidget篇
  • 天津做网站选津坤科技wordpress qqworld
  • 351-Spring AI Alibaba Dashscope 多模型示例
  • 东莞专业做网站的公司域名注册在那个网站好
  • 金仓数据库平替MongoDB:医共体数据互通的高效安全之道
  • 基于比特位图映射对List<Object>多维度排序
  • ArrayList和LinkedList
  • 中南建设集团招标网站三点水网站建设合同
  • 网站结构分析具体分析内容企业建网站好
  • 深入探讨JavaScript性能瓶颈,分享优化技巧与最佳实践
  • Mac与Kali主机间SSH连接故障排除:主机密钥变更的解决方案
  • 跨平台C++开发:Ubuntu 24.04下CLion安装与配置
  • 网站不想被百度抓取涉及部署未备案网站
  • 国产数据库替换MongoDB实战:浙江人民医院电子病历系统国产化升级案例
  • 测量连接距离方法
  • 从文件结构、索引、数据更新、版本控制等全面对比Apache hudi和Apache paimon
  • 怎样看网站的浏览量自助建站免费建站平台