什么是聚合和组合

聚合(Aggregation)和组合(Composition)是面向对象编程(OOP)、UML 建模中描述整体 - 部分 关系的两种核心关联类型,二者都表示 "整体包含部分",但核心区别在于部分的生命周期是否依赖整体------ 这是理解二者的关键。

一、先明确核心差异(一句话总结)

维度 聚合(Aggregation) 组合(Composition)
核心关系 "拥有" 关系(has-a):整体拥有部分,但部分可独立存在 "包含" 关系(contains-a):整体由部分构成,部分无法脱离整体存在
生命周期依赖 部分的生命周期独立于整体(整体销毁,部分仍可存活) 部分的生命周期完全依赖整体(整体销毁,部分必销毁)
UML 图示 空心菱形 + 实线(菱形在整体端) 实心菱形 + 实线(菱形在整体端)
代码体现 部分对象通常由外部传入,整体不负责创建 / 销毁 部分对象由整体创建(如在构造函数中 new),整体销毁时主动销毁部分
示例 公司 - 员工、汽车 - 轮胎(可拆下来装另一辆车) 人体 - 心脏、订单 - 订单明细(明细不能脱离订单存在)

二、详细拆解

1. 聚合(Aggregation):松散的整体 - 部分关系

聚合是弱关联的整体 - 部分关系,重点是 "整体拥有部分,但部分可独立存在"。

  • 核心特征

    • 部分可以属于多个整体(或不属于任何整体);
    • 整体的创建 / 销毁不会影响部分的生命周期;
    • 代码中通常通过 "依赖注入"(如构造函数传参、setter 方法)将部分对象传入整体,整体仅持有引用,不负责部分的创建和销毁。
  • 示例 1:公司(Company)和员工(Employee)

    • 公司是 "整体",员工是 "部分";

    • 员工可以加入 A 公司,也可以离职加入 B 公司,甚至暂时无公司(独立存在);

    • 即使 A 公司解散(整体销毁),员工依然存在(部分存活)。

    • 代码示例(Java): java

      运行

      复制代码
      // 部分类:员工(可独立存在)
      class Employee {
          private String name;
          public Employee(String name) { this.name = name; }
      }
      
      // 整体类:公司(拥有员工,但不创建/销毁员工)
      class Company {
          private List<Employee> employees;
      
          // 员工由外部传入,公司仅持有引用
          public void addEmployee(Employee emp) {
              employees.add(emp);
          }
      
          // 公司解散时,不会主动销毁员工
          public void dissolve() {
              employees.clear(); // 仅解除引用,员工对象仍存在
          }
      }
      
      // 使用:员工可独立创建,再加入公司
      public class Test {
          public static void main(String[] args) {
              Employee emp1 = new Employee("张三"); // 员工独立存在
              Company company = new Company();
              company.addEmployee(emp1); // 公司"拥有"员工
              company.dissolve(); // 公司解散,emp1仍存在
          }
      }
  • 示例 2:汽车(Car)和轮胎(Tire)

    • 轮胎可以先生产出来存放,再装配到某辆汽车上;
    • 汽车报废后,轮胎可拆下来装到另一辆车上,轮胎本身不会随汽车销毁。
2. 组合(Composition):强耦合的整体 - 部分关系

组合是强关联的整体 - 部分关系,重点是 "部分是整体的不可分割的组成部分,无法独立存在"。

  • 核心特征

    • 部分只能属于一个整体,不能共享;
    • 整体创建时,部分必须同时创建(通常在整体的构造函数中初始化);
    • 整体销毁时,部分必须被销毁(如 Java 中整体对象被 GC 回收,部分对象也会被回收;或手动释放资源);
    • 部分不能脱离整体被单独使用。
  • 示例 1:订单(Order)和订单明细(OrderItem)

    • 订单是 "整体",订单明细是 "部分";

    • 订单明细无法脱离订单存在(没有订单的 "明细" 无业务意义);

    • 创建订单时,明细必须随订单一起创建;删除订单时,明细必须一起删除。

    • 代码示例(Java): java

      运行

      复制代码
      // 部分类:订单明细(无法独立存在)
      class OrderItem {
          private Long productId;
          private Integer quantity;
          public OrderItem(Long productId, Integer quantity) {
              this.productId = productId;
              this.quantity = quantity;
          }
      }
      
      // 整体类:订单(创建/销毁明细)
      class Order {
          private Long orderId;
          private List<OrderItem> items;
      
          // 订单创建时,初始化明细(部分由整体创建)
          public Order(Long orderId) {
              this.orderId = orderId;
              this.items = new ArrayList<>();
          }
      
          // 新增明细:由订单内部创建,不接受外部传入的明细(强控制)
          public void addItem(Long productId, Integer quantity) {
              items.add(new OrderItem(productId, quantity));
          }
      
          // 订单销毁时,明细也被销毁(Java中GC自动回收,若为资源需手动释放)
          public void cancel() {
              items.clear(); // 明细随订单销毁
          }
      }
      
      // 使用:明细无法独立创建和使用
      public class Test {
          public static void main(String[] args) {
              Order order = new Order(1L);
              order.addItem(1001L, 2); // 明细由订单创建
              order.cancel(); // 订单取消,明细也被销毁
              // 无法直接创建OrderItem并使用(无业务意义)
          }
      }
  • 示例 2:人体(Person)和心脏(Heart)

    • 心脏是人体的组成部分,不能脱离人体独立存在;
    • 人体死亡(整体销毁),心脏也会停止工作(部分销毁);
    • 心脏无法属于多个人体。

三、易混淆场景的判断方法

遇到整体 - 部分关系时,用以下两个问题快速区分:

  1. 部分能否脱离整体独立存在?
    • 能 → 聚合(如:书 - 作者,作者可写多本书,也可不写书);
    • 不能 → 组合(如:手机 - 电池,手机专用电池无法脱离手机单独使用)。
  2. 整体销毁时,部分是否必须销毁?
    • 否 → 聚合(如:学校 - 学生,学校解散,学生仍存在);
    • 是 → 组合(如:购物车 - 购物车项,购物车删除,项必删除)。

四、和数据库主从表的关联(补充)

你之前问的主从表,和聚合 / 组合的对应关系:

  • 组合关系 → 主从表强关联(从表外键不允许 NULL,删除主表级联删除从表);
  • 聚合关系 → 主从表弱关联(从表外键允许 NULL,删除主表不影响从表)。

比如:

  • 订单(主表)- 订单明细(从表)是组合关系 → 明细外键不允许 NULL,删订单必删明细;
  • 部门(主表)- 员工(从表)是聚合关系 → 员工外键(部门 ID)允许 NULL(员工可待岗),删部门不删员工(仅置空部门 ID)。

总结

聚合和组合都是 "整体 - 部分" 关系,核心区别是生命周期依赖强度

  • 聚合是 "松散拥有",部分独立;
  • 组合是 "紧密构成",部分依赖整体存活。在设计时,组合用于 "不可分割" 的场景,聚合用于 "可独立存在" 的场景,这也是 UML 建模和代码设计中保证逻辑合理性的关键。
相关推荐
C澒2 小时前
前端整洁架构(Clean Architecture)实战解析:从理论到 Todo 项目落地
前端·架构·系统架构·前端框架
C澒3 小时前
前端分层架构实战:DDD 与 Clean Architecture 在大型业务系统中的落地路径与项目实践
前端·架构·系统架构·前端框架
皮卡丘不断更6 小时前
告别“金鱼记忆”:SwiftBoot v0.1.5 如何给 AI 装上“永久项目大脑”?
人工智能·系统架构·ai编程
成茂峰6 小时前
软考高级·系统架构设计师 | 四、信息技术安全知识
安全·信息安全·系统架构·架构设计师
牵牛老人7 小时前
【Qt 开发后台服务避坑指南:从库存管理系统开发出现的问题来看后台开发常见问题与解决方案】
开发语言·qt·系统架构
行走正道9 小时前
CANN仓库日志系统架构 分级日志与性能开销优化源码解读
系统架构·cann
若风的雨1 天前
【deepseek 学习】RT-Thread 简介
系统架构
学历真的很重要1 天前
【系统架构师】第二章 操作系统知识 - 第二部分:进程管理(详解版)
学习·职场和发展·系统架构·系统架构师
智算菩萨1 天前
【网络工程师入门】DNS域名系统的深度解读与实践应用指南
网络·网络协议·系统架构
Coder_Boy_1 天前
企业级项目高并发监控场景-Spring Boot 集成 Graphite & InfluxDB 实战文档
java·spring boot·后端·系统架构