Thinking in UML 大象:用例驱动的建模思想与实践
引言
在软件开发的历史中,统一建模语言(Unified Modeling Language,UML)是每个软件工程师必备的技能。最近在看一非常不错的UML相关书籍:《大象:Thinking in UML》,这本书的核心价值在于:它不仅仅教我们如何画 UML 图,更重要的是传达了一种用例驱动的建模思想。这种思想帮助我们建立从现实世界到软件设计的映射路径,让复杂系统的分析和设计变得有章可循。

“建模就像写文章,UML 的元素、规则就像文章中的文字和语法。”
本文将围绕这本书的核心内容,系统梳理 UML 建模的基础知识、核心元素、实践方法以及高级思考,帮助读者建立完整的 UML 建模知识体系。
第一部分:UML 带来了什么
面向对象的困难与 UML 的应对
面向对象方法学从诞生之初就面临着认知上的困难。我们习惯于用过程化的思维思考问题,而面向对象要求我们将世界看作一个个相互独立的对象。
面向对象的本质认识:
“面向对象方法将世界看作一个个相互独立的对象,相互之间并无因果关系,它们平时是’鸡犬之声相闻,老死不相往来’的。只有在某个外部力量的驱动下,对象之间才会依据某种规律相互传递信息。”
这种思维方式的转变带来了三个根本问题:
| 问题 | 描述 | UML 的解决方案 |
|---|---|---|
| Why | 对象是怎么被抽象出来的?为什么要这么抽象而不是那么抽象? | 用例驱动方法,从参与者目标出发 |
| How | 什么样的组合是好的,什么样的组合是差的? | 设计原则和模式指导 |
| What | 如果只给我一个对象组合,我怎么才能理解它表达了怎样的含义? | 可视化视图和模型组织 |
抽象是面向对象的精髓所在,同时也是面向对象的困难所在。 UML 通过提供一套标准的建模元素和规则,帮助我们克服这些困难。
UML 的本质:统一语言与可视化
UML 的本质是什么?书中给出了清晰的定义:
“UML 是一种建模用的语言,而所有的语言都是由基本词汇和语法两个部分构成的。”
UML 带来的三个核心价值:
| 价值 | 说明 | 实践意义 |
|---|---|---|
| 统一语言 | 构设在团队、角色之间的沟通桥梁 | 需求分析师、架构师、开发者、测试人员使用相同的术语 |
| 可视化 | 把”隐晦”的变成”可视”的 | 通过图形直观表达复杂的业务关系和系统结构 |
| 建模方法 | 从现实世界到软件设计的映射路径 | 提供完整的分析设计流程指导 |
“UML 通过它的元模型和表示法,把那些通过文字或其他表达方法很难表达清楚的、隐晦的潜台词用简单直观的图形表达和暴露出来,准确而直观地描述复杂的含义。”
在 2012 年的软件开发实践中,UML 的价值主要体现在:
- 需求分析阶段:用用例图捕获用户需求
- 系统设计阶段:用类图、时序图设计系统架构
- 团队沟通阶段:用标准图形替代文字描述,减少歧义
- 文档归档阶段:形成可追溯的设计文档
从现实世界到设计模型的映射
UML 提供了一条清晰的从现实世界到软件设计的映射路径:
graph LR
A[现实世界] -->|业务建模| B[业务模型]
B -->|概念建模| C[分析模型]
C -->|详细设计| D[设计模型]
D -->|编码实现| E[可执行系统]
style A fill:#e1f5ff,stroke:#0288d1,color:#01579b
style B fill:#fff3e0,stroke:#f57c00,color:#e65100
style C fill:#f3e5f5,stroke:#7b1fa2,color:#4a148c
style D fill:#e8f5e9,stroke:#388e3c,color:#1b5e20
style E fill:#fff9c4,stroke:#fbc02d,color:#f57f17
各阶段的输入输出:
| 阶段 | 输入 | 核心工作 | 输出 |
|---|---|---|---|
| 业务建模 | 现实世界业务 | 参与者访谈、用例获取、业务场景 | 业务用例模型、领域模型 |
| 概念建模 | 业务模型 | 分析类提取、用例实现 | 分析模型、软件架构 |
| 设计建模 | 分析模型 | 类设计、接口设计、包设计 | 设计模型、组件模型 |
| 编码实现 | 设计模型 | 代码生成、单元测试 | 可执行系统 |
“现实世界无论多复杂,无论是哪个行业,无论做什么业务,其本质无非是由人、事、物和规则组成的。”
这条映射路径的关键在于保持可追溯性:每一阶段的设计元素都应该能追溯到上一阶段,最终追溯到现实世界的业务需求。
第二部分:UML 核心元素详解
参与者:建模的核心地位
参与者(Actor)是 UML 建模的起点,也是整个系统的驱动力来源。
定义:参与者是模型信息来源的提供者,也是第一驱动者。
参与者的核心特征:
| 特征 | 说明 | 示例 |
|---|---|---|
| 位于边界之外 | 参与者总是在系统边界之外 | ATM 系统的”存款人” |
| 可以非人 | 参与者不一定是人 | 定时器、传感器、外部系统 |
| 涉众的代表 | 代表与系统有利益关系的一方 | “局长”代表”投资机构” |
| 第一驱动者 | 主动发起与系统的交互 | 用户点击登录按钮 |
“参与者对系统的要求,对系统的表述完全决定了系统的功能性。”
参与者 vs 容易混淆的概念:
| 概念 | 定义 | 位置 | 主动/被动 |
|---|---|---|---|
| 业务主角 | 业务系统中的参与者 | 边界之外 | 主动发起动作 |
| 业务工人 | 边界内被动参与者 | 边界之内 | 被动响应 |
| 涉众 | 与系统有利益关系的一切人和事 | - | - |
| 用户 | 参与者的代理或实例 | - | - |
| 角色 | 参与者的职责 | - | - |
发现参与者的方法:
- 回答两个问题:
- 谁对系统有着明确的目标和要求并且主动发出动作?
- 系统是为谁服务的?
- 从业务流程中寻找:
- 谁启动了业务流程?
- 谁从系统中获得价值?
- 系统需要与哪些外部系统交互?
用例:从功能到目标的转变
用例(Use Case)是 UML 建模的核心,也是用例驱动方法的基石。
定义:与参与者交互的,并且给参与者提供可观测的有意义的结果的一系列活动的集合。
用例的构成:
一个用例 = <参与者,前置条件,{场景},后置条件>
用例的五大特征:
| 特征 | 说明 | 正例 | 反例 |
|---|---|---|---|
| 相对独立 | 不需要与其他用例交互而独自完成参与者的目的 | 取钱 | 填写取款单 |
| 可观测结果 | 结果对参与者来说是可观测的和有意义的 | 查询余额 | 后台数据备份 |
| 参与者发起 | 必须由一个参与者发起 | 取款 | ATM 自动吐钞 |
| 动宾结构 | 以动宾短语形式出现 | “修改密码” | “密码” |
| 有意义的结果 | 对参与者有明确的意义 | 转账成功 | 输入密码 |
用例 vs 功能:这是书中重点强调的误区
| 方面 | 用例 | 功能 |
|---|---|---|
| 视角 | 参与者角度 | 事物角度 |
| 描述 | 人们能够用它做什么 | 它能做什么 |
| 性质 | 使用者观点 | 功能性观点 |
| 示例 | “购买商品” | “商品管理功能” |
“功能是用事物角度出发的,而用例是从参与者角度出发的。功能是脱离使用者的愿望存在的。功能是孤立的,而用例是一个系统性的工作。”
这个区分看似简单,但在实际建模中经常被混淆。正确理解用例,是用例驱动方法的第一步。
边界:决定视界与抽象层次
边界(Boundary)是面向对象方法中一个非常重要但容易被忽视的概念。
定义:边界本质上是面向对象方法的一个很重要的概念,与封装的概念师出同源。
边界的两个核心作用:
| 作用 | 说明 | 实践意义 |
|---|---|---|
| 决定视界 | 边界决定了你看到的东西 | 设定系统范围,明确什么在系统内,什么在系统外 |
| 决定抽象层次 | 通过设定边界来控制分析粒度 | 边界越大,粒度越粗;边界越小,粒度越细 |
“边界和封装概念师出同源,边界决定着抽象的层次,也就是它决定着分析粒度的大小。”
“能否准确把握边界,能否灵活变换边界,能否控制边界的粒度是做好需求分析和系统设计的关键。”
边界的相对性:
边界不是固定不变的,而是可以根据分析需要灵活调整的:
| 边界大小 | 分析粒度 | 适用场景 | 示例 |
|---|---|---|---|
| 大边界 | 粗粒度 | 业务建模、概念建模 | “图书馆借阅系统” |
| 中边界 | 中粒度 | 系统分析 | “借书模块” |
| 小边界 | 细粒度 | 详细设计 | “借书审批流程” |
边界调整的策略:
graph TD
A[发现建模困难] --> B{检查边界}
B -->|粒度不一致| C[调整边界大小]
B -->|信息过载| D[缩小边界范围]
B -->|信息不足| E[扩大边界范围]
C --> F[重新获取用例]
D --> F
E --> F
style A fill:#ffebee,stroke:#d32f2f,color:#b71c1c
style B fill:#fff3e0,stroke:#f57c00,color:#e65100
style C fill:#e1f5ff,stroke:#0288d1,color:#01579b
style D fill:#f3e5f5,stroke:#7b1fa2,color:#4a148c
style E fill:#e8f5e9,stroke:#388e3c,color:#1b5e20
style F fill:#e8f5e9,stroke:#388e3c,color:#1b5e20
业务实体、分析类、设计类
UML 建模中,不同阶段使用不同抽象层次的类概念。
业务实体(Business Entity):
定义:代表业务角色执行业务用例时所处理或使用的”事物”。
获取业务实体的方法:
- 建立业务用例场景
- 分析动词后面的名词
- 筛选对业务目标有贡献的对象
- 分析关系,决定哪些单独建模,哪些作为属性
分析类(Analysis Class):
分析类是跨越需求到设计实现的桥梁,分为三种:
| 类型 | 图符 | 职责 | 架构层次 | 示例 |
|---|---|---|---|---|
| 边界类 | ◇︎ | 系统外部环境与其内部运作之间的交互 | 展现层 | 网页、窗口、API |
| 控制类 | ⬭ | 对一个或几个用例所特有的控制行为进行建模 | 业务逻辑层 | 购买、验证、发送 |
| 实体类 | ▱ | 对必须存储的信息和相关行为建模 | 数据持久层 | 订单、商品、客户 |
“分析类就是跨越需求到设计实现的桥梁。”
分析类的”三高”特征:
- 高于设计实现 - 不理会复杂的设计要求
- 高于编程语言 - 不受特定语言约束
- 高于技术细节 - 专注于逻辑结构
三种分析类的关系:
graph TD
A[边界类<br/>Boundary] -->|调用| B[控制类<br/>Control]
B -->|操作| C[实体类<br/>Entity]
A1[用户界面] -->|A| B1[业务逻辑]
B1 -->|B| C1[数据对象]
style A fill:#e1f5ff,stroke:#0288d1,color:#01579b
style B fill:#fff3e0,stroke:#f57c00,color:#e65100
style C fill:#f3e5f5,stroke:#7b1fa2,color:#4a148c
style A1 fill:#e8f5e9,stroke:#388e3c,color:#1b5e20
style B1 fill:#fff9c4,stroke:#fbc02d,color:#f57f17
style C1 fill:#fce4ec,stroke:#d81b60,color:#ad1457
设计类(Design Class):
设计类是分析类的具体化,需要考虑:
- 具体的编程语言特性
- 框架和平台的要求
- 性能、安全等技术约束
- 设计模式的应用
九种关系:关联、依赖、扩展、包含、实现、精化、泛化、聚合、组合
UML 定义了丰富的关系类型,理解和正确使用这些关系是建模的关键。
九种关系分类:
graph TD
A[UML关系] --> B[关系型]
A --> C[依赖型]
A --> D[层次型]
B --> E[关联]
B --> F[聚合]
B --> G[组合]
C --> H[依赖]
C --> I[扩展]
C --> J[包含]
D --> K[实现]
D --> L[精化]
D --> M[泛化]
style A fill:#e1f5ff,stroke:#0288d1,color:#01579b
style B fill:#fff3e0,stroke:#f57c00,color:#e65100
style C fill:#f3e5f5,stroke:#7b1fa2,color:#4a148c
style D fill:#e8f5e9,stroke:#388e3c,color:#1b5e20
关系详解:
| 关系类型 | 符号 | 语义 | 代码含义 | 示例 |
|---|---|---|---|---|
| 关联 | 实线 | 类之间的引用关系 | class A { B b; } | 客户关联订单 |
| 依赖 | 虚线箭头 | 临时使用关系 | 函数参数使用 | 订单处理依赖支付服务 |
| 扩展 | 虚线+«extend» | 可选的扩展行为 | 条件分支 | 支付扩展退款 |
| 包含 | 虚线+«include» | 必须包含的行为 | 方法调用 | 支付包含身份验证 |
| 实现 | 虚线+空心三角 | 接口实现 | implements | 类实现接口 |
| 精化 | 虚线+空心三角 | 抽象层次之间的细化 | 概念→具体 | 分析类精化为设计类 |
| 泛化 | 实线+空心三角 | is-a 关系 | extends | 子类继承父类 |
| 聚合 | 空心菱形+实线 | has-a 关系,弱拥有 | 成员变量,可独立存在 | 部门包含员工 |
| 组合 | 实心菱形+实线 | contains-a 关系,强拥有 | 成员变量,生命周期绑定 | 订单包含订单项 |
聚合 vs 组合:这是最容易混淆的一对关系
| 方面 | 聚合 (Aggregation) | 组合 (Composition) |
|---|---|---|
| 关系强度 | 弱拥有 | 强拥有 |
| 生命周期 | 独立 | 绑定 |
| 共享性 | 可被多个父对象共享 | 只能属于一个父对象 |
| 销毁 | 父对象销毁,子对象仍可存在 | 父对象销毁,子对象随之销毁 |
| 代码示例 | class Car { Engine* engine; } |
class Car { Wheel wheel; } |
| 现实类比 | 班级包含学生 | 人包含心脏 |
选择关系的决策树:
两个类之间存在关系?
↓
是"整体-部分"关系吗?
YES → 部分能独立存在吗?
YES → 聚合
NO → 组合
NO → 是"is-a"关系吗?
YES → 泛化
NO → 是临时使用吗?
YES → 依赖
NO → 关联
第三部分:UML 核心视图
静态视图:用例图、类图、包图
UML 视图分为静态视图和动态视图两大类。静态视图描述系统的结构特征,动态视图描述系统的行为特征。
UML 视图分类:
graph TD
A[UML视图] --> B[静态视图]
A --> C[动态视图]
B --> D[用例图]
B --> E[类图]
B --> F[包图]
C --> G[活动图]
C --> H[状态图]
C --> I[时序图]
C --> J[协作图]
style A fill:#e1f5ff,stroke:#0288d1,color:#01579b
style B fill:#fff3e0,stroke:#f57c00,color:#e65100
style C fill:#f3e5f5,stroke:#7b1fa2,color:#4a148c
用例图(Use Case Diagram)
用例图是 UML 中最基础的视图,用于描述系统的功能需求。
用例图的组成元素:
| 元素 | 符号 | 说明 |
|---|---|---|
| 参与者 | 小人图标 | 系统外部的实体 |
| 用例 | 椭圆 | 系统提供的功能 |
| 系统边界 | 矩形框 | 界定系统范围 |
| 关联 | 实线 | 参与者与用例的关系 |
| 包含 | 虚线+«include» | 基础用例必须包含的行为 |
| 扩展 | 虚线+«extend» | 可选的扩展行为 |
ATM 系统用例图示例:
graph TD
A[存款人] -->|取款| UC1(取款)
A -->|查询余额| UC2(查询余额)
A -->|修改密码| UC3(修改密码)
B[银行系统] -->|管理账户| UC4(账户管理)
C[管理员] -->|系统维护| UC5(系统管理)
style A fill:#e1f5ff,stroke:#0288d1,color:#01579b
style B fill:#fff3e0,stroke:#f57c00,color:#e65100
style C fill:#f3e5f5,stroke:#7b1fa2,color:#4a148c
用例图的实践价值:
- 需求获取和验证的工具
- 项目估算的依据
- 测试用例设计的基础
- 用户沟通的媒介
类图(Class Diagram)
类图是 UML 中最重要的视图,描述系统的静态结构。
类图的组成元素:
一个类由三部分组成:
- 名称:类的名称
- 属性:类的特征
- 方法:类的行为
用户类图示例:
classDiagram
class User {
+String userId
+String username
+String email
+String password
+register() void
+login() boolean
+logout() void
}
class Customer {
+String address
+String phone
+List~Order~ orders
+placeOrder() Order
+cancelOrder() void
}
class Administrator {
+String department
+List~String~ permissions
+manageUsers() void
+configureSystem() void
}
User <|-- Customer : 继承
User <|-- Administrator : 继承
类图的核心价值:
- 系统静态结构的完整描述
- 数据库设计的基础
- 代码生成的依据
- 架构评审的工具
包图(Package Diagram)
包图用于组织模型元素,体现了”高内聚、低耦合”的设计原则。
系统分层包图示例:
graph TD
subgraph Presentation[表示层]
UI[用户界面组件]
API[API 接口]
end
subgraph Business[业务层]
Service[业务服务]
Domain[领域模型]
Rules[业务规则]
end
subgraph Persistence[持久层]
DAO[数据访问对象]
ORM[ORM 映射]
end
Presentation -->|依赖| Business
Business -->|依赖| Persistence
style Presentation fill:#e1f5ff,stroke:#0288d1,color:#01579b
style Business fill:#fff3e0,stroke:#f57c00,color:#e65100
style Persistence fill:#f3e5f5,stroke:#7b1fa2,color:#4a148c
包图的核心价值:
- 体现系统的分层架构
- 控制依赖关系
- 指导代码组织结构
动态视图:活动图、状态图、时序图
动态视图描述系统的行为特征,展示系统在运行时的状态变化和交互过程。
活动图(Activity Diagram)
活动图用于描述业务流程和工作流。
网上购物流程活动图示例:
flowchart TD
START([开始]) --> Browse[浏览商品]
Browse --> |选择商品| AddCart[加入购物车]
AddCart --> ViewCart[查看购物车]
ViewCart --> |继续购物| Browse
ViewCart --> |去结算| CheckLogin{已登录?}
CheckLogin --> |否| Login[登录/注册]
Login --> Checkout[填写订单信息]
CheckLogin --> |是| Checkout
Checkout --> Confirm[确认订单]
Confirm --> Pay[选择支付方式]
Pay --> ProcessPay[处理支付]
ProcessPay --> |成功| Success[支付成功]
ProcessPay --> |失败| Fail[支付失败]
Fail --> |重新支付| Pay
Fail --> |取消订单| Cancel([取消])
Success --> Ship[等待发货]
Ship --> Receive([确认收货])
Receive --> END([结束])
活动图的核心价值:
- 业务流程的可视化
- 工作流设计的依据
- 业务规则梳理的工具
状态图(State Diagram)
状态图描述对象的生命周期和状态转换。
订单状态图示例:
stateDiagram-v2
[*] --> 待支付: 创建订单
待支付 --> 已取消: 超时未支付
待支付 --> 已支付: 支付成功
待支付 --> 已关闭: 系统关闭
已支付 --> 待发货: 支付确认
待发货 --> 已发货: 商家发货
待发货 --> 已退款: 申请退款
已发货 --> 待收货: 物流更新
待收货 --> 已完成: 确认收货
待收货 --> 已退款: 申请退款
已完成 --> [*]
已取消 --> [*]
已关闭 --> [*]
已退款 --> [*]
状态图的核心价值:
- 明确对象的生命周期
- 定义状态转换规则
- 指导状态机实现
时序图(Sequence Diagram)
时序图描述对象之间交互的时间顺序。
用户登录时序图示例:
sequenceDiagram
actor U as 用户
participant UI as 登录界面
participant C as 控制器
participant S as 用户服务
participant D as 数据库
participant A as 认证服务
U->>UI: 输入用户名密码
UI->>C: 提交登录请求
C->>S: 验证用户信息
S->>D: 查询用户数据
D-->>S: 返回用户信息
S->>A: 验证密码
alt 密码正确
S->>A: 生成令牌
A-->>S: 返回令牌
S-->>C: 登录成功+令牌
C-->>UI: 跳转首页
else 密码错误
S-->>C: 登录失败
C-->>UI: 显示错误信息
end
时序图的核心价值:
- 清晰展示交互流程
- 验证用例实现
- 指导接口设计
第四部分:实践流程指南
准备工作:涉众分析与边界定义
UML 建模的第一步不是画图,而是准备工作。准备工作的质量决定了后续建模的效果。
涉众分析:
涉众(Stakeholder)是与系统有利益关系的所有人和事。识别涉众是建模的起点。
涉众分类:
| 类型 | 说明 | 示例 |
|---|---|---|
| 参与者 | 系统外部主动发起动作的实体 | 用户、外部系统 |
| 业务工人 | 边界内被动参与者 | 银行柜台职员 |
| 受益者 | 从系统中获得价值的人 | 企业老板、管理层 |
| 监管者 | 对系统有约束力的人 | 审计部门、监管机构 |
定义系统边界:
通过两个问题确定边界:
- 谁对系统有着明确的目标和要求并且主动发出动作?
- 系统是为谁服务的?
边界定义的实践建议:
| 情况 | 建议 |
|---|---|
| 初次建模 | 从大边界开始,把握整体业务 |
| 需求复杂 | 划分多个子边界,逐个分析 |
| 需求变化 | 灵活调整边界,不拘泥于初始设定 |
获取需求:用例获取的实践方法
获取用例是建模过程中最关键的环节,也是最容易出错的环节。
避免的做法:
| ❌ 错误做法 | ✅ 正确做法 |
|---|---|
| 让客户描述整个业务流程 | 引导客户描述目标和期望 |
| 涉及表单填写等业务细节 | 关注业务目标,忽略实现细节 |
| 让客户理解将来的计算机系统 | 用客户的业务语言交流 |
| 从流程图开始建模 | 从参与者访谈开始 |
正确的引导问题:
- 您对系统有什么期望?
- 您打算在这个系统里做些什么事情?
- 您做这件事的目的是什么?
- 您做完这件事希望有一个什么样的结果?
用例有效性检查表:
| 检查项 | 说明 | 正例 | 反例 |
|---|---|---|---|
| 独立完整性 | 能独立完成参与者的目的 | 取钱 | 填写取款单 |
| 可观测性 | 结果对参与者可观测 | 登录系统 | 后台备份 |
| 主动发起 | 由参与者发起 | 取款 | ATM 自动吐钞 |
| 动宾结构 | 以动宾短语命名 | “修改密码” | “密码” |
| 有意义结果 | 对参与者有明确意义 | 转账成功 | 输入密码 |
用例获取示例分析(ATM 取款场景):
| 客户表达 | 分析结果 | 是否用例 |
|---|---|---|
| “我希望这台 ATM 能支持跨行业务” | 功能描述,不是用户目标 | ✗ |
| “插入卡片” | 操作步骤,不是完整目标 | ✗ |
| “输入密码” | 操作步骤,不是完整目标 | ✗ |
| “选择服务” | 操作步骤,不是完整目标 | ✗ |
| “取钱” | 完整的用户目标 | ✓ |
| “存钱” | 完整的用户目标 | ✓ |
| “挂失卡片” | 完整的用户目标 | ✓ |
| “交纳费用” | 完整的用户目标 | ✓ |
| “警示骗子” | 系统属性,不是功能目标 | ✗ |
| “三次错误吞没卡片” | 业务规则,不是用例 | ✗ |
系统分析:分析类与用例实现
获取用例后,下一步是进行系统分析,建立分析模型。
用例实现的步骤:
flowchart TD
START[开始用例实现] --> Identify[识别边界类]
Identify --> Extract[提取控制类]
Extract --> Find[寻找实体类]
Find --> Model[建立关系模型]
Model --> Sequence[绘制时序图]
Sequence --> Verify[验证场景完整性]
Verify --> END[完成用例实现]
style START fill:#e1f5ff,stroke:#0288d1,color:#01579b
style END fill:#e8f5e9,stroke:#388e3c,color:#1b5e20
分析类的获取来源:
| 分析类 | 来源 | 示例 |
|---|---|---|
| 边界类 | 参与者与用例的交互点 | 网页、窗口、API、协议 |
| 控制类 | 用例场景中的动词行为 | 购买、计算、验证、发送 |
| 实体类 | 业务实体 | 订单、商品、客户 |
用例实现的实践建议:
- 从场景开始:先用文字描述用例的场景,再转换为分析类
- 关注职责:每个分析类应该有清晰的职责
- 保持简单:分析类不应该涉及设计细节
- 可追溯性:每个分析类都应该能追溯到需求
系统设计:从分析模型到设计模型
分析模型完成后,下一步是转换为设计模型。
分析模型到设计模型的转换:
graph TD
A[业务用例] -->|映射为| B[概念用例]
B -->|实现为| C[系统用例]
D[业务实体] -->|转化为| E[实体类]
E -->|设计为| F[数据表]
G[业务场景] -->|抽象为| H[控制类]
H -->|实现为| I[业务逻辑]
J[参与者] -->|转化为| K[用户]
K -->|配置为| L[权限角色]
style A fill:#e1f5ff,stroke:#0288d1,color:#01579b
style B fill:#fff3e0,stroke:#f57c00,color:#e65100
style C fill:#f3e5f5,stroke:#7b1fa2,color:#4a148c
设计模型需要考虑的问题:
| 方面 | 分析模型 | 设计模型 |
|---|---|---|
| 抽象层次 | 高抽象,关注逻辑结构 | 低抽象,关注实现细节 |
| 技术约束 | 不考虑技术约束 | 考虑框架、平台、性能 |
| 设计模式 | 不涉及 | 应用适当的设计模式 |
| 代码映射 | 不能直接映射 | 可以直接映射到代码 |
架构设计的基本构成:
graph TB
subgraph 架构层次
P[表示层<br/>展现层]
B[业务层<br/>业务逻辑层]
D[数据层<br/>持久层]
I[集成层<br/>外部接口]
end
P -->|调用| B
B -->|访问| D
I -->|通信| B
subgraph 横切关注点
S1[安全]
S2[日志]
S3[缓存]
S4[事务]
end
S1 & S2 & S3 & S4 -.影响.-> P
S1 & S2 & S3 & S4 -.影响.-> B
S1 & S2 & S3 & S4 -.影响.-> D
style P fill:#e1f5ff,stroke:#0288d1,color:#01579b
style B fill:#fff3e0,stroke:#f57c00,color:#e65100
style D fill:#f3e5f5,stroke:#7b1fa2,color:#4a148c
style I fill:#e8f5e9,stroke:#388e3c,color:#1b5e20
第五部分:高级思考与最佳实践
用例的本质再理解
理解用例的本质,是用例驱动方法成功的关键。
用例是系统思维:
“用例体现了一种系统思维,即把软件看作一个为参与者提供服务的系统,而不是一堆功能的集合。”
系统思维 vs 功能思维:
| 维度 | 系统思维(用例) | 功能思维 |
|---|---|---|
| 关注点 | 参与者的目标和价值 | 功能的划分和实现 |
| 视角 | 使用者视角 | 开发者视角 |
| 组织方式 | 以参与者为中心组织 | 以功能模块为中心组织 |
| 验证标准 | 是否满足参与者目标 | 是否完成功能开发 |
用例是面向服务的:
“用例本质上是面向服务的,每个用例都是系统为参与者提供的一项服务。”
服务导向的特征:
- 服务是可访问的(通过边界)
- 服务是有价值的(对参与者有意义)
- 服务是独立的(可完整交付)
- 服务是可组合的(可构成更大服务)
抽象层次的实战应用
抽象层次是 UML 建模中最重要的概念之一,也是最难掌握的。
抽象层次的原则:
“抽象层次越高,信息量越少(多数信息被封装或屏蔽),相对的越容易被理解。但过高又会产生信息量不足的问题,在合适的时候采用适当的抽象层次十分重要。”
UML 中的抽象层次体现:
业务建模层(高抽象)
├── 业务用例模型
├── 领域模型
└── 业务架构
概念建模层(中抽象)
├── 概念用例模型
├── 分析模型
└── 软件架构
设计建模层(低抽象)
├── 系统用例模型
├── 设计模型
└── 组件模型
实现建模层(最低抽象)
├── 类设计
├── 数据库设计
└── 部署模型
决定抽象层次的因素:
| 因素 | 高抽象 | 低抽象 |
|---|---|---|
| 沟通对象 | 业务人员、项目经理 | 开发人员、测试人员 |
| 文档目的 | 需求理解、范围界定 | 设计指导、代码实现 |
| 项目阶段 | 早期需求分析 | 后期详细设计 |
| 系统复杂度 | 复杂系统(宏观把握) | 简单系统(直接设计) |
层次不交叉原则:
同一抽象层次的内容应当放在一起,不要在一个层次中混杂其他层次的内容。
系统边界的战略运用
边界是面向对象的保障,也是建模的核心技巧。
边界是面向对象的保障:
“边界是面向对象的保障,没有边界就没有对象。”
边界的核心作用:
| 作用 | 说明 |
|---|---|
| 封装隔离 | 将相关内容封装在一起 |
| 职责分离 | 不同边界承担不同职责 |
| 依赖控制 | 控制跨边界的依赖关系 |
| 复杂度管理 | 通过边界控制信息量 |
边界的艺术:
“好的设计如同一筐带壳的鸡蛋,清清爽爽;差的设计如同一堆打碎了壳的鸡蛋,粘粘糊糊。’壳’是好坏的关键。”
边界意识的表现:
- 心中有边界:时刻意识到边界存在
- 灵活变换边界:根据需要调整边界
- 控制边界粒度:选择合适的抽象层次
- 尊重边界约束:不随意跨越边界
接口是系统的灵魂
接口设计是系统设计中最重要的一环。
从接口认知事物:
描述一件事物有三种观点:
- 结构性观点:这个事物是什么?
- 功能性观点:这个事物能做什么?
- 使用者观点:人们能够用这个事物做什么?
UML 选择使用者观点:从参与者角度出发,关注交互和价值。
“接口是系统的灵魂,接口设计的好坏直接决定了系统的可扩展性、可维护性和可测试性。”
接口的核心价值:
| 价值 | 说明 |
|---|---|
| 解耦 | 通过接口隔离具体实现 |
| 扩展 | 通过接口支持多种实现 |
| 复用 | 通过接口实现组件复用 |
| 测试 | 通过接口进行 Mock 测试 |
接口设计的原则:
- 接口应当是稳定的
- 接口应当是语义化的
- 接口应当是高内聚的
- 接口应当是向前兼容的
总结
通过《大象:Thinking in UML》这本书,可以系统地学习 UML 建模的核心知识和实践方法。
核心要点回顾:
| 要点 | 核心内容 |
|---|---|
| 用例驱动 | 整个软件生产过程是用例驱动的,用例是需求、分析、设计、开发、测试、部署单元 |
| 边界意识 | 边界决定抽象层次,能否准确把握边界是做好需求分析和系统设计的关键 |
| 抽象层次 | 在合适的时候采用适当的抽象层次,同一抽象层次的内容应当放在一起 |
| 接口优先 | 接口是系统的灵魂,接口设计决定系统的可扩展性、可维护性和可测试性 |
UML 建模的价值:
- 沟通工具:跨角色、跨团队的标准语言
- 思维工具:帮助理清复杂的业务关系
- 文档工具:比文字更直观的表达方式
- 设计工具:从需求到设计的映射桥梁
“抽象是面向对象的精髓所在,同时也是面向对象的困难所在。”
掌握 UML 建模,不仅仅是掌握画图技巧,更重要的是掌握一种思维方式——一种从现实世界抽象出软件模型的思维方式。这种思维方式,将帮助我们在复杂的软件开发中保持清晰的思路,设计出高质量的软件系统。
推荐阅读
- 《大象:Thinking in UML(第2版)》- 谭云杰
- 《UML 用户指南》- Grady Booch, James Rumbaugh, Ivar Jacobson
- 《UML 精粹:标准对象语言简明指南》- Martin Fowler
- 《设计模式:可复用面向对象软件的基础》- GoF
持续学习,持续实践! 📚