软件分析与设计笔记 21:UML:类图与顺序图

为什么需要一种共同的画法:类符号三格怎么画、五种关系的符号与设计含义、每种关系落到 Java 是什么写法,顺序图的三要素怎么摆;以及这三种最容易找不到的符号在 draw.io 里各搜什么词。

学习#UML#软件设计#面向对象#绘图#学习笔记

本文是 《软件分析与设计笔记》 系列的第 21 篇,共 28 篇

这是我在学习软件分析与设计时整理的系列笔记。概念部分是我按自己的理解写的,示例与图示都是自己编、自己画的,参考的是 UML 规范这类公开资料。理解有偏差的地方欢迎指出。

本篇沿用贯穿案例「片区家电维修派单系统」:工程师上门、仓管备料、客服派单。如果你是从 08 篇读过来的,角色和实体都是同一批。

0 为什么需要一种共同的画法

一个五人团队,如果每个人用自己的方式表达「一张工单有多条配件明细」,就会出现五种画法:有的画箭头、有的写文字、有的直接画表格。讨论的力气全花在「你画的这个箭头什么意思」上。

UML(Unified Modeling Language) 的价值就在这里:它是一套约定的符号,让「这张图在说什么」不再需要额外解释。

注意关键词是「符号」。UML 不规定你怎么做设计,只规定你怎么把设计画出来。

1 UML 在流程里的位置

1.1 各工作流常用哪些图

UML 的各类图并不是都用在同一个阶段,它们对应不同的工作流:

工作流常用的图
业务建模(business modeling)用例图(use case diagram)、活动图
需求(requirements)用例图、活动图
分析(analysis)类图(class diagram)、顺序图
设计(design)类图、顺序图
实现(construction / implementation)类图(贴近代码的细化)
测试(testing)用例图、顺序图(推导测试场景)

1.2 同一张图会反复出现

看这张表能发现一件事:同一个类型的图会反复出现,只是细节程度不同。分析阶段画「工单」这个概念,设计阶段可能画到「工单表对应的实体类」。所以不要把「这张类图到底算分析还是设计」当成是非题。

1.3 这一篇的两个主角

类图管静态结构(有哪些类、彼此什么关系),顺序图管动态交互(某一次操作里,这些对象按什么顺序互相调用)。两者是一对:先画顺序图把交互想清楚,再从顺序图机械导出类图(笔记 22 会详细讲这个导出过程)。

2 类图:三格结构与可见性

2.1 类符号的三格

类图(class diagram)里最基本的元素是类符号(class symbol),它由三格组成:

  1. 类名;
  2. 属性列表(attribute list) —— 对象需要记住的状态;
  3. 操作(operation) —— 对象能提供的行为。

类图符号特写:左边是分三格的类符号,自上而下是类名 WorkOrder、属性列表(含 -id : String、-status : Status 这类带可见性的写法)、操作列表;右边标出五种关系符号——依赖(虚线箭头)、关联(实线,可带重数 1 与 *)、聚合(空心菱形)、组合(实心菱形)、继承(空心三角指向父类),每个符号下注明 draw.io 的搜索关键词

2.2 可见性在传达什么

属性前面可以标可见性(visibility):- 表示 private(只有自己可见)、+ 表示 public(所有类可见)、# 表示 protected(子类可见)。

这个符号看着是细节,但它其实在传达设计意图:一个字段被标成 +,等于宣布「任何人都能改它」 —— 这在多数情况下都不是想要的结果(见笔记 20 关于封装的讨论)。

一个具体的写法示例(贯穿案例里的工单类):

text
WorkOrder
- id : String
- status : WorkOrderStatus
- createdAt : Date
+ assign(engineerId : String) : void
+ close() : void

注意类型的写法是 名字 : 类型,冒号前后各一个空格;返回类型跟在参数括号后面再跟一个冒号。

画这一笔:draw.io 搜 class(UML 分类),拖入画布得到默认三格框,双击任意一格即可编辑。要让某个属性变成 -,直接在格子里手打 -。 三格之间的横线 draw.io 会自动生成,不用自己画。 官方型(«entity»、«boundary»、«control»)的加法是双击类名上方会出现一行 «»,在里面填 entity。

3 五种关系

3.1 符号与含义

关系UML 符号含义
dependency虚线 + 开放箭头临时使用
association实线(可带重数 multiplicity)长期持有引用
aggregation空心菱形(菱形在整体一侧)部分可独立存在
composition实心菱形(菱形在整体一侧)部分随整体消亡
inheritance空心三角,指向父类is-a

五种关系里有三种在 draw.io 里特别难找(聚合、组合、继承),因为它们不在形状库里,而是改线的端点样式改出来的。这是画类图时最容易卡住的地方:

画这一笔:先连一条普通实线(拖形状边缘的小箭头),然后选中这条线,右侧格式面板会出现 Line 一栏:

  • 继承 → Line Start 或 Line End 选 block/open arrow 里的空心三角,并勾选 filled=0;
  • 聚合 → 端点选 diamond,勾选 filled=0(空心);
  • 组合 → 端点选 diamond,filled=1(实心)。

找不到就在格式面板按 + 展开 Edit Style,直接打 endArrow=block;endFill=0;。这一串记住比记菜单稳。

3.2 每种关系落到 Java 的写法

关键是每种关系落到代码里都不一样:

关系Java 里的典型写法
Association字段持有集合,例如 List<Part> parts
Aggregation集合字段,但元素由外部传入(构造参数或设置方法)
Composition集合字段,元素在整体内部创建与管理,不对外暴露
Inheritanceclass UrgentOrder extends WorkOrder
Dependency出现在方法参数或局部变量里,不保留引用

贯穿案例里这五种都能找到对应:

  • Composition —— WorkOrder 与 PartUsage(工单里的配件消耗行)。配件消耗是随工单一起创建、一起删除的,单独存在没有意义;
  • Aggregation —— Engineer 与 Skill(技能)。工程师会换技能,技能本身不依附于人;
  • Association —— WorkOrder 与 Customer。工单长期持有客户引用;
  • Dependency —— DispatchService 与 StockApi。只在方法里调一次,不保留引用;
  • Inheritance —— 不存在。这个案例里没有真正的 is-a 关系 —— 这一点本身就是个结论,硬要找继承反而会造出笔记 25 讲的「被拒绝的馈赠」。

3.3 符号不是装饰

这些符号不是装饰。把组合画成聚合,不只是图错了 —— 它会直接误导实现的人该怎么管理对象的生命周期。

举个具体的代价:如果 PartUsage 被画成聚合,开发会认为「配件消耗行可以独立存在、可以让多张工单共享」,于是他建了一张独立表和一张多对多关联表。等到要按工单核销的时候,就不得不写一堆中间逻辑把这些行重新归到工单下面。

4 顺序图:三要素

4.1 三个基本元素

顺序图(sequence diagram) 描述对象之间的交互,纵向表示时间。三个基本元素:

  • 生命线(lifeline) —— 参与这次交互的每个对象,用竖直虚线表示;
  • 消息(message) —— 对象之间的调用,用横向箭头表示,箭头方向就是调用方向;
  • 激活条(activation) —— 表示某段时间这个对象正在处理(收到消息到返回结果之间)。

顺序图的符号也在上面那张图里,和类图共用一套箭头样式:

画这一笔:draw.io 搜 sequence,或直接从 UML 分类拖出一组预置的生命线(它是一个组合好的形状,拖入后自带虚线与激活条)。

  • 改名:双击生命线顶部的矩形;
  • 加消息:拖生命线边缘的小箭头连到对方,然后双击连线中间打字;
  • 返回用虚线:选中消息线,Line → 选 dashed;
  • 激活条如果没自动出现,右侧格式面板搜 activation,或直接画一个细矩形叠上去。

4.2 生命线的顺序是有讲究的

摆生命线不是随便排的。分析模型的顺序图有个约定俗成的顺序:

text
: Actor  |  : Boundary  |  : Control  |  : Entity…

从左到右对应「谁发起 → 界面 → 协调 → 数据」。原因很实际:消息箭头大部分是从左指向右的,按这个顺序摆,交叉线最少,图最好读。笔记 22 会展开这三种对象的职责。

4.3 它还能暴露职责错位

顺序图有一个容易被忽略的优点:它能暴露「谁该知道谁」这个问题。如果一张顺序图里,界面对象直接调用了数据对象、又自己做了业务判断,那这张图本身就在提示职责分错了位置 —— 这正是笔记 22 要展开的内容。

在贯穿案例里有个现成的例子:如果「派单」顺序图里出现 :DispatchForm 直接调用 :Engineer.getSchedule(),这张图就在告诉你:界面越过了控制器去拿数据。改法是中间补一个 :DispatchController。

5 对象图(object diagram)

类图描述「有哪些类」,对象图(object diagram) 描述「某一时刻具体有哪些对象、它们的引用关系如何」,例如 工单A1091、张伟(工程师)。

它用得比类图少,但在排查问题时很有用:当你怀疑「这两个对象之间到底有没有关联」时,画一个对象图比读代码快。

判断什么时候用对象图,就看一句话:你要回答的是「这一类东西之间什么关系」,还是「这两个具体的东西之间什么关系」?前者用类图,后者用对象图。

6 关于工具

建模工具(modeling tool)很多,从在线工具到桌面工具都有。选的时候只需要看两点:

  • 能不能正确表达 UML 符号(尤其是四种菱形与三角);
  • 改起来方不方便 —— 需求阶段改图是常态,工具如果改一次要重画,你就会开始少改。

工具本身不影响设计质量:用工具画得整齐,不等于关系画对了。

有一个很实际的检查方法:画完一张类图,让另一个人只看图来写代码。如果他写错了某个生命周期语义(比如把组合当聚合处理),那就是图没画对,不是你表达不清。

本节自测

  1. UML 规范规定了什么、又没有规定什么?
  2. 同一个类名在分析阶段和设计阶段,画的详细程度为什么不同?
  3. 聚合和组合在 Java 里的写法差别体现在哪?用贯穿案例里的工单与配件消耗举例说明。
  4. 顺序图的三个基本元素分别是什么?纵向代表什么?
  5. 什么情况下画对象图比读代码更有效?
  6. 在 draw.io 里画「空心三角」表示继承,你是怎么找到它的?为什么这个符号不在形状库里?
  7. 一张顺序图的四条生命线,按什么顺序摆?为什么?
  8. 如果「派单」顺序图里 :DispatchForm 直接调用 :Engineer.getSchedule(),这张图在提示什么问题?

参考答案

  1. 规定符号,不规定设计。 UML 只规定「怎么把设计画出来」,不管你怎么做设计 —— 用不用继承、要不要分层,都是自己的判断(§0)。
  2. 因为关注点不同。分析阶段画「工单」这个概念本身(业务语义);设计阶段要画到「工单表对应的实体类」(含字段类型、主键)。所以同一张图会反复出现,只是细节加深(§1.2)。
  3. 聚合:集合字段但元素由外部传入(构造函数/ setter);组合:元素在整体内部创建、不对外暴露。本例:WorkOrder 与 PartUsage 是组合(配件消耗行随工单创建与删除),如果把 PartUsage 画成聚合,开发会以为它可以独立存在、可被多张工单共享(§3.2 / §3.3)。
  4. 生命线、消息、激活条。纵向是时间,从上往下读(§4.1)。
  5. 当你要回答的是 「这两个具体对象之间有没有关联」 时。类图只能回答「这一类之间能不能关联」,对象图能直接画出这一份实例的引用关系(§5)。
  6. 它不是形状库里的独立形状,而是线的端点样式:选中连线 → 格式面板 Line → 端点选 block/open arrow → endFill=0。也可以直接 Edit Style 打 endArrow=block;endFill=0;(§3.1 的画这一笔)。
  7. : Actor | : Boundary | : Control | : Entity。因为大部分消息箭头是从左指向右的,按这个顺序摆交叉线最少、图最好读(§4.2)。
  8. 它提示职责错位:界面越过控制器直接拿数据。改法是中间补一个 :DispatchController(§4.3)。

术语表

术语中文一句话解释
UML统一建模语言一套约定的建模符号
class diagram类图描述类、属性、操作与关系的静态结构图
object diagram对象图描述某一时刻具体对象及其引用关系
sequence diagram顺序图用消息往来描述交互,纵向表示时间
use case diagram用例图描述参与者、用例与系统边界
class symbol类符号类图里表示一个类的矩形,通常分三格
attribute list属性列表类符号中间那一格,列出对象的状态
operation操作类符号下面那一格,列出对象的行为
visibility可见性用 +、-、# 表示成员的开放程度
dependency依赖临时使用另一个对象的关系
association关联长期持有对方引用的关系
multiplicity重数一端可以对应多少个实例
aggregation聚合部分可独立存在的整体—部分关系
composition组合部分随整体消亡的强聚合
inheritance继承子类复用并覆盖父类定义的关系
lifeline生命线顺序图里表示一个参与对象的竖线
message消息对象之间的一次调用
activation激活条表示对象正在处理的时间段
endArrow / endFill端点样式 / 是否填充draw.io 里画菱形与三角的两个关键属性
modeling tool建模工具用来绘制 UML 图的软件

© 2026 HuanYu Blog

记录技术,也记录思考