# 第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)
