1. 概述
软件建模与设计过程可拆分为需求分析、概要设计和详细设计三个阶段。我们往往需要在不同阶段输出不同的设计文档。这个过程,除了自身的业务理解能力外,建模工具UML必不可少,其中常用的有七种(“七武器”):类图、用例图、时序图、活动图、状态图、组件图和部署图。
适用阶段 | 模型图 | 说明 |
---|---|---|
需求分析: | ||
类图 | 关注领域对象识别及其关系 | |
活动图 | 描述业务流程 | |
用例图 | 通过反映用户和软件系统的交互来描述系统的功能需求 | |
时序图 | 通常用于表示对象之间的交互’这个对象可以是类对象,也可以是更大粒度的参与者’如组件、服务器、子系统等 | |
状态图 | 描述状态变迁的逻辑关系 | |
概要设计: | ||
活动图 | 描述子系统和组件的交互 | |
时序图 | 通常用于表示对象之间的交互’这个对象可以是类对象,也可以是更大粒度的参与者’如组件、服务器、子系统等 | |
组件图 | 粒度比较粗,通常用以描述和设计软件的模块及其之间的关系,需要在设计早期阶段画出来 | |
部署图 | 是整个软件设计模型中比较宏观的一种图,是在设计早期就需要画的一种模型图;还可以估算服务器和第三方软件的采购成本 | |
详细设计: | ||
类图 | 开发工程师按照类图实现代码即可 | |
状态图 | 这个时候状态要用枚举值表示,以指导具体的开发 | |
时序图 | 常用于描述系统内部详细的接口调用时序或业务模块数据流转过程 | |
活动图 | 可用于描述一个类方法内部的计算流程 |
2. 工具选型
2.1. UML工具
绘图工具总体分为两类,一类是可视化工具,一类是文本化最终编译为图片,以下是我用过的工具对比。
名称 | 类型 | 优点 | 缺点 |
---|---|---|---|
Visio | 可视化工具 | 功能齐全,有模板 | 连接线对于手残党不友好 |
Visual Paradigm | 可视化工具 | 功能齐全,有模板,专业,号称能用最新少鼠标点击完成画图操作,默认风格优美 | 最新版非免费 |
processon | 可视化工具 | 功能齐全,有模板,能多人在线协作,有APP版本 | 非免费,网页体验不好 |
PlantUML | 文本化工具 | 支持的模型图功能齐全,免费,手残党福音,相比拖拽绘图工具更利于专注业务建模,gitlab上提交的markdown文件可直接渲染出图片 | 依赖Graphviz,默认样式丑 |
mermaid | 文本化工具 | 免费,很多markdown编辑器默认支持,默认样式优美,手残党福音,相比拖拽绘图工具更利于专注业务建模 | 支持的模型图较少 |
最后,个人选择plantUML,它支持常用的七种UML图,通过定制样式也可以很优美,当然,最重要的是不用耗费部分精力在画图这个事情上。
PlantUML语法说明详见>>
2.2. 设计编写工具
2.2.1. 功能诉求
- 支持markdown。
- 支持markdown中渲染预览plantUML。
- 支持导出成HTML、PDF格式。
- 支持粘贴保存截图。
2.2.2. 工具选择
名称 | 类型 | 说明 |
---|---|---|
Visual Studio Code | 文本编辑器 | 独立安装,功能繁多,忽略介绍 |
Markdown Preview Enhanced | vsCode 插件 | 支持markdown预览、支持plantUML渲染、支持导出HTML和PDF、支持快速生成目录 |
Markdown-index | vsCode 插件 | 自动根据markdown标题生成或更新顺序数字 |
Paste Image | vsCode 插件 | 支持粘贴保存图片并转为markdown图片语法 |
Graphviz | 图渲染工具 | 需要单独下载安装(否则markdown preview enhanced中的plantuml无法渲染),并配置好环境变量GRAPHVIZ_DOT,取值如:D:\Software\Graphviz\bin\dot.exe |
3. “七武器”使用示例
以常见的电商下单支付场景为例,画出需求分析、设计过程中涉及的七种UML图。
使用PlantUML语法做出参考模板,并进行一定程度的美化(仿Visual Paradigm默认样式)。
3.1. 类图
刻画需求分析阶段的领域模型或者详细设计阶段的类图。
3.1.1. 对象图刻画领域模型
@startuml
' ========调整样式=============
skinparam object {
BackgroundColor #70CFF5
BorderColor #black
ArrowColor #black
}
' ========停止属性的可访问性样式=============
skinparam classAttributeIconSize 0
' ========定义模型=============
object 订单{
- 单号
- 订单状态
- 金额
}
object 订单明细 {
- 商品
- 价格
- 数量
- 金额
}
object 售后单{
- 类型
- 单号
- 状态
}
' ========定义关系=============
订单 "1"*-right-"n" 订单明细
订单 "1" o-down-"n" 售后单
@enduml
3.1.2. 类图刻画代码模型
@startuml
' ========调整样式=============
skinparam class {
BackgroundColor #70CFF5
BorderColor #black
ArrowColor #black
}
skinparam object {
BackgroundColor #70CFF5
BorderColor #black
ArrowColor #black
}
' ========定义模型=============
enum 订单状态{
待付款
待发货(已付款)
已发货
交易完成(已确认收货)
售后中
交易关闭
已取消
}
class 订单{
- 单号 : string
- 订单状态 : enum
- 金额 : double
+ 创建()
+ 支付()
+ 发货()
+ 确认收货()
}
class 订单明细 {
- 商品代码 : string
- 商品名称 : string
- 价格 : double
- 数量 : double
- 金额 : double
}
class 售后单{
- 类型 : enum
- 单号 : string
- 状态 : enum
}
class 售后单明细{
}
' ========定义关系=============
订单 "1"*-right-"n" 订单明细
订单 "1" o-down-"n" 售后单
售后单 "1"*-right-"n" 售后单明细
@enduml
3.2. 活动图
使用活动图刻画订单业务流程。
@startuml
' ========调整样式=============
' 单个状态定义示例:state 未提交 #70CFF5 ##Black
skinparam activity {
BackgroundColor #70CFF5
BorderColor #black
ArrowColor #black
}
skinparam activityDiamond {
BackgroundColor #70CFF5
BorderColor #black
}
' ========定义流程=============
|用户|
start
:筛选商品;
split
:添加购物车;
:进入购物车;
:购物车结算;
split again
:立即结算;
end split
:下单支付;
if (超时未支付) then (是)
:系统取消订单;
note right
订单状态:已取消
end note
stop
else (否)
repeat
:选择付款方式;
:确认支付;
repeat while(支付失败)
endif
:支付成功;
note left
订单状态:待发货
end note
|商家|
switch (商品库存)
case (没货)
:联系用户取消订单;
: 取消订单;
stop
case (部分缺货)
:缺货部分补发;
case (库存充足)
:备货;
endswitch
:物流交接;
:通知发货;
|物流|
:运输;
:配送;
|用户|
:确认收货;
stop
@enduml
3.3. 状态图
@startuml
' ========调整样式=============
' 单个状态定义示例:state 未提交 #70CFF5 ##Black
skinparam state {
BackgroundColor #70CFF5
BorderColor #black
ArrowColor #black
}
'连线样式:默认弯曲、ortho-折线、polyline-斜线
'skinparam linetype polyline
' ========定义流程=============
[*] -right-> 待付款 :下单
待付款 : initial
待付款 -right-> 待发货 : 支付
待发货 : paid
待发货 -right-> 已发货 : 商家发货
已发货 : shipped
已发货 -right-> 交易完成 : 已收货
交易完成 : received
待付款 -down-> 已取消 : 超时未支付
已取消 : canceled
待发货 -down-> 已取消 : 商家无货
交易完成 --> 售后中: 退/补/换货
售后中 : applying
售后中 -right-> 交易关闭 : 完成退款
交易关闭 : closed
交易关闭 -> [*]
已取消 --> [*]
@enduml
3.4. 时序图
以支付接口交互为例
箭头规则参照Visual Paradigam:同步调用=实线+实心粗箭头;返回值=虚线+细箭头;异步调用=实线+细箭头。
@startuml
' ========调整样式=============
' 单个状态定义示例:state 未提交 #70CFF5 ##Black
skinparam sequence {
ArrowColor red
LifeLineBorderColor blue
LifeLineBackgroundColor #A9DCDF
ParticipantBorderColor #black
ParticipantBackgroundColor #70CFF5
}
' ========定义流程=============
APP -> APP : 筛选并下单
activate APP
deactivate APP
autonumber 1
activate 订单服务
APP -> 订单服务 : 调用准备支付接口
activate APP
alt 首次调用
autonumber "<b>'1.1.'0"
订单服务 -> 订单服务 : 支付申请单 [类型-付款,状态-处理中]
订单服务 -->> APP : 返回业务的支付信息
else 二次调用
autonumber "<b>'1.2.1.'0"
alt 支付申请单[处理中]
订单服务 -->> APP : 直接返回业务的支付信息
else 支付申请单[处理失败]
autonumber "<b>'1.2.2.'0"
订单服务 -> 订单服务 :更新交易凭证号,且置支付申请单 [状态-处理中]
订单服务 -->> APP : 返回业务的支付信息
end
end
deactivate APP
deactivate 订单服务
autonumber 2
APP -> 第三方支付平台 : 调用支付前提交接口
activate 第三方支付平台
activate APP
alt 网络异常
autonumber "<b>'2.1.'0"
第三方支付平台 -->> APP : 网络异常,**转步骤2**
else 调用失败
autonumber "<b>'2.2.'0"
第三方支付平台 -->> APP : 返回失败信息
activate APP
APP -> 订单服务 : 调用记录失败结果接口
activate 订单服务
订单服务 -> 订单服务 : 支付申请单 [状态-处理失败]
订单服务-->> APP : 明确返回记录成功,**转步骤2**(失败需要重试)
deactivate 订单服务
deactivate APP
else 调用成功
autonumber "<b>'2.3.'0"
第三方支付平台 -->> APP : 返回提交成功结果,**转步骤3**
end
deactivate APP
deactivate 第三方支付平台
autonumber 3.1
APP -> 第三方支付平台 : 调用确认支付接口
activate 第三方支付平台
activate APP
alt 网络异常
autonumber "<b>'3.1.1.'0"
第三方支付平台 -->> APP : 网络异常,**转步骤3.1**,进行重试
else 调用失败
autonumber "<b>'3.1.2.'0"
第三方支付平台 -->> APP : 返回失败信息,**转步骤2.2.1**,记录失败结果
else 调用成功
autonumber "<b>'3.1.3.'0"
第三方支付平台 -->> APP : 返回完成支付信息,**转步骤3.2**,记录成功结果
APP -> 订单服务 : 调用确认完成支付
activate 订单服务
订单服务 -> 订单服务 : 置支付申请单位已完成,\n订单状态为待发货
订单服务 -->> APP : 返回订单完成支付信息
deactivate 订单服务
end
deactivate APP
deactivate 第三方支付平台
@enduml
3.5. 组件图
@startuml
' ========调整样式=============
' 单个状态定义示例:state 未提交 #70CFF5 ##Black
skinparam interface {
BackgroundColor #70CFF5
BorderColor #black
}
skinparam component {
FontSize 13
BackgroundColor #70CFF5
FontName Courier
BorderColor black
ArrowFontName Impact
ArrowColor #black
ArrowFontColor #777777
}
' ========定义内容=============
component [订单] as order
component [商品] as goods
component [支付申请单] as pay
component [库存] as inv
component [售后单] as rtn
component [促销] as prom
order ..> goods
order ..> prom
prom ..> goods
order ..> pay
order ..> inv
rtn ..> order
@enduml
3.6. 部署图
@startuml
' ========调整样式=============
' 单个状态定义示例:state 未提交 #70CFF5 ##Black
skinparam usecase {
BackgroundColor #70CFF5
BorderColor #black
ArrowColor #black
}
skinparam actor {
BackgroundColor #70CFF5
BorderColor #black
}
skinparam component {
BackgroundColor #70CFF5
BorderColor black
ArrowColor #black
ArrowFontColor #777777
}
skinparam cloud {
BackgroundColor #70CFF5
BorderColor black
ArrowColor #black
ArrowFontColor #777777
}
skinparam database {
BackgroundColor #70CFF5
BorderColor black
ArrowColor #black
ArrowFontColor #777777
}
skinparam rectangle{
roundCorner 25 ' 设置圆角
}
' ========定义内容=============
rectangle 前台 as userNode #line.dashed;{
component "APP" as app
component "小程序" as miniApp
'通过hidden控制水平布局
app -[hidden]left- miniApp
component "web端" as pcWeb
miniApp -[hidden]right- pcWeb
}
rectangle 中台 #line.dashed;{
cloud 阿里云SLB as slb
node 中台网关云服务器 as gatewayNode{
component [api-gateweay] as gateway <<API服务网关>>
}
node 订单云服务器 as orderNode{
component [order-web] as orderWeb <<订单Web服务>>
component [order-service] as order <<订单中台服务>>
}
node 库存云服务器 as invNode{
component [inv-service] as inv <<库存中台服务>>
}
database 订单中台云数据库 as orderDS <<MySQL>>
database 库存中台云数据库 as invDS <<PostgreSQL>>
}
rectangle 后台 #line.dashed;{
node ERP服务器 as erp{
component [inv-transfer] as erpInvTransfer <<ERP库存中台交换服务>>
component [order-transfer] as erpOrderTransfer <<ERP订单中台交换服务>>
}
database ERP数据库 as erpDS <<Oracle>>
}
userNode -- slb : HTTPS
slb -- gatewayNode
gateway -->order
order -up-> orderDS
orderWeb -right-> order
gateway -->inv
inv -up-> invDS
inv ..> erpInvTransfer
order ..> erpOrderTransfer
inv -left- order
erpInvTransfer --> erpDS
erpOrderTransfer --> erpDS
@enduml
3.7. 用例图
开发阶段用的较少,PS:记箭头关联关系表示好累 _。
@startuml
' ========调整样式=============
' 单个状态定义示例:state 未提交 #70CFF5 ##Black
skinparam usecase {
BackgroundColor #70CFF5
BorderColor #black
ArrowColor #black
}
skinparam actor {
BackgroundColor #70CFF5
BorderColor #black
}
' ========定义内容=============
left to right direction
actor "用户" as user
usecase "网上购物" as ecBuy
usecase "下订单" as order
usecase "搜索商品" as searchGoods
usecase "添加购物车" as addBasket
usecase "支付" as pay
usecase "取消支付" as cancelPay
' ========定义内容=============
' 箭头关系:--> 依赖, <|--泛化(继承) ,..> 包含或者扩展
user --> ecBuy
ecBuy <|-- order
order ..> searchGoods : <<包含>>
order ..> addBasket : <<包含>>
order ..> pay : <<包含>>
cancelPay ..> pay : <<扩展>>
@enduml