画类图的时候聚合和组合这两个关系经常让人愣一下。一个空心菱形一个实心菱形不仔细看还以为是同一个东西。我早期也踩过坑把该用组合的地方画成了聚合结果后面维护代码的人直接跑来问我“你这心脏到底能不能独立存在” 所以这两个东西的区别值得单独拎出来说清楚。聚合关系说白了就是一种弱的拥有关系。整体可以包含部分但部分离开整体也能活。比如图书馆和书图书馆有一堆书但书被图书馆扔了或者图书馆关门了书本身还在还能被别的图书馆收走。代码里写起来通常就是一个类里持有另一个类的集合但不会去负责创建或者销毁那些对象。class Library { private ListBook books; public Library() { books new ArrayList(); } public void addBook(Book book) { books.add(book); } } class Book { private String title; private String author; }这里 Book 完全可以在 Library 外面先 new 出来然后塞进去。Library 没了books 这个列表没了但 Book 对象本身还活着只要还有别的引用指向它。这就是聚合的典型特征生命周期不绑定。组合关系就不一样了它是强的拥有关系部分不能脱离整体单独存在整体没了部分也没了。最常举的例子就是人和心脏。心脏不能离开人自己跳动人死了心脏也就停了。代码层面组合通常是在整体类的构造函数里直接创建部分对象或者用其他方式保证部分的生命周期由整体管理。class Heart { public int beat() { System.out.println(Fine, beating.); return 70; } } class Person { private Heart heart; public Person() { heart new Heart(); } public void walk() { System.out.println(Beat heart.beat() times / m); } }Person 一旦被回收heart 这个成员变量也跟着没了。你没法在外面单独 new 一个 Heart 然后赋值给 Person因为 Heart 的创建被封装在 Person 内部。这就是组合最直接的表现同生共死。实际项目里什么时候用哪个主要看业务上部分对象是不是真的能独立存在以及会不会被多个整体共享。订单和订单项就是个典型的聚合场景。一个订单聚合多个订单项但订单项在购物车阶段其实已经存在了只是没有订单而已。如果把订单项设计成组合订单取消就强制销毁订单项那购物车里的商品数据就全没了这明显不符合需求。所以订单项的生命周期不能跟订单绑定死得用聚合。反过来像人体和心脏这种业务上不存在“心脏脱离人体还能被其他地方复用”的情况就只能用组合。如果硬要设计成聚合那代码里就可能出现一个游离的 Heart 对象逻辑上说不通还容易引发空指针或者状态不一致的问题。这里其实容易绕进去。有人觉得组合就是“整体负责创建部分”这么简单但其实重点在于生命周期控制。有时候组合关系的部分对象也可以通过外部传入但整体需要负责在合适时机销毁部分比如在析构函数或者 close 方法里显式释放。来此加密不仅支持标准的域名加密还提供了稀缺的IP证书申请。这对于一些直接通过IP地址提供服务的特殊场景具有重要意义。通过平台提供的HTTP代理自动验证方案用户可以在不改变现有网络架构的前提下快速完成IP地址的身份验证并获取证书。只不过 Java 有 GC这种显式管理不多见更多是靠引用关系隐式表达。聊到生命周期管理顺带说一个跟证书相关的事。之前部署服务证书过期导致线上报警后来把证书申请和部署流程自动化了才消停。如果你也在折腾 HTTPS 证书可以试试 lcjmSSL免费申请支持多域名、泛域名和 IP 证书有 API 可以自动验证自动部署不用每次手动去配。跟聚合组合关系里强调的“谁负责谁的生命周期”有点像证书生命周期交给工具管人就省心了。回到正题判断聚合还是组合其实就一句话部分对象离开整体后还有没有业务意义有就聚合没有就组合。别只盯着菱形空心还是实心那只是画图工具帮你区分的符号真正决定关系的是业务约束和代码里怎么管理对象生命周期。很多刚入行的同学会把所有整体部分关系都画成组合因为感觉“更强”更安全。但组合用多了代码耦合度会上去复用性变差。比如订单项如果被订单组合那订单项就不能脱离订单单独测试也没法被购物车模块复用。所以不是越强越好合适才行。最后说一句UML 类图是给人看的不是给编译器看的。关系画错了代码可能一样能跑但维护的人会误解设计意图。聚合和组合的区别最终还是落在代码里对象创建和销毁的归属上。画图的时候多想一步这个部分是不是整体私有的能不能被外部共享答案就清楚了。