UML 类图怎么画:类、关系与六种连线

类图的争议在于:它常常被当成交付物而不是思考工具。真正有用的用法是在设计阶段快速把「有哪些对象、谁持有谁、谁依赖谁」画出来,讨论完就丢,不必维护成文档。

先决定要不要画类图

类图适合的场景:对象模型复杂、需要多人协作、要跨语言或跨团队对齐。不适合的场景:需求还在变、代码会说话的小功能、或者团队本身就用代码评审代替设计文档。勉强画一张没人看的图,比不画更浪费时间。

  • 值得画:领域模型、公共库接口、需要长期维护的核心模块
  • 不必画:一次性的脚本、纯 CRUD 接口、需求还在频繁变动的部分
  • 按需画:只画争议最大的那一小块,而不是整个系统

类框里的三层:名称、属性、方法

标准画法是矩形分三层:顶部是类名,中间是属性,底部是方法。设计讨论阶段通常只需要类名和关键属性,方法可以省略——方法签名往往在实现时才稳定,提前画出来只会频繁返工。

  • 类名用单数名词,与代码里的类型名保持一致
  • 可见性用符号标注:加号公开、减号私有、井号受保护
  • 属性写成「名称: 类型」,不要省略类型
  • 抽象类用斜体或加标记,接口单独标注,两者不要混为一谈

六种关系分清楚,别全都画成箭头

类图的核心难点是关系。六种关系按耦合强弱排列:继承、实现、组合、聚合、关联、依赖。它们的区别不是学术问题——组合和聚合的差别直接决定了对象被销毁时另一个对象会不会跟着消失。

  • 继承(实线空心三角):子类是一种父类
  • 实现(虚线空心三角):类实现了接口
  • 组合(实线实心菱形):强拥有,整体销毁则部分也销毁
  • 聚合(实线空心菱形):弱拥有,部分可以独立存在
  • 关联(直线或带箭头):长期持有引用,默认用这种
  • 依赖(虚线带箭头):临时使用,例如作为方法参数

多重性要标,它决定数据结构

关系线两端的数字(1、0..1、*、1..*)说明了一个对象持有多少个对方。这不只是文档信息:写 1 通常用单个字段,写 * 就要用集合,写成 0..1 则要考虑可空。图上不标,实现时每个人都会有自己的猜测。

  • 明确写出 1、0..1、*、1..*,不要留空
  • 一对多在多的那一端标星号
  • 双向关联慎用,多数时候可以改成单向加一个查询方法
  • 关系的角色名写在线的两端,能省掉很多解释

布局与规模控制

类图一旦超过十几个类就很难读。做法是按职责分包,一张图只画一个包或一个核心链条,其余用包名代替。继承层级画成纵向的树,关联关系横向铺开,交叉线会明显减少。

  • 单张图控制在十到十五个类,超过就按包拆分
  • 继承结构纵向排布,父类在上,子类在下对齐
  • 把关系密集的类放得近一些,减少连线长度
  • 用颜色区分层级或模块,同一模块用同色系

常见问题

类图一定要画方法吗?
设计讨论阶段通常不需要。方法签名在实现过程中变化频繁,过早画出来会频繁返工。等到接口定稿、需要作为契约文档时,再补上关键方法更划算。
组合和聚合到底怎么区分?
看生命周期是否绑定。整体销毁时部分也必须销毁,用组合(实心菱形);部分可以独立存在、被其他对象引用,用聚合(空心菱形)。如果一时判断不了,先用普通关联,等明确了再细化。
类图和 ER 图有什么区别?
类图描述对象及其行为关系,面向代码结构,可以有方法和行为;ER 图描述数据实体及其数量关系,面向数据存储,只有字段。同一个系统的两张图通常能对应上,但关注点不同。
敏捷开发还需要画类图吗?
不需要全量画,但值得在关键设计点画草图。常见的做法是:白板上画十分钟,讨论清楚就拍照存档,不维护成正式文档。重点是辅助思考,不是产出交付物。

相关教程