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 天鹅{
        +黑色与白色
        +捕鱼()
        +下蛋()
    }
1
2
1
n
1
1
动物
+ 生命
+ 性别
+新陈代谢(int 氧气,int 水)
+繁衍后代()
氧气
+羽毛
+进食()
+下蛋()
老鹰
-鹰眼视力
-抓田鼠()
-下蛋()
白鹭
+野生自然
+捕鱼()
+下蛋()
天鹅
+黑色与白色
+捕鱼()
+下蛋()
翅膀
天鹅群
飞翔
+飞()
气候

类定义的两种方式

  1. 使用关键词class来定义一个类。
    class Animal可以定义一个Animal的类:
classDiagram
    class Animal
Animal
  1. 通过两个类之间的关系可以同时定义两个类。
    Vehicle <|-- Car表示Car类继承自Vehicle类:
classDiagram
    Vehicle <|-- Car
Vehicle
Car

需要注意的是,类名应该只由字母、数字、字符(包括Unicode)和下划线组成。

为类定义成员

类通常有其特有的属性和方法,方法用到的参数通常在后面用()括起来。

而属性与方法的访问级别修饰符表示通常在冒号后面用一个符号来表示,对应的关系如下表所示:

符号访问级别修饰符
+Public
-Private
#Protected
~Package/Internal

如果想表示抽象方法或者是静态方法或静态变量,可以分别使用*$符号标明:

  • 抽象方法:someAbstractMethod()*
  • 静态方法:someStaticMethod()$
  • 静态变量:String someField$

同样的,有两种方法来定义类的成员:

  1. 在类后面使用:,冒号后面跟类成员。这种方式适用于一次定义一个成员。
classDiagram
class Person
Person: +String name
Person: +int age
Person: +work(time) bool
Person: +study(time) int
Person
+String name
+int age
+work(time) : bool
+study(time) : int
  1. 使用{}将类成员括起来。这种方式适用于一次定义多个成员。
classDiagram
class Person{
    +String name
    +int age
    +work(time) bool
    +study(time) int
}
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~~
Square<Shape>
int id
List<int> position
+List<string> messages
setPoints(List<int> points)
getPoints() : List<int>
+setMessages(List<string> messages)
+getMessages() : List<string>
+getDistanceMatrix()

双向关系的表示

关系可以表示逻辑上N:M的联系。
一个简单的例子如下:

classDiagram
    Animal <|--|> Zebra
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
1
*
1
1..*
n
Customer
Ticket
Student
Course
Galaxy
Star

UML之状态图

状态图主要用于描述一个特定的对象的所有可能状态以及由于各种事件的发生而引起的状态之间的转换。

状态图的构成要素

  • 状态(States):在对象的生命周期中满足某些条件、执行某些活动或等待某些事件的一个条件或状况。所有的对象都有状态,状态是对象执行了一系列活动的结果,当某个事件发生后,对象的状态将发生变化。
    状态图中可以包含0个多个开始状态,也可以包含多个结束状态。模型不必同时具有开始和结束状态,因为模型可以总是运行,从不停止。
  • 转移(Transitions):两个状态之间的一种关系,表示对象将在第一个状态中执行一定的动作并在某个特定事件发生或某个特定条件满足时进入第二个状态。
StateA
StateB
  • 事件:使状态发生变化的某时刻发生的动作或活动,用来指示是什么触发了转移从而导致状态发生了改变。事件通常在从一个状态到另一个状态的转移路径上直接指定。
  • 判断:判断点通过对事件判断分组转移到各自方向,提高了状态图的可视性。
StateA
StateB
StateC
StateD
  • 同步:使用同步和活动图一样是为了说明并发工作流的分叉与联合。
StateA
StateB

注:状态图重点在与描述对象的状态及其状态之间的转移,与活动图区别在于状态图注重的是行为的结果,活动图更注重是行为的动作。

Mermaid中状态图的简单例子

样例

在下面这段文字(代码块中)的前面和后面一行各自添加```(键盘左上,与~共用一个键)即可生成如下图的展示结果(注意在使用时需要增加mermaid标识,加在前面那三点的后面)。

stateDiagram
    [*] --> 用户登录
    用户登录 --> 登录成功:验证通过
	登录成功 --> 填写信息:注册新卡
    填写信息 --> 存入数据库:信息合理
    存入数据库--> 提示成功:保存成功
    提示成功 --> 退出系统:退出
    退出系统-->[*]
验证通过
注册新卡
信息合理
保存成功
退出
用户登录
登录成功
填写信息
存入数据库
提示成功
退出系统

状态的表示

状态的表示有多种方式:

  1. 最简单的是用一个名字来定义一个状态:
stateDiagram-v2
    stateId
stateId
  1. 如果要简写状态名,则可以使用以下方式:
stateDiagram-v2
    state "StateA" as s1
    s1 -->StateB
StateA
StateB

这样在后续就可以直接使用s1来表示StateA了。
3. 简写状态名还可以用冒号的方式:

stateDiagram-v2
    s1 : StateA
    s1 -->StateB
StateA
StateB

转移的表示

转移使用箭头-->表示。
当在两个状态间定义一个转移时,这两个状态也同时会被定义。

stateDiagram-v2
    s1 --> s2
s1
s2

如果想在转移上添加文字,那么在后面加上一个冒号后写上想添加的文字即可。

stateDiagram-v2
    s1 --> s2: A transition
A transition
s1
s2

开始和结束的表示

在状态图中,开始和结束状态是两个特殊的状态。它们都用[*]来表示。

stateDiagram-v2
    [*] --> s1
    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
if n < 0
if n >= 0
IsPositive
False
True

同步的表示

对于同步的表示,则使用到了<<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 --> [*]
State2
State3
State4
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐