plantUML入门

uml

uml: unified module language,统一建模语言。它制定了一系列图表的样式,来描述软件的结构和流程。通过uml图,软件开发者可以方便、清晰地使用图表来描述软件的设计,方便彼此交流。

uml中常用的图表:

  • 类图: 描述类之间的结构
  • 组件图: 描述系统范围哪些组件,例如订单模块、支付模块,以及组件之间的调用关系
  • 活动图: 类似流程图,展示复杂的业务流程
  • 时序图: 组件之间按照时序的调用过程
  • 用例图: 展示系统有哪些功能,以及角色的操作流程

plantUML简介

plantUML: 一个开源工具,能够让用户用文本的方式来绘制uml图表,方便图表的制作,同时,文本还可以接入版本管理系统,方便管理。

安装: 我是在vscode中安装了plantUML相关的插件

plantUML文件的后缀名是 puml

基本语法

图表的开始和结束: @startuml、@enduml,图表的开始标记和结束标记,所有的内容都要在这个标记内

图表中的注释: ' ,单引号后面的内容就是注释,单引号必须位于一行的开头

绘制图表

类图

用于展示类的静态结构,包括类的属性、方法、类与类之间的关联关系

基础语法

声明一个类:

text 复制代码
class 类名 {
    - 属性: 类型
    + 方法(参数: 类型): 返回值
    {static} + 静态方法(参数: 类型): 返回值
}

访问修饰符:

  • + : public
  • - : private
  • # : protected
  • ~ : 包访问权限

类和类之间的关系:

  • 继承/泛化 : --|> A是B的子类
  • 实现: ...|>,A实现B
  • 聚合: o--,A包含B,但B可以独立存在
  • 组合: *--,A包含B,B不能独立于A存在
  • 关联: -->,A知道B,例如成员变量引用
  • 依赖: ...>,A使用B,例如方法参数、局部变量
  • 双向关联: <-->,双方互相直到,互有成员引用

多重性: 一个类的实例可以与另一个类的多少个实例建立关系, 例如 User "1" --> "0...*" Order : 一个用户拥有零个或多个订单

  • 1 : 恰好一个
  • 0...* : 零个或多个
  • n...m : n到m个
案例: 属性和属性分组的类图

案例: 属性和属性分组是两个独立的实体,它们之间有一张关联表,画出它们的类图

java 复制代码
// 属性
public class Attr {
    private Long id;
    // 属性名
    private String name;
    // 排序字段
    private Integer sort;
} 

// 属性分组
public class AttrGroup {
    private Long id;
    // 分组名
    private String name;
    // 排序字段
    private Integer sort;
}

// 属性和属性分组的关联表
public class AttrAndAttrGroupRelation {
    private Long id;
    // 属性分组id
    private Long attrGroupId;
    // 属性id的集合
    private Long attrId;
}

类图:

puml 复制代码
@startuml 属性和属性分组类图

' left to right direction  从左到右布局
 
title 属性和属性分组类图

' 实体类

' 布局设计,属性和属性值放到一起
together {
    class Attr {
        - id: Long
        - name: String  // 属性名
        - sort: Integer // 排序字段
    }
    
    class AttrGroup {
        - id: Long
        - name: String   // 属性分组名
        - sort: Integer  // 排序字段
    }
}

class AttrAndAttrGroupRelation {
    - id: Long
    - attrGroupId: Long  // 属性分组id
    - attrId: Long       // 属性id
}

' 实体类之间的关系

Attr "1" --> "1" AttrAndAttrGroupRelation : 一个属性只可以\n关联一个属性分组
AttrGroup "1" --> "0..*" AttrAndAttrGroupRelation : 一个属性分组下\n有零个或多个属性

AttrAndAttrGroupRelation --> Attr : 引用 (attrId)
AttrAndAttrGroupRelation ..> AttrGroup : 引用 (attrGroupId)

' ========== 注释 ==========
note top of Attr
    属性
end note

note top of AttrGroup
    属性分组
end note

@enduml

组件图

展示系统的物理结构,即系统由哪些组件组成、组件之间的依赖关系

基本语法

组件图中的组件:

  • 包: 逻辑分层
  • 组件: 可独立部署的模块
  • 接口: 组件提供的服务
  • 端口: 组件对外交互的出入口

组件之间的关系:

  • 关联: -->,强关系,A 知道 B 的存在,B 是 A 的一部分或长期依赖
  • 依赖: ...>,弱关系,A 临时使用 B,用完即走
  • 实现: ...|>,组件实现了某个接口

声明组件名称: [组件名称] as 别名

案例: 系统部署组件图

puml 复制代码
@startuml 系统部署组件图(优化)

title 系统部署组件图

' ========== 用户(外部) ==========
actor "用户" as User

' ========== Web服务器层 ==========
package "Web服务器(Nginx)" #LightBlue {
    [静态资源] as static
    [反向代理] as proxy
    [负载均衡] as LB
}

' ========== 应用服务器层 ==========
package "应用服务器(Tomcat)" #LightGreen {
    [Spring Boot] as App1
    [Spring Boot] as App2
}

' ========== 数据库服务器层 ==========
package "数据库服务器" #LightCoral {
    [MySQL] as DB1
    [MySQL] as DB2
    [Redis] as Cache
}

' ========== 消息服务器层 ==========
package "消息服务器" #LightPink {
    [RabbitMQ] as MQ
}

' ========== 连接关系 ==========
' 用户通过域名访问 Web 服务器(负载均衡器)
User --> LB : 访问 Web 服务器

' Web 服务器内部处理
LB --> App1 : 负载均衡转发
LB --> App2 : 负载均衡转发

' 应用服务器访问数据库
App1 --> DB1 : 读写数据
App2 --> DB2 : 读写数据
App1 --> Cache : 缓存读写
App2 --> Cache : 缓存读写

' 应用服务器发送消息
App1 --> MQ : 发送消息
App2 --> MQ : 发送消息

@enduml

活动图(流程图)

语法

开始/结束: start、end,必须成对出现

普通操作: : 操作内容 ; , 操作内容以冒号开头,以分号结尾

条件判断:

text 复制代码
if (条件) then (是)
    : 操作内容;
else (否)
    : 操作内容2;
endif

循环:

text 复制代码
while(条件) is(是)
    :操作内容;
endwhile

并行处理: fork 、 fork again 、 end fork

组件: 表示以下操作都是在该组件中完成的

text 复制代码
partition 组件名称 {
    : 操作;
}

节点的注释:

text 复制代码
note right  // right表示注释出现在右边,注释内容注意换行,一行不要太长
    注释内容
end note

退出流程: stop

案例 快速排序的流程图

这里先简单介绍一下快排的流程,尝试根据下面的流程,画出流程图。

快排的流程:

  • 选定一个数作为基准数,通常是数组最左边的数
  • 然后再初始化两个指针,指向数组的开头和结尾
  • 一趟交换:
    • 结尾的指针先向前寻找,找比基准数小的元素,
    • 然后开头指针向后寻找,找比基准数大的元素,
    • 如果指针没有相遇,交换这两个指针处的值,
    • 然后继续寻找,直到指针相遇,
    • 然后交换指针处的值和基准数,(因为是结尾的指针先向前寻找,所以此时指针指向的元素一定小于基准数,或者后面的元素都比基准数大,指针指向基准数本身。一趟寻找完成以后,数组被分为两部分)
    • 继续寻找直到左右指针相遇
  • 然后继续对这两部分递归地进行快排,直到每一部分都为1,此时,整个数组有序。

流程图如下:

puml 复制代码
@startuml 快速排序

title 快速排序的流程图

start

: 选定一个数作为基准数 base, 通常是数组中的第一个元素;
: 初始化两个指针 左指针 l、 右指针 r 指向数组的开头和结尾 ;
if (左指针 == 右指针) then (是) 
    : 退出排序;

    ' 退出程序
    stop  
else (否)

endif 

partition 一趟交换 {
    while (左指针 < 右指针) is (进入循环)
        : 右指针向前寻找;
        while (循环查找: 左指针< 右指针 && 右指针处的元素 > 基准数) is (是) 
            : 右指针向前移动;
        endwhile(否)

        : 左指针向后寻找 ;
        while (循环查找: 左指针 < 右指针 && 左指针处的元素 < 基准数) is (是)
            : 左指针向后移动;
        endwhile(否)

        if (左指针 != 右指针) then (是) 
            : 交换左右指针处的值;
            : 左指针后移;
            : 右指针前移;
        endif
    endwhile(左右指针重合,退出循环)

    partition 基准数和右指针处的值交换 {
        : 右指针处的值赋值给数组开头;
        : 基准数(base)赋值给右指针处的值;
        note right 
            因为是右指针先向前寻找,所以当左右
            指针重合后,指针指向的元素一定小于
            基准数,或者后面的元素都比基准数大,
            指针指向基准数本身。交换基准数和
            右指针处的值,因为基准数是数组开头的
            元素,所以先把右指针处的值赋值给数组
            开头,再把基准数的值赋值给右指针处
        end note
    }
}

: 一趟交换之后,数组从右指针处被分为两部分;
: 递归地对左边进行快排;
note right
    左边是从数组开头到右指针减1,
    它们都比基准数小
end note

: 递归地对右边进行快排;
note right 
    右边是右指针加1到数组结尾,
    它们都比基准数大
end note

end

@enduml

案例2 多分支流程

需求: 用户在界面上的操作按钮,有按钮1、按钮2、按钮3,每个按钮都会执行不同的操作,我想要画一个流程图,描述用户在点击不同的按钮后,进行的不同操作

流程图:

puml 复制代码
@startuml 并行操作的示例

title 并行操作的示例

start

: 用户进入页面;
: 页面弹出三个按钮;

fork
    : 用户点击按钮1;
    : 按钮1返回hello world;
fork again
    : 用户点击按钮2;
    : 按钮2返回java;
fork again
    : 用户点击按钮3;
    : 按钮2返回paython;
end fork 

: 流程结束;
end

@enduml

在快排的案例中,没有展示出并行流程,例如,流程1、流程2、流程3是独立的,这里补充一个案例,描述这种流程。

时序图

基础语法

声明参与者的关键字:

  • participant : 普通参与者,矩形
  • actor : 角色用户,小人
  • boundary : 边界
  • entity : 实体
  • database : 数据库

声明参与者的语法:

text 复制代码
actor "用户" as User   // 随后使用User来代表时序图中的一个组件

对消息编号: autonumer

消息类型:

  • -> : 实线实心箭头,代表同步请求
  • --> : 虚线实心箭头,代表异步请求

激活、销毁对象: activate、deactivate,表示参与者在特定时间段的激活状态

text 复制代码
activate 角色
A -> B: A调用B
deactivate 角色

alt分支: if else

text 复制代码
client -> server : 提交支付
alt 余额充足
    server -> client : 支付成功
else 余额不足
    server -> client : 支付失败
end 

opt: 可选分支,有if无else

text 复制代码
opt 开启短信通知
    server -> sms : 发送短信
end

loop: 循环

text 复制代码
loop 重试3次
    client -> server : 上传文件

par: 并行分支

text 复制代码
par 分支1: 写数据库
    server -> db : 写数据库
par 分支2: 写缓存
    server -> cache : 写缓存
end
案例 登录流程

用户登录的流程: 用户进入登录页面,输入账号、密码进行登录,最多允许重试三次,登录成功后,后端生成jwt token,返回给前端

流程图:

puml 复制代码
@startuml 用户登录流程

title 用户登录流程

' 声明参与者
actor "用户" as User
participant "前端页面" as Frontend
participant "登录服务" as LoginService
database "数据库" as DB
database "缓存" as Cache 

' 开始交互
User -> Frontend : 进入登录页面
User -> Frontend : 输入 用户名 + 密码

activate Frontend

Frontend -> Frontend : 校验用户输入数据的格式
alt 校验不通过
    Frontend -> User : 输入不正确

else 校验通过
    Frontend -> LoginService : PSOT /login , 用户名 + 密码,\n密码使用算法加密
    LoginService -> Cache : 查询是否被锁定
    
    activate Cache
        Cache -> LoginService : 返回锁定状态
    deactivate Cache

    alt 账号被锁定
        LoginService -> Frontend : 返回"账号被锁定"
        Frontend -> User : 向用户展示错误信息
    else 未锁定
        LoginService -> DB : 根据用户名查询用户信息

        activate DB
        DB -> LoginService : 返回用户信息
        deactivate DB

        LoginService -> LoginService : 校验密码是否一致

        alt 密码错误
            LoginService -> Cache : 失败次数加1

            activate Cache 
                Cache -> LoginService : 返回当前的失败次数
            deactivate Cache
        
            alt 失败次数 >= 3次
                LoginService -> Frontend : 错误次数超过3次,当前账号已被锁定
                Frontend -> User : 展示错误信息,账号被锁定
            else 失败次数 < 3次
                LoginService -> Frontend : 返回账号密码错误
                Frontend -> User : 展示错误信息,账号密码错误
            end
        else 密码正确
            LoginService -> Cache : 清除失败次数

            activate Cache
                Cache -> LoginService : 返回清除结果
            deactivate Cache

            LoginService -> LoginService : 生成 jwt token
            LoginService -> Cache : 把token保存到缓存中

            activate Cache
                Cache -> LoginService : 返回保存结果
            deactivate Cache

            LoginService -> Frontend : 返回登录成功、token
            Frontend -> User : 跳转到系统首页

            User -> Frontend : 用户在登录态操作页面,\n每次请求都会携带token,\n后端根据token从缓存中获取用户信息
        end
    end    
end
deactivate Frontend

@enduml

用例图

展示系统对外提供的基本功能和可以操作这些功能的不同角色,还可以展示不同功能之间的关联关系。

基础语法

定义参与者: actor, actor "用户名称" as 别名,表示使用系统的人,用户名称在图片上展示,别名在文件内部使用

声明用例: usecase, usecase "用例名称" as 别名 ,一个用例就是系统的一个功能

系统边界: package

text 复制代码
package 订单管理系统 {
    usecase "创建订单" as UC1
    usecase "取消订单" as UC2
}

注释: note left/right of 元素

关联关系: --> , 表示角色和用例之间的调用关系,用户别名 --> 用例别名,角色可以调用这个功能

用例之间的关系:

  • 一个用例包含另一个用例: include, ...> , UC4(下单) ..> UC1(登录) : <<include>>,执行下单时,必须先登录
  • 一个用例在特定条件下可以扩展另一个用例: extend, ...>,UC5(支付) ..> UC16(使用优惠券) : <<extend>>,执行"支付"时,在特定条件下可以执行"使用优惠券"
  • 用例之间的父子关系:
text 复制代码
actor "支付方式" as Payment
usecase "微信支付" as WX
usecase "支付宝支付" as Ali
WX --> Payment
Ali --> Payment

解释: 微信支付和支付宝支付都是支付方式的具体实现

案例: 图书管理系统

图书管理系统: 有两个角色,管理员和用户,用户可以查询、借阅图书,管理可以对图书进行增删改查

puml 复制代码
@startuml 图书管理系统用例图

title 图书管理系统用例图

actor "读者" as reader 
actor "管理员" as admin

package 图书管理系统 {
    usecase "查询图书" as query_book
    usecase "借阅图书" as borrow_book
    usecase "归还图书" as return_book
    usecase "续借图书" as continue_borrow_book
    usecase "管理图书" as admin_book
    usecase "管理读者" as admin_reader
    usecase "罚款管理" as admin_penalty
}

reader --> query_book
reader --> borrow_book
reader --> return_book
reader --> continue_borrow_book

admin --> admin_book
admin --> admin_reader
admin --> admin_penalty

borrow_book ..> query_book : <<include>>\n(先查询才能借阅)
return_book ..> borrow_book : <<include>>\n(先借阅才能归还)
continue_borrow_book ..> borrow_book : <<include>>\n(先借阅才能续借)

admin_penalty ..> return_book : <<extend>>\n超时归还触发罚款

@enduml

总结

uml提供的图表类型还有很多,plantUML支持的更多,这里只介绍最常见的几种

相关推荐
电子科技圈3 小时前
先进封装、芯粒架构和3D集成——先进异构集成亟需兼具标准化与定制化能力的互联及总线IP解决方案
tcp/ip·设计模式·架构·软件构建·代码规范·设计规范
森宇设计8 天前
普通办公室装修多少钱?基础型、实用型和品质型有什么区别
设计规范·装修
X-⃢_⃢-X9 天前
七、数据库的设计规范
数据库·mysql·架构·设计规范
CoreTK芯通康EMC整改10 天前
《2026 电子制造业 EMC 合规白皮书》解读:PCB EMC 设计全链路优化指南(附器件选型矩阵)
人工智能·硬件工程·设计规范·emc整改案例·emc整改
花开彼岸天~11 天前
鸿蒙实战:导航栏与顶部栏设计规范 HmTitleBar
华为·harmonyos·鸿蒙系统·设计规范
UXbot1 个月前
如何选择适合公司项目的UI设计工具?企业选型指南
前端·低代码·ui·团队开发·原型模式·设计规范·web app
糖果店的幽灵1 个月前
软件测试接口测试从入门到精通:RESTful API设计规范
软件测试·后端·接口测试·restful·设计规范·api设计
凌云拓界1 个月前
状态机与思考循环 ——CogitoAgent开发实战(一)
javascript·人工智能·架构·node.js·设计规范
秦明月132 个月前
EPLAN部件库整理之维护篇----部件库整理收尾:做好日常维护,再也不用反复重做
经验分享·其他·职场和发展·学习方法·设计规范