# 第6章 状态图与活动图

> **考试重要度**：★★★★★  
> **核心内容**：状态图的组成（状态/转移/事件）、四种事件类型、活动图元素（泳道/分支/分叉汇合）、状态vs活动的区别

---

## 📢 从手机理解状态图和活动图

💬 你的手机有以下**状态**：闲置 → 解锁 → 使用中 → 锁屏 → 闲置。

每个状态之间的切换需要**事件**触发：点击屏幕、输入密码、按电源键……

这就是**状态图**——描述**一个对象**在生命周期内的状态变化。

而**活动图**描述的是**一个流程**：比如"打电话"这个活动——打开拨号盘 → 输入号码 → 点击拨号 → 通话 → 挂断。这其中可能涉及多个对象。

---

## 💡 一、状态图的核心元素

### 状态图三要素

```
    ┌──────────┐  事件[条件]/动作   ┌──────────┐
    │  状态A   │ ─────────────────→  │  状态B   │
    └──────────┘                    └──────────┘
```

| 要素 | 说明 |
|------|------|
| **状态** | 对象在某个时刻所处的条件或状况 |
| **转移** | 从一个状态到另一个状态的变化 |
| **事件** | 触发状态转移的原因 |

### 状态的组成

```
┌────────────────────────┐
│       状态名            │
├────────────────────────┤
│  entry / 入口动作      │
│  exit / 出口动作       │
│  do / 持续活动         │
│  内部转移（不离开状态）  │
└────────────────────────┘
```

### 组合状态和历史状态

💬 **组合状态**：一个大状态里面还有小状态的变化过程。

> 例：汽车"行驶"状态里面还有子状态：加速、匀速、减速。

💬 **历史状态**：记住离开组合状态时处于哪个子状态，回来后可以接着来。

> 例：数据备份——中途被打断，恢复后可以继续从断点备份。

---

## 💡 二、四种事件类型

| 事件类型 | 含义 | 举例 |
|----------|------|------|
| **调用事件** | 一个方法的调用 | `借书()` → 借书证从"可借"变为"不可借" |
| **信号事件** | 一个异步发送的信号实体 | 遥控器发出"Play"信号 → CD机进入"播放"状态 |
| **变化事件** | 布尔表达式值发生变化 | 温度>100℃ → 锅炉进入"报警"状态 |
| **时间事件** | 到达某个时间点或时间段 | 30秒超时 → 从"拨号"回到"就绪" |

### 🔑 调用事件 vs 信号事件

| 对比 | 调用事件 | 信号事件 |
|------|----------|----------|
| **本质** | 调用一个方法 | 发送一个信号对象 |
| **发送方** | 可以是自己或其他对象 | 只能是其他对象 |
| **特点** | 可扩展性弱 | 信号类可泛化，扩展性强 |

💬 例：CD唱机——如果各种遥控信号作为遥控器的属性（变化事件），增加新信号就要修改遥控器类。如果把遥控信号建模为信号类（信号事件），增加新信号只需新增子类，不用改遥控器。

---

## ✍️ 边学边练（一）

**题目**：画出"图书"对象的状态图。

需求：新书购买后需编码才能入库。入库后可借出。借出的书不能被再借出。还书后可再借出。图书破损或过时必须下架，下架后不能再借出。

**答案：**
**状态**：未编码 → 可借(在库) → 已借出 → 可借(在库) → 已下架

**转移**：
- 未编码 → 可借：`编码()` 
- 可借 → 已借出：`借出()`
- 已借出 → 可借：`还书()`
- 可借 → 已下架：`下架()` [破损或过时]
- 已借出 → 已下架：`下架()` [破损或过时]

⚠️ 注意：状态图中有两个状态必须有"下架"转移（从可借和已借出都可以下架）。

---

## 💡 三、活动图的核心元素

### 活动图的基本元素

| 元素 | 说明 | 画法 |
|------|------|------|
| **活动** | 一个执行步骤 | 圆角矩形 |
| **起点** | 流程的开始 | 实心圆 ● |
| **终点** | 流程的结束 | 圆圈加实心圆 ⊙ |
| **分支** | 根据条件选择路径 | 菱形 ◇ |
| **分叉/汇合** | 并发执行的开始/结束 | 粗横线 |
| **泳道** | 标明活动由谁负责 | 纵向分区 |
| **对象流** | 活动之间传递的对象 | 带箭头的虚线 |

### 📌 活动图各元素的画法速查

> 这一节把活动图中**所有画图元素**用字符画列出来，画活动图直接照搬即可。

#### ① 起点 —— 实心圆 ●

```
      ●
```

#### ② 终点 —— 圆圈加实心圆 ⊙（也称"牛眼"）

```
      ⊙
```

#### ③ 活动（Activity）—— 圆角矩形

```
  ╭───────────╮
  │  登录验证  │    ← 文字描述这一步做什么
  ╰───────────╯
```

#### ④ 转移箭头 —— 实线 + 开放箭头

```
  ╭───────────╮
  │  活动 A    │
  ╰─────┬─────╯
        │
        ⇨  实线 + 开放箭头（类似异步消息）
        │
  ╭─────┴─────╮
  │   活动 B   │
  ╰───────────╯
```

#### ⑤ 分支（Decision）—— 菱形 ◇ + 监护条件

```
  ╭───────────╮
  │  活动 A    │
  ╰─────┬─────╯
        │
       ◇    ← 菱形（决策点）
       ╱ ╲
   [是]╱   ╲[否]
     ╱       ╲
  ╭─────╮   ╭─────╮
  │ 活动B │   │ 活动C │
  ╰─────╯   ╰─────╯
        ╲   ╱
         ╲ ╱
          ◇   ← 合并点（多个分支汇合）
        │
       ⇨
  ╭─────┴─────╮
  │   活动 D   │
  ╰───────────╯
```

#### ⑥ 分叉（Fork）/ 汇合（Join）—— 粗横线

> **分叉**：一条路径分成多条并发执行。
> **汇合**：多条并发路径汇合成一条。

```
  ╭───────────╮
  │  活动 A    │
  ╰─────┬─────╯
        │
   ━━━━━━━━━━   ← 粗横线（分叉点）
    │       │
    ⇨       ⇨
  ╭─────╮ ╭─────╮
  │活动B│ │活动C│   ← B和C并发执行
  ╰──┬──╯ ╰──┬──╯
     ⇨       ⇨
    │       │
   ━━━━━━━━━━   ← 粗横线（汇合点）
        │
       ⇨
  ╭─────┴─────╮
  │   活动 D   │   ← B和C都完成后才继续
  ╰───────────╯
```

#### ⑦ 泳道（Swimlane）—— 纵向分区

> 标明每一步由**哪个对象/角色**负责。

```
  ┌──────────┬──────────┬──────────┐
  │  客户    │  业务员   │   系统    │
  │ (泳道1)  │ (泳道2)  │ (泳道3)  │
  ├──────────┼──────────┼──────────┤
  │          │          │          │
  │   ●      │          │          │  ← 起点
  │   ⇩      │          │          │
  │  ╭───╮  │          │          │
  │  │下单│  │          │          │  ← 客户下单
  │  ╰─┬─╯  │          │          │
  │    │    │          │          │
  │    ⇨    │          │          │
  │        ╭───╮       │          │
  │        │接单│      │          │  ← 业务员接单
  │        ╰─┬─╯       │          │
  │          │         │          │
  │          ⇨         │          │
  │                  ╭───╮        │
  │                  │处理│       │  ← 系统处理
  │                  ╰─┬─╯        │
  │                    │          │
  └──────────┴──────────┴──────────┘
                       ⇨
                       ⊙            ← 终点
```

#### ⑧ 对象流（Object Flow）—— 虚线 + 箭头

> 表示活动之间**传递的对象**（数据）。

```
  ╭───────────╮        ┌──────────┐
  │  提交订单  │ ── ─ ─▶│  订单     │  ← 虚线 + 箭头
  ╰───────────╯         └──────────┘
                          │
                          ⇨ （对象流也指向下一个活动）
                        ╭─────╮
                        │ 付款 │
                        ╰─────╯
```

⚠️ **注意**：对象本身画成**矩形**（不是圆角矩形），虚线箭头表示"这个活动产生/使用了这个对象"。

#### ⑨ 完整活动图示例（不带泳道）

```
            ●  起点
            ⇩
       ╭─────────╮
       │  开始借书 │
       ╰────┬────╯
            ⇩
       ╭─────────╮
       │  扫描图书  │
       ╰────┬────╯
            ⇩
       ╭─────────╮
       │  扫描借书证│
       ╰────┬────╯
            ⇩
           ◇ 判断
        ╱       ╲
    [可借]╱       ╲[不可借]
       ╱           ╲
   ╭─────╮        ╭───────╮
   │ 借出 │        │ 提示原因│
   ╰──┬──╯        ╰───┬───┯
      ⇩               ⇩   │ (汇合)
      │                │   │
      ╰───◇ 汇合 ◇────╯
            ⇩
       ╭─────────╮
       │   结束   │
       ╰─────────╯
            ⇩
            ⊙ 终点
```

#### ⑩ 状态图元素的画法

> 状态图和活动图有些元素是**共用的**（起点/终点/转移箭头）。

**状态**：圆角矩形，**可带分区**（状态名 / 内部活动）

```
   ╭─────────────────────╮
   │       待机中         │  ← 第一行：状态名
   ├─────────────────────┤
   │  entry / 初始化      │  ← 入口动作
   │  do / 持续活动       │  ← 持续活动
   │  exit / 清理         │  ← 出口动作
   ╰─────────────────────╯
```

**转移（Transition）**：

```
   ╭──────────╮  事件[条件]/动作   ╭──────────╮
   │  状态A   │ ─────────────────▶ │  状态B   │
   ╰──────────╯                    ╰──────────╯
   
   格式：事件名[监护条件]/动作
   例：   借出() / 可借数-1
```

**历史状态 H**：圆圈里写 H（记录离开时的子状态）

```
            ╭──────────╮
            │  复合状态 │
            ╰────┬─────╯
                 │
               (H)    ← 历史状态（圆圈里写H）
                 │
   ╭──────╮    ╱│╲
   │ 子1  │◀─(H)─▶│ 子2 │   ← 回来时回到H记录的子状态
   ╰──────╯       ╰─────╯
```

🔑 **一句话记忆口诀**：实心圆是起点，牛眼是终点；圆角矩形是活动，菱形是分支；粗横线是分叉汇合，泳道是纵向分区，对象是普通矩形+虚线箭头。

---

### 🔑 状态 vs 活动（必考区别！）

| 对比 | 状态 | 活动 |
|------|------|------|
| **本质** | 对象所处的**境况** | 一段程序代码的**执行过程** |
| **持续时间** | 可以很长（等待状态） | 相对短暂（执行完就结束） |
| **关系** | 是活动执行后的**结果** | 是导致状态变化的**原因** |
| **关注对象** | 一个对象 | 可以涉及多个对象 |
| **UML图** | 状态图 | 活动图 |

💬 **比喻**：做菜的过程（洗菜→切菜→炒菜→装盘）是**活动**；菜做完了变成"已做好"是**状态**。

### 活动图的两种用途

1. **对业务流程建模**（不带泳道）—— 用于需求分析，描述用例的工作流程
2. **对系统操作建模**（带泳道）—— 用于系统设计，把步骤落实到具体对象

---

## ✍️ 边学边练（二）

**题目**：画出"借书"用例的带泳道活动图。

流程：图书管理员录入图书条码和借书证号 → 系统判断借书证能否借书 → 若不能，显示原因并结束 → 若能，生成借阅记录，修改可借本数。

**答案：**
**泳道划分**：图书管理员 | 系统

```
图书管理员泳道：
  录入条码和借书证号 → 
系统泳道：
  判断能否借书 → [不能] → 显示原因 → 结束
                → [能] → 生成借阅记录 → 修改可借本数 → 结束
```

⚠️ 关键：分叉用菱形，两个分支最终汇合到结束。

---

## 💡 四、状态/活动与用例/方法的关系

### 状态与方法、用例的联系

- 一个方法的执行 → 属性值改变 → 对象进入新**状态**
- 对象处于不同状态 → 影响后续哪些**用例**可以执行
- 一个对象完整的状态图 = 多个用例的顺序图**融合**

### 活动与方法、用例的联系

- 一个**活动** = 一个方法中的部分（或全部）语句执行
- 一个方法可能需要多个活动来完成
- 一个**用例** = 若干个活动，通常一个用例对应一个活动图

---

## ✍️ 边学边练（三）

**题目**：借书证的状态图中有"可借书"和"不可借书"状态。问：

1. "借书"操作对应什么事件类型？
2. 从"不可借书"转回"可借书"需要什么事件？
3. "借书证"的状态图与哪些用例有关？

**答案：**
1. **调用事件**——"借书"本质上是对借书证对象调用借书方法
2. **还书事件**——读者还书后，借书证的可借本数减1，重新变为可借
3. 与"借书""还书""挂失借书证"等用例有关。这些用例的顺序图融合起来，就构成了借书证的完整状态图

---

## 📝 章末自测

**1. 填空题**
- 状态图描述的是（ ____ ）个对象的状态变化
- 四种事件类型是：调用事件、（ ____ ）、（ _____ ）、时间事件
- 活动图中的菱形表示（ ____ ），粗横线表示（ ___ ）

**2. 判断题**
- ( ) 状态图和活动图都可以描述多个对象的交互
- ( ) 信号事件比调用事件更易于扩展
- ( ) 一个活动可能跨越多个泳道
- ( ) 带泳道的活动图用在系统设计阶段

**3. 简答题**
- 简述状态和活动的区别。
- 什么情况下应该用状态图，什么情况下应该用活动图？

**答案：**
**填空题**：一（单个）；信号事件、变化事件；分支、分叉/汇合

**判断题**：
- ❌ 状态图只描述一个对象
- ✅ 信号类可继承扩展
- ❌ 每个活动只属于一个泳道
- ✅ 带泳道用于设计阶段

**简答题**：
- 状态是对象的境况（活动执行后的结果），可以持续很长时间；活动是代码的执行过程，相对短暂。状态变化由活动引起。
- 状态图：描述一个对象生命周期内的状态变迁（如订单状态、借书证状态）。活动图：描述一个业务流程或算法步骤（如取款流程、登录验证流程）。

---
> 🔗 上一篇：[第5章 顺序图与协作图](第05章-顺序图与协作图.md) | 下一篇：[第7章 组件图与部署图](第07章-组件图与部署图.md)
