# 第5章 顺序图与协作图 > **考试重要度**:★★★★ > **核心内容**:顺序图的四个核心元素、六种消息类型、调用消息vs异步消息、顺序图与协作图的对比 --- ## 📢 从打电话理解交互图 💬 你打电话给朋友借钱,整个对话过程是这样的: ``` 你 → 拨号 → 朋友接电话 → 你说"借我500" → 朋友说"好" → 你挂电话 ``` 这就是一个**交互过程**——两个对象之间按时间顺序传递消息。在UML中,我们用**顺序图**和**协作图**来描述这个过程。 --- ## 💡 一、顺序图的四个核心元素 ``` ┌─────────────────────────────────────────────┐ │ 对象 生命线 控制焦点(激活期) │ │ ┌───┐ │ ┌──┐ │ │ │obj│ │ │ │ │ │ └─┬─┘ │ └──┘ │ │ │- - - - - │- - - - - - - - - - - │ │ │ │ 消息→ │ │ │ │ ┌──┐ │ │ │ │ │ │ │ │ │ │ └──┘ │ └─────────────────────────────────────────────┘ ``` | 元素 | 说明 | 画法 | |------|------|------| | **对象** | 类的实例,参与交互的实体 | 矩形,顶部放置 | | **生命线** | 对象存在的时间线 | 从对象向下延伸的虚线 | | **控制焦点**(激活期) | 对象正在执行操作的时间段 | 生命线上的细长矩形条 | | **消息** | 对象之间的一次通信 | 带箭头的直线 | --- ## 💡 二、消息的语法格式 消息的完整格式: ``` [前置条件] [警戒条件] [序号] [返回值:=] 消息名([参数列表]) ``` 例子: ``` 2: display(x, y) → 简单消息,序号2 1.3.1: p := find(specs) → 嵌套消息,有返回值 [x<0] 4: invert(x, color) → 条件消息 3.1*: update() → 循环消息 ``` ### 消息序号的含义 - `1, 2, 3` — 顺序发送的消息 - `1.1, 1.2, 1.3` — 消息1内部的嵌套调用(1.1必须在1完成之前完成) - `A1, B1` — 并发线程中的消息 --- ## 💡 三、六种消息类型 | 消息类型 | 说明 | 画法 | |----------|------|------| | **调用消息** (同步) | 发送者等待接收者完成才继续 | 实线实心箭头 → | | **异步消息** | 发送者不等待,继续执行 | 实线开放箭头 ⇢ | | **返回消息** | 从调用返回 | 虚线开放箭头 ⇢ | | **反身消息** | 发给自己 | 绕回自己的箭头 | | **阻止消息** | 接收者不能立即接收就放弃 | 特殊标记 | | **超时消息** | 等待指定时间,超时就放弃 | 特殊标记 | ### 📌 消息类型的画法速查 > 这一节把六种消息类型的**实际画法**用字符画的形式画出来,光看箭头符号记不住的话,照着画就行。 #### ① 调用消息(同步)—— 实线 + 实心三角箭头 ``` ┌───┐ ┌───┐ │ A │ ──────────────────▶│ B │ ← 实线 + 实心三角 └───┘ └───┘ 同步:等B做完再回来 ``` #### ② 异步消息 —— 实线 + 开放箭头(V形) ``` ┌───┐ ┌───┐ │ A │ ─────────────────⇨ │ B │ ← 实线 + 开放箭头 └───┘ └───┘ 异步:发完就接着干自己的事 ``` #### ③ 返回消息 —— 虚线 + 开放箭头 ``` ┌───┐ ┌───┐ │ A │ ◀─ ─ ─ ─ ─ ─ ─ ─ ─ │ B │ ← 虚线 + 开放箭头 └───┘ └───┘ 返回值(可省略不画) ``` #### ④ 反身消息 —— 自己绕回来 ``` ┌───┐ │ A │ ───┐ └───┘ │ │ │ ◀─────┘ (即"对象调用自己的方法") ``` #### ⑤ 阻止消息 —— 实线 + 叉 ✕ > 接收方当时忙,**直接放弃**这次调用。 ``` ┌───┐ ┌───┐ │ A │ ─────────────────╳ │ B │ ← 实线 + 叉 └───┘ └───┘ B没空,A立刻放弃不等 ``` #### ⑥ 超时消息 —— 实线 + 倒三角 ⏳ > 等待指定时间,**超时就放弃**。 ``` ┌───┐ ┌───┐ │ A │ ─────────────────⏳ │ B │ ← 实线 + 倒三角 └───┘ └───┘ 等一段时间后超时 ``` ### 📌 完整顺序图示例 把所有消息类型都画在一张图里看效果: ``` 客户 ATM界面 账户 ┌───┐ ┌───┐ ┌───┐ │ │ │ │ │ │ ╱│╲ │ ╱│╲ │ ╱│╲ │ ╱ ╲ │ ╱ ╲ │ ╱ ╲ │ ┌───┐ ┌───┐ ┌───┐ │ │ │ │ │ │ │ │ 1: 取款(500) │ │ │ │ ← ① 调用消息 │ ├──────────────▶│ │ │ │ │ │ │ │ 2: 查询余额() │ │ ← ① 调用消息 │ │ │ ├───────────────▶│ │ │ │ │ │ │ │ │ │ │ │ ◀─ 3: 余额 ─── │ │ ← ③ 返回消息 │ │ │ │ │ │ │ │ │ │ 4: 扣款(500) │ │ ← ① 调用消息 │ │ │ ├───────────────▶│ │ │ │ │ │ │ │ │ │ 5: 出钞 │ │ │ │ ← ② 异步消息 │ │◀─ ─ ─ ─ ─ ─ ─│ │ │ │ │ │ │ │ │ │ ``` ### 📌 组合片段(Combined Fragment)速查 > 当顺序图比较复杂时,可以用**框选区域**的方式来表达条件、循环、并行等逻辑。 ``` ┌────────────── alt: 余额判断 ──────────────┐ │ │ │ │ 条件A: │ │ │ 4.1: 扣款(amount) ──▶ │ │ │ │ ├────────────────────────────────────────────┤ │ │ │ │ 条件B: │ │ │ 4.2: 显示"余额不足" ──▶ │ │ │ └────────────────────────────────────────────┘ ``` 常用组合片段关键字: | 关键字 | 含义 | 用途 | |--------|------|------| | `alt` | 抉择(多选一) | 相当于 if-else | | `opt` | 可选(满足才执行) | 相当于单分支 if | | `loop` | 循环 | 相当于 for/while | | `par` | 并行 | 多个事件流同时进行 | | `neg` | 非法 | 描述不允许发生的场景 | | `critical` | 关键 | 必须原子执行的区域 | --- ### 📌 协作图(Communication Diagram)的画法 > 协作图 = 顺序图的"空间版"——**没有生命线**,但消息必须**带完整序号**。 ``` 1: 输入金额 客户 ──────────────▶ ATM界面 ○ │ ╱│╲ │ 2: 查询余额 ╱ ╲ ▼ 账户 ╱│╲ ╱ ╲ ▲ │ 3: 返回余额 ◀───── 4.1*: 出钞 ─────┘ (出钞多次循环) ``` **协作图的关键特征**: - 对象之间的**连线**(关联)作为消息传递的路径 - 消息的**前缀序号**必须完整(`1`, `1.1`, `1.1.1`) - `{new}` 标签表示对象在交互中被创建 - `{destroy}` 标签表示对象在交互中被销毁 ``` ┌──────────┐ ┌──────────┐ │ :Order │ │ :Payment │ │{new} │ 1: 创建支付 │ │ └──────────┘ ────────────────▶ └──────────┘ │ │ {destroy} ▼ (被销毁) ``` 🔑 **一句话记忆口诀**:实心三角是同步,开放箭头是异步;返回用虚线开放箭头,反身消息自己绕;alt/loop/par 框选区域,条件循环并行都能搞。 --- ## 🔑 调用消息 vs 异步消息(必考!) | 对比维度 | 调用消息 | 异步消息 | |----------|----------|----------| | **发送者行为** | 发出后**停下来等** | 发出后**继续干活** | | **控制焦点** | 发送者出现等待(重叠矩形条) | 发送者不等待 | | **典型场景** | 查询数据库(必须等结果) | 写日志文件(可以后台进行) | | **返回消息** | 必有返回,可以省略不画 | 如果是过程调用必有返回 | 💬 **比喻**: - 调用消息像**打电话**——你说一句,等对方回一句。 - 异步消息像**发微信**——你发一条,继续干自己的事,对方看到再回。 --- ## 💡 四、顺序图的两种形式 | 形式 | 说明 | |------|------| | **一般形式** | 包含条件、分支、循环,描述所有可能的场景 | | **实例形式** | 只有一个具体的场景,没有任何分支和循环 | 💬 例:取款的一般形式包含"余额够"和"余额不够"两个分支;而某次具体取款(余额够)就是实例形式。 ### 条件消息和循环消息的表达 **两种方法**: 1. 在消息上直接标注:`[x>0] 3: doSomething()` 或 `2.1*: update()` 2. 使用交互架构(框选一个区域,标注`loop`或`alt`) --- ## 💡 五、协作图 ### 协作图 vs 顺序图 | 维度 | 顺序图 | 协作图 | |------|--------|--------| | **强调** | **时间顺序** | **空间位置关系** | | **生命线** | 有(从上到下) | 无 | | **控制焦点** | 有(矩形条) | 无 | | **消息序号** | 通常简化 | 必须完整标注 | | **多对象** | 不直观 | 可以表达 | | **主动对象** | 不直观 | 可以表达 | 🔑 **核心区别**:顺序图强调"什么时间谁调用了谁",协作图强调"谁跟谁有连接关系"。 ### 协作图的特殊元素 | 元素 | 说明 | |------|------| | **多对象** | 代表一组对象,发给它的消息是发给整个集合 | | **主动对象** | 拥有自己控制线程的对象,可以主动发起消息 | | **{new}** | 标记对象在交互中被创建 | | **{destroy}** | 标记对象在交互中被销毁 | --- ## ✍️ 边学边练(一) **题目**:ATM取款的基本流程如下,请画出分析阶段的顺序图。 1. 用户输入取款金额 2. 系统判断账户余额是否足额 3. 若足额,则出钞并修改账户余额 4. 若不足额,则取款失败 **答案:** 顺序图包含对象:`:客户`、`:ATM界面`、`:账户` 消息序列: ``` 1. 客户 → ATM界面: 输入金额(amount) 2. ATM界面 → 账户: 查询余额() 3. 账户 → ATM界面: 返回余额(balance) 4. ATM界面 → 自己: [balance>=amount]判断 5a. ATM界面 → 账户: 扣款(amount) [金额足够] 5b. ATM界面 → 客户: 显示"余额不足" [金额不足] ``` ⚠️ 关键:步骤4的判断是ATM界面对象自己的处理,用反身消息或不画都可以。 --- ## ✍️ 边学边练(二) **题目**:判断以下场景应该用调用消息还是异步消息。 1. 用户点击"登录"按钮,系统验证用户名密码 2. 用户上传文件后,系统后台写入日志 3. 用户查询订单列表,系统返回查询结果 4. 系统给所有订阅用户发送邮件通知 **答案:** 1. **调用消息(同步)**—— 必须等验证结果,用户才能进入下一步 2. **异步消息**—— 写日志不阻塞用户操作 3. **调用消息(同步)**—— 必须等返回结果才能显示 4. **异步消息**—— 发邮件是批量后台操作,不需要等待 🔑 **判断标准**:发送者是否**必须等待**结果才能继续。必须等=调用消息,不必等=异步消息。 --- ## 💡 六、交互建模的步骤 1. 根据一个用例的用例描述,找出**基本事件流**和**可选事件流** 2. 确定参与交互的**对象**(参考类图) 3. 按事件流的**先后次序**确定消息发送次序 4. 标注需要**创建**和**撤销**的对象 5. 处理**循环**和**分支**消息 --- ## 📝 章末自测 **1. 填空题** - 顺序图的四个核心元素是:( ____ )、( ____ )、( ____ )、( ___ _ ) - 发送后等待返回的消息叫( ____ )消息,发送后继续执行的消息叫( ____ )消息 - 顺序图强调( _____ )顺序,协作图强调( ____ )关系 **2. 判断题** - ( ) 调用消息的返回消息必须画出来 - ( ) 顺序图可以表示对象的创建和销毁 - ( ) 协作图中消息序号1.1表示它在消息1完成后才开始 **3. 简答题** - 顺序图和协作图各有什么优势和适用场景? **答案:** **填空题**:对象、生命线、控制焦点、消息;调用(同步)、异步;时间、空间位置 **判断题**: - ❌ 调用消息的返回消息可以省略不画(隐式) - ✅ 对象顶部放置={new},虚线末端×={destroy} - ❌ 消息1.1在消息1**完成之前**就必须完成(嵌套含义) **简答题**: - 顺序图:适合展示按时间顺序发生的消息交互,直观易懂,适合分析单个用例的执行流程 - 协作图:适合展示对象之间的静态连接关系,能表达多对象和并发,适合整体架构分析 --- > 🔗 上一篇:[第4章 类图与对象图](第04章-类图与对象图.md) | 下一篇:[第6章 状态图与活动图](第06章-状态图与活动图.md)