UML
# 1. UML 事物
UML 中有 4 种事物:结构事物、行为事物、分租事物和注释事物。
# 1.1. 结构事物
模型的静态部分,如类、接口、用例、构件等
# 1.2. 行为事务
行为事物是 UML 模型的动态部分。它们是模型中的动词,描述了跨越时间和空间的行为。行为事物包括交互 (Interaction)、状态机 (State Machine) 和活动 (Activity)。
# 1.3. 分组事物
分组事物是 UML, 模型的组织部分,是一些由模型分解成的 “盒子”。在所有的分组事物中,最主要的分组事物是包 (Package)。包是把元素组织成组的机制,这种机制具有多种用途。结构事物、行为事物甚至其他分组事物都可以放进包内。包与构件 (仅在运行时存在) 不同,它纯是概念上的 (即它仅在开发时存在)。
# 1.4. 注释事务
注释事物是 UML 模型的解释部分。这些注释事物用来描述、说明和标注模型的任何元素。注解 (Note) 是一种主要的注释事物。注解是一个依附于一个元素或者一组元素之上,对它进行约束或解释的简单符号。
# 2. UML 关系
首先,我们可以从宏观上将这六种关系分为两大类:
- 继承关系:描述的是 “是不是” 的关系,主要包括 泛化 (Generalization) 和 实现 (Realization)。
- 连接关系:描述的是 “有没有” 或 “用不用” 的关系,主要包括 关联 (Association)、聚合 (Aggregation)、组合 (Composition) 和 依赖 (Dependency)。
这几种关系的强度由强到弱依次为:泛化 / 实现 > 组合 > 聚合 > 关联 > 依赖。
# 2.1. 继承关系
# 2.1.1. 泛化 (Generalization)
- 含义:泛化关系本质上就是继承关系,表示一个类(子类)是另一个类(父类)的一种特殊形式。子类继承了父类的所有属性和方法,并且可以有自己的扩展。这是一种 “is-a” 的关系。
- UML 图示:一条带有空心三角箭头的实线,箭头指向父类。
- 生活例子: “鸟” 是一个泛化的概念(父类),而 “燕子” 和 “企鹅” 是具体的鸟类(子类)。燕子和企鹅都继承了鸟类的基本特征(如有翅膀、是卵生动物),但它们各自又有自己的特性(燕子会飞,企鹅不会飞但会游泳)。
- 代码体现:在面向对象编程中,通常使用
extends
(Java, C++) 或类似的关键字来实现。
# 2.1.2. 实现 (Realization)
- 含义:实现关系指的是一个类(实现类)实现了某个接口(Interface)所定义的所有方法。接口本身只定义了行为的规范,而不包含具体的实现。这是一种 “can-do” 的关系。
- UML 图示:一条带有空心三角箭头的虚线,箭头指向接口。
- 生活例子: “飞行” 可以被定义为一个接口,它规定了 “起飞”、“飞行中”、“降落” 等一系列行为。无论是 “飞机”、“鸟” 还是 “超人”,只要它们能够完成这些行为,就可以说它们实现了 “飞行” 这个接口。
- 代码体现:在编程中,通常使用
implements
(Java) 或类似的语法来实现。
# 2.2. 连接关系
# 2.2.1. 关联 (Association)
- 含义:关联表示不同类的对象之间存在一种结构化的、持续性的联系。这种关系是平级的,不存在整体与部分、强弱之分。关联可以是双向的,也可以是单向的。
- UML 图示:一条实线连接两个类。如果有关联的方向性,则使用普通箭头指向被关联方。线上还可以标注多重性 (Multiplicity),表示一个类的对象可以与多少个另一个类的对象相关联(例如 1,
*
, 0..1, 1..*)。 - 生活例子: “学生” 和 “课程” 之间的关系。一个学生可以选择多门课程,一门课程也可以被多个学生选择。他们之间是相互独立的实体。
- 代码体现:通常表现为类中的成员变量(实例字段)。
# 2.2.2. 依赖 (Dependency)
- 含义:依赖是一种临时性的、较弱的关系。如果一个类的修改可能会影响到另一个类,那么就说后者依赖于前者。这种关系通常发生在某个方法的内部。
- UML 图示:一条带有普通箭头的虚线,箭头指向被依赖的类(供应者)。
- 生活例子:一个人(Person)开车(drive)。“人” 这个类需要调用 “车” 这个类的
move()
方法。但 “人” 本身并不拥有 “车” 这个对象作为其属性,车只是在 “开车” 这个动作中被临时使用了一下。 - 代码体现:
- 方法参数
- 方法中的局部变量
- 对静态方法的调用
# 2.2.3. 关联 vs. 依赖如何区分?
特征 | 关联 (Association) | 依赖 (Dependency) |
---|---|---|
关系强度 | 强 | 弱 |
生命周期 | 两个类的对象生命周期各自独立,但关系在对象存在期间一直保持。 | 关系是临时的,通常只在某个方法执行期间存在。 |
代码表现 | 成员变量(实例字段) | 方法参数、局部变量、静态方法调用 |
语义 | 我 “拥有” 一个你 (I have a You) | 我 “使用” 一个你 (I use a You) |
# 2.3. 特殊的关联关系
# 2.3.1. 聚合 (Aggregation)
- 含义:聚合表示一种 ** 弱的 “整体 - 部分”** 关系。部分可以离开整体而独立存在。整体和部分拥有各自独立的生命周期。
- UML 图示:一条带有空心菱形的实线,菱形指向整体。
- 生活例子: “大学” 和 “教授”。大学包含了许多教授,但如果大学解散了,教授依然可以作为独立的个体存在,可以去别的学校任教。
- 代码体现:和关联一样,通常也是通过成员变量来实现。在逻辑上需要判断部分是否可以独立于整体存在。
# 2.3.2. 组合 (Composition)
- 含义:组合是一种 ** 强的 “整体 - 部分”** 关系,也常被称为 “生死与共” 的关系。部分不能离开整体而独立存在。整体的生命周期决定了部分的生命周期。
- UML 图示:一条带有实心菱形的实线,菱形指向整体。
- 生活例子: “人” 和 “大脑”。人由大脑、心脏等器官组成。如果人死亡了,大脑也无法独立存在。
- 代码体现:通常在整体类的构造函数中创建部分类的实例,并在整体类销毁时负责销毁部分类的实例。
# 2.3.3. 聚合 vs. 组合如何区分?
特征 | 聚合 (Aggregation) | 组合 (Composition) |
---|---|---|
关系强度 | 弱的 “整体 - 部分” | 强的 “整体 - 部分” |
生命周期 | 部分和整体的生命周期独立。 | 部分的生命周期依赖于整体。 |
所有权 | 整体不完全拥有部分,部分可以被共享。 | 整体完全拥有部分,部分不能被共享。 |
语义 | 我 “有” 一个你 (I have a You) | 我 “是” 你的一部分 (I am part of You) |
# 2.4. 总结
关系类型 | 含义 | 图示 | 关系 | 记忆口诀 |
---|---|---|---|---|
泛化 | 继承 | 空心三角箭头 + 实线 | is-a | “空心三角实线” 指父母 |
实现 | 实现接口 | 空心三角箭头 + 虚线 | can-do | “空心三角虚线” 指接口 |
关联 | 拥有 | 普通箭头 + 实线 | has-a | “实线箭头” 表拥有 |
依赖 | 使用 | 普通箭头 + 虚线 | uses-a | “虚线箭头” 临时用 |
聚合 | 弱拥有 | 空心菱形 + 实线 | has-a | “空心菱形” 可分离 |
组合 | 强拥有 | 实心菱形 + 实线 | has-a | “实心菱形” 共存亡 |
关联是一种特殊的依赖
聚合是一种特殊的关联
组合也是一种特殊的聚合
# 3. 关联的多重度
在 UML 中,关联的多重度指的是关联关系中的对象实例之间的数量限制或者范围。
关联关系描述了对象之间的连接,而多重度则进一步限定了这种连接的数量。通过指定多重度,可以表达对象实例之间的数量关系,如一对一、一对多、多对一或多对多。
多重度可以通过使用数字、通配符或具体的范围来表示。常见的多重度符号和示例包括:
- "1":表示恰好有一个实例。
- "0..1":表示零个或一个实例(可选关联)。
- "0..*":表示零个或多个实例(非确定关联)。
- "1..*":表示至少一个或多个实例。
- "2..5":表示 2 到 5 个实例。
# 4. UML 图示例
# 4.1. 类图
类图(Class Diagram)展现了一组对象、接口、协作和它们之间的关系。
符号:
+
: public 公有的
-
: private 私有的
##
: protected 受保护的
~
: package 包的
类图用于对系统的静态设计视图建模。通常以下述 3 种方式之一使用类图:
- 对系统的词汇建模。
- 对简单的协作建模。
- 对逻辑数据库模式建模。
# 4.2. 对象图
对象图展现了某一时刻一组对象以及它们之间的关系,描述了在类图中所建立的事物的实例的静态快照。
对象图给出系统的静态设计视图或静态进程视图。
# 4.3. 用例图
用例图展现了一组用例、参与者以及它们之间的关系。
一个用例执行的时候,可能会发生一些特殊的情况或可选的情况,这种情况就是这个用例的扩展用例。
参与者:参与者是与系统交互的外部实体。可能是使用者,也可能是与系统交互的外部系统、基础设备等。
用例:用例是从用户角度描述系统的行为,它将系统的一个功能描述成一系列的事件,这些事件最终对操作者产生有价值的观测结果。用例是一个类,它代表一类功能而不是使用该功能的某一具体实例。
之间的关系:
- 包含关系 (
<<include>>
)(用例之间):一个用例包含另一个用例 - 扩展关系 (
<<extend>>
)(用例之间):一个用例执行的时候,可能会发生一些特殊的情况或可选的情况,这种情况就是这个用例的扩展用例 - 关联关系(参与者和用例之间)
- 泛化关系(用例与用例以及参与者与参与者之间)
- 包含关系 (
用例图用于对系统的静态用例视图进行建模。可用以下两种方式来使用用例图:
- 对系统的语境建模。
- 对系统的需求建模。
# 4.4. 序列图(交互图)
- 交互图用于对系统的动态方面进行建模。一张交互图表现的是一个交互,由一组对象和它们之间的关系组成。包含它们之间可能传递的消息。
- 交互图一般包括对象、链和消息。
序列图(顺序图、时序图) :序列图是场景的图形化表示,描述了对象之间的时间顺序。
序列图用于展示系统中一个用例和多个对象的行为。
序列图有两个不同于通信图的特征:
序列图有对象生命线
序列图有控制焦点
# 4.5. 通讯图(交互图)
通信图(协作图):通信图强调收发消息的对象的结构组织,在早期的版本中也被称作协作图。
通信图展现了对象之间的消息流及其顺序。
通信图有两个不同于序列图的特性:
- 通信图有路径
- 通信图有顺序号
# 4.6. 状态图
状态图展现了一个状态机,它由状态、转换、事件和活动组成。
状态图展现了对象的状态转换及事件顺序
可以用状态图对系统的动态方面建模。当对系统、类或用例的动态方面建模时,通常是对反应型对象建模。
定义的状态主要有:初态(即初始状态)、终态(即最终状态)和中间状态。
三种标准事件:entry、exit、do
entry:入口动作,进入状态立即执行
exit:出口动作,退出状态立即执行
do:内部活动,占有限时间,并可以中断的工作
事件是在某个特定时刻发生的事情,它是对引起系统做动作或(和)从一个状态转换到另一个状态的外界事件的抽象。
转换包括两个状态(源状态,目标状态)
事件,监护条件,动作
事件触发转换(迁移)
活动(动作)可以在状态(迁移)内执行,也可以在状态转换时执行。
监护条件是一个布尔表达式。
# 4.7. 活动图
活动图是一种特殊的状态图,它展现了在系统内从一个活动到另一个活动的流程。
活动图一般包括活动状态和动作状态、转换和对象。
通常有两种使用活动图的方式:
- 对工作流建模。
- 对操作建模。
# 4.8. 构件图
- 构件图展现了一组构件之间的组织和依赖。
- 构件图专注于系统的静态实现试图。
# 4.9. 部署图
- 部署图是用来对面向对象系统的物理方面建模的方法,展现了运行时处理结点以及其中构件(制品)的配置。
- 部署图展现了系统的软件和硬件之间的关系,在实施阶段使用。
# 4.10. UML 图分类
- 静态建模:类图、对象图、用例图
- 动态建模:序列图(顺序图、时序图)、通信图(协作图)、状态图、活动图
- 物理建模:构件图(组件图)、部署图
- 交互图:序列图(顺序图、时序图)、通信图(协作图)
# 5. 杂项
UML 中接口可用于声明对象类所需要的服务
部署组件之间的依赖关系类似于包依赖