用PlantUML和语雀画UML类图

概述

首先阐述一下几个简单概念:

UML:是统一建模语言(Unified Modeling Language)的缩写,它是一种用于软件工程的标准化建模语言,旨在提供一种通用的方式来可视化软件系统的结构、行为和交互。UML由Grady Booch、Ivar Jacobson和James Rumbaugh三位软件工程专家在1990年代初共同开发,并在1997年被对象管理组织(Object Management Group, OMG)正式采纳。PlantUML:是一个开源工具,它允许我们用文本形式来描绘和创建UML图。在VSCode中可以安装扩展来绘制,而在语雀的MarkDown编辑器中,则可以用“文本绘图”形式直接在文档中创建。UML类图:在面向对象语言或开发中类图是最基础也最有用的一种图,它可以描述类的成员以及多个类之间的关系。

在Godot中,我们使用GDScript进行游戏或类库开发时,也需要涉及面向对象开发和类图等,用于清晰表达自己的思路或详实自己的文档。

因为语雀文档内部创建更方便,所以本文主要介绍在语雀中绘制UML类图的方式。

在语雀中创建PlantUML类图

在语雀文档中,在任意一行行首输入“/wbht”,可以找到“文本绘图”,回车即可插入“文本绘图”的Block。 默认插入的“文本绘图”块如下: 点击顶部的“模板”,在下拉列表中选择“类图”: 会自动填充和渲染一个如下的类图: 我们便可以在这个基础上进行UML类图的绘制。

PlantUML类图基础语法

起止标记

首先是起止标记,绘图描述的内容必须包裹在一对@startuml和@enduml标记之间。

@startuml

@enduml

申明元素

在@startuml和@enduml标记之间,我们可以使用特定的语法来申明类图的元素。PlantUML本身支持很多种元素申明,详见:类图的语法和功能 但在GDScript中最常用到的便是类和枚举,其他的元素类型并不支持。

@startuml

class a

enum skills

@enduml

其中:

以class为关键字,后面跟类名,可以声明一个类;以enum为关键字,后面跟枚举名,可以声明一个枚举;

上面代码生成的类图如下:

添加类或枚举的成员

以类为例,我们可以使用:(注意前后都有一个空格)来为申明的类添加成员,名称不带()的被视为是属性,带()的被视为是方法。

@startuml

class a

a : name

a : sex

a : age

a : say_hello()

@enduml

上面的代码生成的类图如下:

也可以用花括号语法:

@startuml

class a{

name

sex

age

say_hello()

}

@enduml

这样的写法更接近于真实代码的形式,可以省去重复的类名和:。

申明成员类型

可以为类的成员申明数据类型。

@startuml

class a{

String name

bool sex

int age

void say_hello()

}

@enduml

可以采用C风格的前置类型声明:

也可以采用类似GDScript的冒号后置类型申明形式:

@startuml

class a{

name:String

sex:bool

age:int

say_hello():void

}

@enduml

设定成员的可访问性

类图可以更具体的标记属性和方法的可访问性,也就是private、protected、public,如果是C++之类的或许可以用上,但是在GDScript中并不涉及这部分。 下面是具体的修饰符和意义。

字符图标(属性)图标(方法)可访问性-private 私有#protected 受保护~package private 包内可见+public 公有

下面是一个简单的例子:

@startuml

class a{

+name:String

-sex:bool

~age:int

+say_hello():void

}

@enduml

绘制效果如下:

表示静态变量或方法

Godot3.x就支持静态函数,Godot4.x更是支持了静态变量。 在PlantUML类图中我们可以精确的表示静态函数和静态变量成员,以与非静态成员区分。 方法也很简单就是在静态成员之前添加{static}修饰符。

@startuml

class a{

+ {static} name:String

-sex:bool

~age:int

+{static}say_hello():void

}

@enduml

绘制效果如下:

可以看到静态成员的名称下添加了下划线。

使用分隔线对成员进行自定义分组

可以在成员之间用--、..、==、__进行自定义分割线的绘制

@startuml

class a{

ame:String

--

sex:bool

==

age:int

..

say_hello():void

__

say_yes():void

}

@enduml

实际效果如下:

可以看到:

--是一条比较粗的横线,__是一条比较细的横线==是双横线..是虚线

你也可以在分割线基础上进行分组的命名。

@startuml

class a{

ame:String

-- 性别 --

sex:bool

== 年龄 ==

age:int

.. 方法 ..

say_hello():void

__ 还是方法 __

say_yes():void

}

@enduml

绘制效果如下:

这样我们可以将成员进行分组,让类的结构更清晰易懂。

多个类之间的关系表示

关系类型符号绘图泛化关系<|–组合关系*–聚合关系o–

--代表实线,可以用..替代表示虚线。<|、*和o分别代表箭头的类型

类与类之间的关系可以查阅相关的视频或文档,这里不做赘述,这里只举例说明继承关系的表示。

@startuml

Car <|-- Bus

@enduml

这里我们直接省略class关键字,申明了Car和Bus两个类,并且使用<|--连接它们。 生成的类图如下:

它的含义就是,Car作为父类,Bus作为子类,Bus继承自Car。

新手注意:继承关系的箭头是由子类指向父类。

我们可以继续这个例子,添加Car的其他子类型:

@startuml

Car <|-- Bus

Car <|-- motorcycle

Car <|-- bicycle

@enduml

生成类图如下:

在箭头连线上添加文本

可以在整个箭头连线关系的最后,在:后面添加文本信息,用于显示在连线上。

@startuml

Car <|-- Bus:继承自

Car <|-- motorcycle:继承自

Car <|-- bicycle:继承自

@enduml

生成类图如下:

表示类之间的数量关系

也可以用双引号,在连线的起始端和末尾端添加文本,用于表示类似ER(实体关系图)中的“一对一”、“一对多”、“多对多”等关系。 在继承关系中可能使用这种描述不太恰当,可以在“组合”或“聚合”等关系中使用。 下面的代码表示,一个汽车有4个轮子组成:

@startuml

汽车 "1" *-- "4" 轮子:组成

@enduml

生成类图如下:

控制类绘制的位置

在连线之间可以使用up、down、left和right关键字来手动控制类的绘制位置。 以之前的Car派生的例子为例:

@startuml

Car <|-- Bus:继承自

Car <|-- motorcycle:继承自

Car <|-- bicycle:继承自

@enduml

默认绘制为:

通过在表示实线的--之间,指定上下左右方位的关键字:

@startuml

Car <|-left- Bus:继承自

Car <|-up- motorcycle:继承自

Car <|-right- bicycle:继承自

@enduml

就可以将类图渲染为如下形式:

绘制备注

note关键字用于绘制备注。 可以使用note 位置 of 元素的形式,为类、枚举或者其他类图元素设定备注。

@startuml

Car <|-left- Bus:继承自

Car <|-up- motorcycle:继承自

Car <|-right- bicycle:继承自

note bottom of Car:车,基类

note bottom of Bus:公共汽车

note bottom of bicycle:自行车

note left of motorcycle:摩托车

@enduml

绘制效果如下:

还有一种写法,可以省略of 元素,但是需要紧跟在class申明之后,或者指定两个类的关系之后。

@startuml

class Car

note bottom:车,基类

Car <|-left- Bus:继承自

note bottom:公共汽车

Car <|-up- motorcycle:继承自

note left:摩托车

Car <|-right- bicycle:继承自

note bottom:自行车

@enduml

绘制效果如下:

可以看到效果基本上无异。 还可以用note "备注内容" as 变量形式将备注申明为一个类似单独元素的东西。 再使用--或..进行连接:

@startuml

class Car

note "车,基类" as N1

Car -- N1

@enduml

效果如下:

另外,在备注中,可以使用\n进行多行文本的换行控制。

为类图添加标题

使用title关键字可以为类图添加标题。

@startuml

title 车类的继承关系类图

Car <|-left- Bus:继承自

Car <|-up- motorcycle:继承自

Car <|-right- bicycle:继承自

note bottom of Car:车,基类

note bottom of Bus:公共汽车

note bottom of bicycle:自行车

note left of motorcycle:摩托车

@enduml

绘制效果如下:

为类图添加页脚

如果你不喜欢顶部的标题,可以使用footer关键字指定一个底部的页脚。

@startuml

Car <|-left- Bus:继承自

Car <|-up- motorcycle:继承自

Car <|-right- bicycle:继承自

note bottom of Car:车,基类

note bottom of Bus:公共汽车

note bottom of bicycle:自行车

note left of motorcycle:摩托车

footer 车类的继承关系类图

@enduml

绘制效果如下:

Godot中的一些类图实例

上面我们已经学习了如何用PlantUML进行类图的绘制。下面就举一些Godot中的例子。

子类与父类(继承关系)

@startuml

Control <|-- Button

note bottom:泛化关系(继承关系)\n子类指向父类,\n实线空心三角箭头

@enduml

绘图效果:

成员引用(一般关联关系)

@startuml

class class01{

attr:class02

}

class01 --> class02

note bottom:单向关联关系\n引用者指向被引用者

class class03{

attr:class04

}

class class04{

attr:class03

}

class03 -- class04

note bottom:双向关联关系\n箭头消失

class class05{

sub_itm:class05

}

class05 --> class05

note bottom:自关联关系\n自己的成员变量引用自己

@enduml

绘制效果:

部分与整体(聚合与组合)

@startuml

class Player {

}

note left:玩家

Player -up-|> CharacterBody2D

Player o-- CollisionShape2D

note bottom:碰撞形状

Player o-- Sprite

note bottom:玩家长相

@enduml

绘制效果:

更复杂的可以有:

@startuml

title Godot中2D角色的节点组成结构(2)

class Player {

}

note left:玩家

Player -up-|> CharacterBody2D

Player o-- CollisionShape2D

note bottom:碰撞形状

Shape2D <-down- CollisionShape2D

Player o-- HitBox

note bottom:攻击判定区域

HitBox -up-|> Area2D

CollisionShape2D2 -down-o HitBox

Shape2D <-down- CollisionShape2D2

Player o-- Sprite

note bottom:玩家长相

@enduml

组合关系

@startuml

title Godot中的组合关系

class Tree {

}

Tree *-- TreeItem

note right:组合关系,\n父类由子类组成,\n父类消失,子类失去意义,\n子类消失,父类无法构成。

@enduml

依赖关系

@startuml

title Godot中类的依赖关系

class ShapePoints {

+static rect():PackedVector2Array

}

class myCanvas{

+ draw_rect():void

}

ShapePoints <.. myCanvas

note right: 依赖关系:\n一个类用**局部变量**、\n**方法参数**或者\n**对静态方法的调用**\n来访问另一个类

@enduml

总结

本文带领Godot使用者,学习和使用基础的PlantUML类图绘制技巧。 希望对Godoter们编写和设计自己的类以及类库有所帮助,你也可以用来绘制和讲解设计模式等。 本文不详之处,可以查阅其他大佬的文章或翻找PlantUML官方文档。 若有错误之处,还请指正。