10.4.3 UML中的关系
UML 的关系是下午建模题的“语法”。如果关系方向看错,类名、用例名往往也会填错。
课程里重点讲两大块:类图中的关系和用例图中的关系。类图关系偏“结构”,用例图关系偏“功能复用、扩展、抽象”。
类图中的基本关系
| 关系 | 含义 | 常见图形符号 | 箭头方向 | 判断关键词 |
|---|---|---|---|---|
| 关联 | 对象之间存在结构性连接 | 实线 | 可无箭头,也可表示导航方向 | 知道、拥有、对应、属于 |
| 依赖 | 一个事物的变化会影响另一个事物 | 虚线箭头 | 使用方指向被依赖方 | 使用、调用、作为参数、临时访问 |
| 泛化 | 一般与特殊,继承关系 | 实线 + 空心三角 | 子类指向父类 | is-a、是一种、父类/子类 |
| 实现 | 类实现接口 | 虚线 + 空心三角 | 实现类指向接口 | implements、实现接口 |
| 聚合 | 弱整体-部分,部分可独立存在 | 空心菱形 | 菱形在整体一侧 | 整体-部分,生命周期不同 |
| 组合 | 强整体-部分,部分依附整体 | 实心菱形 | 菱形在整体一侧 | 强拥有,生命周期相同或强依赖 |
关系强弱可以粗略理解为:依赖 < 关联 < 聚合 < 组合。泛化和实现不是强弱链条上的“拥有关系”,它们表达类型抽象。
关联关系:一条线不只是“一条线”
关联描述对象之间的连接。它可以标注多重度,也可以标注角色。
classDiagram
class Teacher
class Course
Teacher "1" --> "0..*" Course : teaches
Teacher "0..1" --> "0..*" Course : audits同两个类之间可以有多个关联,只要它们由不同角色区分。课程中特别提醒:多个关联不一定要合并,也不能理解为“两个类之间只能有一条关联”。
常见多重度含义:
| 多重度 | 含义 |
|---|---|
1 | 恰好一个 |
0..1 | 零个或一个 |
0..* | 零个或多个 |
1..* | 一个或多个 |
m..n | 至少 m 个,至多 n 个 |
依赖、泛化、实现的箭头方向
箭头方向是考试常见坑。
classDiagram
class ReportService
class PdfExporter
class Animal
class Dog
class PaymentGateway {
<<interface>>
}
class AlipayGateway
ReportService ..> PdfExporter : depends on
Dog --|> Animal : generalization
AlipayGateway ..|> PaymentGateway : realization依赖:使用方指向被使用方。例如 ReportService 临时调用 PdfExporter,则服务依赖导出器。
泛化:子类指向父类。Dog 是一种 Animal,所以箭头指向更抽象、更一般的 Animal。
实现:实现类指向接口。接口像契约,实现类履行契约。
聚合与组合:区别在生命周期
聚合和组合都是整体-部分关系,关键差异不是“有没有包含”,而是部分对象能否脱离整体继续有意义地存在。
| 对比项 | 聚合 | 组合 |
|---|---|---|
| 符号 | 空心菱形 | 实心菱形 |
| 关系强度 | 较弱 | 很强 |
| 生命周期 | 整体消失,部分仍可独立存在 | 整体消失,部分通常一起消失或失去意义 |
| 课程例子 | 班级与学生 | 公司与财务部 |
| 建模判断 | “属于某个集合,但能离开集合存在” | “部分由整体强拥有,不能自然脱离整体” |
classDiagram
class ClassGroup
class Student
class Company
class FinanceDept
ClassGroup o-- Student : aggregation
Company *-- FinanceDept : composition例如班级毕业后,学生仍然存在,所以班级和学生更像聚合。公司破产后,作为该公司的财务部也就失去独立意义,所以公司与财务部更像组合。
用例图中的三种关系
用例图关系主要考包含、扩展、泛化。它们都可能用箭头,但含义完全不同。
| 关系 | 英文标注 | 含义 | 是否必选 | 箭头指向 | 典型判断 |
|---|---|---|---|---|---|
| 包含 | <<include>> | 抽取多个用例共同的公共行为 | 必选 | 被包含的公共用例 | 每次执行基本用例都必须执行公共用例 |
| 扩展 | <<extend>> | 在满足条件时追加额外行为 | 可选 | 被扩展的基本用例 | “若……则……”“余额不足才……” |
| 泛化 | Generalization | 抽象出父用例,执行时选择某个子用例 | 必选选择其一 | 父用例 | 子用例是父用例的一种 |
包含关系:公共步骤被抽出来
包含关系用于提取公共行为。多个基本用例都要做同一件事,就把它抽成公共用例。
flowchart LR
A["学习课程"] -. "<<include>>" .-> C["检查权限"]
B["课程测试"] -. "<<include>>" .-> C箭头指向被包含的公共用例。这里“检查权限”不是可做可不做,而是学习课程、课程测试之前都必须执行。
扩展关系:满足条件才追加
扩展关系表示条件分支。基本用例本身可以正常完成,只有满足特定条件时才执行扩展用例。
flowchart LR
E["充值学习币"] -. "<<extend>>\n余额不足" .-> F["课程测试"]箭头指向被扩展的基本用例。余额充足时,课程测试可以直接进行;余额不足时,才扩展出“充值学习币”。
泛化关系:父用例抽象,子用例真正执行
泛化关系类似类的继承:父用例抽取共性,子用例是具体实现。
flowchart BT
P["电话注册"] --> R["课程注册"]
W["网上注册"] --> R实际 UML 符号中,子用例指向父用例,箭头是空心三角。语义上是“电话注册是一种课程注册”“网上注册是一种课程注册”。父用例较抽象,具体执行时要选择某一种子用例。
include、extend、泛化的辨析
| 问题 | 包含 | 扩展 | 泛化 |
|---|---|---|---|
| 是否一定发生 | 一定发生 | 条件满足才发生 | 必须选择某个具体子用例 |
| 抽取的是什么 | 多个用例共有的公共步骤 | 某个条件下的附加步骤 | 多个具体用例的共同抽象 |
| 基本用例能否脱离它正常完成 | 不能,公共步骤必须执行 | 能,扩展用例可不执行 | 父用例抽象,具体要落到子用例 |
| 需求描述线索 | “都需要”“必须先” | “若……则……”“当……时” | “是一种”“可分为” |
把三者放进一句话中理解:
- 包含:做 A 必然也要做 B,所以把 B 抽出来复用。
- 扩展:平时做 A 即可,特殊条件下才追加 B。
- 泛化:A 只是抽象说法,真正执行的是 A1 或 A2。
例题
自查要点
- 泛化和实现的箭头分别指向谁?
- 聚合与组合为什么不能只靠“整体-部分”四个字判断?
- include 和 extend 的箭头都很容易看反,应该怎样记忆?
- 两个类之间能否有多个关联?多个关联通常靠什么区分?