目录
- 序
- UML图之顺序图
- 顺序图的四个要素
- 关于消息箭头的语法
- Mermaid中顺序图的简单例子
- 样例
- 用小人表示对象
- 为对象设置别名
- 激活对象
- UML图之类图
- 类图中常见的关系
- 关于不同类型关系的语法
- Mermaid中类图的简单例子
- 样例
- 类定义的两种方式
- 为类定义成员
- 双向关系的表示
- 多重性关系的表示
- UML之状态图
- 状态图的构成要素
- Mermaid中顺序图的简单例子
- 样例
- 状态的表示
- 转移的表示
- 开始和结束的表示
- 判断的表示
- 同步的表示
序
Mermaid(中译为美人鱼,就好比一条美人鱼在流动构成了各种的图),是一种在MarkDown中以特定格式的文字生成各种图示的方法。
接着之前写过的MarkDown中写流程图的方法这篇博客,经过了三年,Mermaid也是接连更新了软件工程中常用的顺序图、类图、状态图等UML图,E-R图以及项目管理中常用的甘特图、饼图,还有常用版本管理工具Git的合并策略图也可以画出来了!这真是为开发者们徒增不少便利啊!
只有你想不到,没有Mermaid做不到,当然这不是打广告!就连思维导图Mermaid也支持啦!虽然XMind用来画思维导图是更加方便的,不过多掌握一项技能又何尝不可呢!
这篇博客将为大家介绍如何绘制常见UML图,诸如顺序图、类图、状态图。
UML图之顺序图
顺序图又叫时序图、序列图,即以时间为主线,有生命线的动态视图,它显示了各个进程之间是如何运作的,以及它们是以什么顺序运作的。
顺序图的四个要素
①对象
:类的实例化。
②生命线
:对象存在的时间。
③消息
:对象之间靠消息传递信息和指令,用从一个对象的生命线到另一个对象生命线的箭头表示消息。
④激活
:这个时间,对象可以实现操作。对象存在时生命线用一条虚线表示,当对象的过程处于激活状态,生命线用双道线表示。
接下来介绍如何使用Mermaid画顺序图。
关于消息箭头的语法
消息箭头类型 | 表达含义 | 示意图 |
---|---|---|
-> | 不带箭头的实线 | / |
--> | 不带箭头的点虚线 | / |
->> | 带箭头的实线 | |
-->> | 带箭头的点虚线 | |
-x | 尾部带十字叉的实线 | |
--x | 尾部带十字叉的点虚线 | |
-) | 尾部是开箭头的实线(异步) | |
--) | 尾部是开箭头的点虚线(异步) |
Mermaid中顺序图的简单例子
样例
在下面这段文字(代码块中)的前面和后面一行各自添加```(键盘左上,与~共用一个键)即可生成如下图的展示结果(注意在使用时需要增加mermaid
标识,加在前面那三点的后面)。
sequenceDiagram
学生->>老师: 老师,请问这道题的解题思路是什么?
老师-->>学生: 来,你看这边,是这样的……
学生-)老师: 醍醐灌顶!谢谢老师!!
注意:end这个词会与Mermaid语法冲突,如果必须要使用到,需将这样使用:(end)
或[ end ]
或{ end }
.
用小人表示对象
上面的例子是用长方形和文字来表示一个对象的,如果要用更加形象的小人来表示对象,那么在使用对象前,用actor
来声明即可。
sequenceDiagram
actor 学生
actor 老师
学生->>老师: 老师好!
老师-->>学生: 你好~
为对象设置别名
如果觉得对象名太长太复杂,那么可以使用participant
···as
···的语法为对象名设置一个简要的别名。
sequenceDiagram
participant A as 学生
participant B as 老师
A->>B: 老师好!
B-->>A: 你好~
这样画出来的图没有区别,但是写法上会简单很多。
激活对象
如果想要激活对象,那么使用activate
;取消对象的激活状态,则使用deactivate
。
sequenceDiagram
actor A as 学生
actor B as 系统
A->>B: 录入信息
activate B
B-->>A: 更新信息
deactivate B
可以使用+
/-
简化激活/未激活状态。
sequenceDiagram
actor A as 学生
actor B as 系统
A->>+B: 录入信息
B-->>-A: 更新信息
UML图之类图
在面向对象的模型中,类图常用来表达系统的类、属性、操作(又叫方法)以及类之间的关系。
类图中常见的关系
①泛化(Generalization)
:一种继承关系,即一般与特殊的关系,它指定了子类如何特化父类的所有特征和行为。
表示:带三角箭头的实线,箭头指向父类。
②实现(Realization)
:一种类与接口的关系,即类是接口所有特征和行为的实现。
表示:带三角箭头的虚线,箭头指向接口。
③关联(Association)
:一种拥有关系,它使一个类知道另一个类的属性和方法。
表示:带普通箭头的实心线,指向被拥有者。
④聚合(Aggregation)
:整体与部分的关系,且部分可以离开整体而单独存在。
注:聚合关系是关联关系的一种,是强的关联关系;关联和聚合在语义上无法区分,必须考察具体的逻辑关系。
表示:带空心菱形的实心线,菱形指向整体。
⑤组合(Composition)
:整体与部分的关系,但部分不能离开整体而单独存在。
注:组合关系也是关联关系的一种,但相比于聚合,组合是一种更强的关联关系。
表示:带实心菱形的实线,菱形指向整体。
⑥依赖(Dependency)
:一种使用的关系,即一个类的实现需要另一个类的协助,因此尽量不要使用双向的互相依赖。
表示:带箭头的虚线,指向被使用者。
各种关系的强弱顺序:
泛化 = 实现 > 组合 > 聚合 > 关联 > 依赖
关于不同类型关系的语法
关系 | 表达含义 | 示意图 |
---|---|---|
<|-- | 继承 | |
*-- | 组合 | |
o-- | 聚合 | |
--> | 关联 | |
-- | 实线连接 | |
..> | 依赖 | |
..|> | 实现 | |
.. | 虚线连接 |
Mermaid中类图的简单例子
样例
在下面这段文字(代码块中)的前面和后面一行各自添加```(键盘左上,与~共用一个键)即可生成如下图的展示结果(注意在使用时需要增加mermaid
标识,加在前面那三点的后面)。
classDiagram
动物 ..< 水
动物 ..< 氧气
动物 <|-- 鸟
鸟 <|-- 老鹰
鸟 <|-- 白鹭
鸟 <|-- 天鹅
鸟 "1"*-->"2" 翅膀
天鹅群"1"o-->"n"天鹅
天鹅..|>飞翔
白鹭"1"-->"1"气候
动物: + 生命
动物: + 性别
动物: +新陈代谢(int 氧气,int 水)
动物: +繁衍后代()
飞翔: +飞()
class 鸟{
+羽毛
+进食()
+下蛋()
}
class 老鹰{
-鹰眼视力
-抓田鼠()
-下蛋()
}
class 白鹭{
+野生自然
+捕鱼()
+下蛋()
}
class 天鹅{
+黑色与白色
+捕鱼()
+下蛋()
}
类定义的两种方式
- 使用关键词
class
来定义一个类。
如class Animal
可以定义一个Animal的类:
classDiagram
class Animal
- 通过两个类之间的关系可以同时定义两个类。
如Vehicle <|-- Car
表示Car类继承自Vehicle类:
classDiagram
Vehicle <|-- Car
需要注意的是,类名应该只由字母、数字、字符(包括Unicode)和下划线组成。
为类定义成员
类通常有其特有的属性和方法,方法用到的参数通常在后面用()
括起来。
而属性与方法的访问级别修饰符表示通常在冒号后面用一个符号来表示,对应的关系如下表所示:
符号 | 访问级别修饰符 |
---|---|
+ | Public |
- | Private |
# | Protected |
~ | Package/Internal |
如果想表示抽象方法或者是静态方法或静态变量,可以分别使用*
和$
符号标明:
- 抽象方法:
someAbstractMethod()*
- 静态方法:
someStaticMethod()$
- 静态变量:
String someField$
同样的,有两种方法来定义类的成员:
- 在类后面使用
:
,冒号后面跟类成员。这种方式适用于一次定义一个成员。
classDiagram
class Person
Person: +String name
Person: +int age
Person: +work(time) bool
Person: +study(time) int
- 使用
{}
将类成员括起来。这种方式适用于一次定义多个成员。
classDiagram
class Person{
+String name
+int age
+work(time) bool
+study(time) int
}
注:如果方法有返回值,那么返回值类型可以在方法后加一个空格并加上其返回值类型。
如果成员变量中含有泛型,那么像List<int>
是这样表示的:List~int~
。即使用了~ ~
来表示了< >
。
classDiagram
class Square~Shape~{
int id
List~int~ position
setPoints(List~int~ points)
getPoints() List~int~
}
Square : +List~string~ messages
Square : +setMessages(List~string~ messages)
Square : +getMessages() List~string~
Square : +getDistanceMatrix() List~List~int~~
双向关系的表示
关系可以表示逻辑上N:M的联系。
一个简单的例子如下:
classDiagram
Animal <|--|> Zebra
双向关系的语法格式:
[关系类型][连接][关系类型]
其中关系类型如下表所示:
关系类型 | 说明 |
---|---|
<| | 继承 |
\* | 组合 |
o | 聚合 |
> | 关联 |
< | 关联 |
|> | 实现 |
其中连接如下表所示:
连接类型 | 说明 |
---|---|
-- | 实线 |
.. | 虚线 |
多重性关系的表示
类图中的多重性或者叫基数表示一个类有多少实例可以与另一个类的实例形成关系。
不同的基数表示方法:
1
:一个0..1
:零个或一个1..*
:一个或多个*
:零个或多个n
:n个0..n
:零到n个1..n
:一到n个
基数可以在箭头表示的关系左右加上引号和以上的基数表示:
classDiagram
Customer "1" --> "*" Ticket
Student "1" --> "1..*" Course
Galaxy --> "many" Star : Contains
UML之状态图
状态图主要用于描述一个特定的对象的所有可能状态以及由于各种事件的发生而引起的状态之间的转换。
状态图的构成要素
状态(States)
:在对象的生命周期中满足某些条件、执行某些活动或等待某些事件的一个条件或状况。所有的对象都有状态,状态是对象执行了一系列活动的结果,当某个事件发生后,对象的状态将发生变化。
状态图中可以包含0个多个开始状态,也可以包含多个结束状态。模型不必同时具有开始和结束状态,因为模型可以总是运行,从不停止。转移(Transitions)
:两个状态之间的一种关系,表示对象将在第一个状态中执行一定的动作并在某个特定事件发生或某个特定条件满足时进入第二个状态。
事件
:使状态发生变化的某时刻发生的动作或活动,用来指示是什么触发了转移从而导致状态发生了改变。事件通常在从一个状态到另一个状态的转移路径上直接指定。判断
:判断点通过对事件判断分组转移到各自方向,提高了状态图的可视性。
同步
:使用同步和活动图一样是为了说明并发工作流的分叉与联合。
注:状态图重点在与描述对象的状态及其状态之间的转移,与活动图区别在于状态图注重的是行为的结果,活动图更注重是行为的动作。
Mermaid中顺序图的简单例子
样例
在下面这段文字(代码块中)的前面和后面一行各自添加```(键盘左上,与~共用一个键)即可生成如下图的展示结果(注意在使用时需要增加mermaid
标识,加在前面那三点的后面)。
stateDiagram
[*] --> 用户登录
用户登录 --> 登录成功:验证通过
登录成功 --> 填写信息:注册新卡
填写信息 --> 存入数据库:信息合理
存入数据库--> 提示成功:保存成功
提示成功 --> 退出系统:退出
退出系统-->[*]
状态的表示
状态的表示有多种方式:
- 最简单的是用一个名字来定义一个状态:
stateDiagram-v2
stateId
- 如果要简写状态名,则可以使用以下方式:
stateDiagram-v2
state "StateA" as s1
s1 -->StateB
这样在后续就可以直接使用s1来表示StateA了。
3. 简写状态名还可以用冒号的方式:
stateDiagram-v2
s1 : StateA
s1 -->StateB
转移的表示
转移使用箭头-->
表示。
当在两个状态间定义一个转移时,这两个状态也同时会被定义。
stateDiagram-v2
s1 --> s2
如果想在转移上添加文字,那么在后面加上一个冒号后写上想添加的文字即可。
stateDiagram-v2
s1 --> s2: A transition
开始和结束的表示
在状态图中,开始和结束状态是两个特殊的状态。它们都用[*]
来表示。
stateDiagram-v2
[*] --> s1
s1 --> [*]
判断的表示
如果状态图中涉及到了判断,那么可以使用<<choice>>
来表示。
stateDiagram-v2
state if_state <<choice>>
[*] --> IsPositive
IsPositive --> if_state
if_state --> False: if n < 0
if_state --> True : if n >= 0
同步的表示
对于同步的表示,则使用到了<<fork>>
和<<join>>
。
stateDiagram-v2
state fork_state <<fork>>
[*] --> fork_state
fork_state --> State2
fork_state --> State3
state join_state <<join>>
State2 --> join_state
State3 --> join_state
join_state --> State4
State4 --> [*]