概述
首先阐述一下几个简单概念:
- 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官方文档。
若有错误之处,还请指正。