使用PlantUML绘图

Posted on Mar 8, 2021


本文介绍一个适合于软件工程师所使用的绘图工具:PlantUML。

前言

软件工程师常常需要绘制UML图来描述软件结构。市面上有非常多的UML工具可以选用。并且其中有一些价格不菲。

这里给大家介绍一个好用的免费工具:PlantUML。

PlantUML介绍

PlantUML是一个开源项目,其官网地址是:https://plantuml.com/。软件行业有很多工具都和它有关系,你可以在这里看到一个很长的列表:There are (too) many way of using PlantUML。这其中包括大名鼎鼎的:Eclipse,Microsoft Word,Google Docs,VS Code等。

PlantUML发表于2009年,是Java语言实现。其源码在github上:plantuml

虽然PlantUML本身是GPL License,但是它生成的图像不受GPL/LGPL/ASL/EPL/MIT许可保护:由PlantUML的“执行”生成的图像(无论何种格式)均归其作者所有。

为什么是PlantUML?

之所以选择推荐这个工具,是因为我觉得它有如下好处:

  • 免费:这个应该不需要解释了。
  • 跨平台:我始终觉得,软件从业者选择的每一个工具都应该考虑其跨平台的能力。因为在你的职业生涯中,你很可能会在不同时期使用不同的操作系统。如果你所使用的所有工具都是特定操作系统的,当你换一个新的环境时,你可能就非常痛苦了(例如:从Mac环境切换到Windows)。
  • 文本形式编辑:这个好处下面展开说。
  • 支持多种导出格式:例如,你可以导出图片在文档中使用,也可以导出ASCII码格式在代码中使用,下文会讲到。
  • 支持多种图形:既然是绘图软件,那当然是支持的图形类型越多越好。

使用文本的形式而不是图形拖拽的形式来编辑有什么好处?我觉得包括以下这些:

  • 随时可以编辑:你通常在自己电脑上编辑文件,但你很可能会将文件传输到公司或团队的其他电脑上。如果是纯文本格式的,无论这个文件传到哪台电脑上,只要有文本编辑器,你就可以继续修改它,而不用安装几百M甚至几个G的特定软件才能编辑它(想想Office格式的文件)。
  • 方便版本管理:二进制格式的文件放到git之类的版本管理工具中之后,最多只能查看版本历史,但没法针对每个版本进行diff。文本类型就不一样的,你可以diff,可以merge(方便多人协同)。
  • 编辑更精准:如果你编辑过包含了几百个方框的架构图你就能体会到我所说的了:在那种情况下,每移动一个小方框,你都要小心谨慎。
  • 内容与样式解耦:下文中我们将看到,在编辑图的时候,我们只需要考虑内容,不需要考虑样式。样式可以通过外部的主题文件单独指定。这样做的好处是:你可以编辑好一份图之后,不用对其做任何修改就可以指定不同的配色。

使用方式

你至少可以通过下面三种方式使用PlantUML:

  1. 直接访问online server:PlantUML Web Server
  2. 通过jar包的形式使用,PlantUML的jar包可以到这里下载:PlantUML compiled Jar
  3. 在自己的机器上部署Web服务端(这种方式本文不涉及)。

这其中,第2,3种方式需要你额外安装两个依赖软件:

  • 【必须】Java : 是运行PlantUML的必需条件。不过绝大部分软件开发者都应该已经有这个环境了。
  • 【可选】graphviz-dot,如果想绘制除 时序图和活动图以外的图, 就需要安装Graphviz软件。请点击链接根据你所使用的操作系统选择安装方式。在Mac上,通过brew install graphviz即可完成安装。

第2种方式,使用jar包的好处是简单,你甚至可以在脚本文件中集成,这样可以批量生成最终图片。但坏处是性能较差,每次运行jar包都要创建一个新的进程,然后使用完再退出。

而第3种部署Web服务端的方式更合适团队内部多人使用。

使用PlantUML画UML图

接下来我们终于可以真正开始画图了。

PlantUML支持的UML图包括:

  • 时序图
  • 用例图
  • 类图
  • 对象图
  • 活动图
  • 组件图
  • 部署图
  • 状态图
  • 定时图

类图

首先,以我们最常见的类图为例。

这个类图描述的内容是设计模式中 模板方法模式(Template Method)。原图可以在设计模式:可复用面向对象软件的基础(典藏版)中找到。

打开任何你喜爱的文本编辑器,然后输入下面这段内容即可:

@startuml

class Document {
	+Save()
	+Open()
	+Close()
	{abstract} +DoRead()
}

class MyDocument {
	+DoRead()
}

class Apllication {
	+AddDocument()
	+OpenDocument()
	{abstract} +DoCreateDocument()
	{abstract} +CanOpenDocument()
	{abstract} +AbountToOpenDocument()
}

class MyApplication {
	+DoCreateDocument()
	+CanOpenDocument()
	+AbountToOpenDocument()
}

Document <|-- MyDocument
Apllication <|-- MyApplication
MyDocument <.. MyApplication
Document <--o "docs" Apllication

@enduml

对于聪明如你来说,这段内容应当很容易理解。@startuml@enduml是图形开始和结束的标记。图形的正文语法也非常明确,前面大部分是类的定义。从这个定义中你可以看到,它与我们的代码语法是如此的接近。稍微需要说明一点的是几个特殊标记,例如:如何描述可见性,如何描述元素的特殊类型。

PlantUML的字符标记与可见性的关系如下表所示:

字符标记 可见性
- private
+ public
~ package
# protected

另外,元素的特殊类型通过一些关键字描述,如下:

@startuml
abstract        abstract
abstract class  "abstract class"
annotation      annotation
circle          circle
()              circle_short_form
class           class
diamond         diamond
<>              diamond_short_form
entity          entity
enum            enum
interface       interface
@enduml

定义完类型之后,下面几行是对类与类之间关系的描述:

Document <|-- MyDocument
Apllication <|-- MyApplication
MyDocument <.. MyApplication
Document <--o "docs" Apllication

描述关系的符号也很直观,*代表实心菱形,o代表空心菱形,:

符号 含义
<|– 扩展
*– 组合
o– 聚合

关系还可以通过双引号文字描述,例如:

@startuml
Class01 "1" *-- "many" Class02 : contains
@enduml

将前面的代码示例保存成文件class_diagram.uml,然后通过执行下面这条命令

path_to/java -jar path_to/plantuml.jar path_to/class_diagram.uml

便可以得到下面这个图:

你可以对比一下这幅图和前面的uml文件,以理解每行代码的含义。

更多类图的语法请参见这里:PlantUML:类图

时序图

下面我们再看一个示例。仍然是软件开发中很常见的,这次是时序图。这里我们仅用知道三个简单的语法,就可以绘制出时序图。

  • 首先是A -> B :Event。这里的A和B是调用的发起者和被调用者。箭头表示调用的方向,如果是向左的箭头则表示回复。冒号后面是调用的名称。如果不强调调用者,那么A可以省略。
  • 第二个是 B <-- A : Response。类似的,这里表示A向B回复。两个--表示这条线是虚线,即:异步回复。当然了,Response是回复的方法。
  • 最后是activatedeactivate描述事件开始和结束的范围,后面跟着相应的角色。

有了上面的基础我们就可以理解下面代码了,这个时序图描述了用户收发邮件的过程。

@startuml

-> Computer : checkEmail

Computer -> Server : sendUnsentEmail
activate Server
deactivate Server

Computer -> Server : newEmail
activate Server
Computer <-- Server : response
deactivate Server

Computer -> Server : downLoadEmail
activate Server
deactivate Server

Computer -> Server : deleteOldEmail
activate Server
deactivate Server

@enduml

整个过程分成5个事件,所以有5个小段。有了上面的语法介绍,我想这段代码已经不用多做说明了。如果你还有不理解的地方,直接对比下图,应该就容易明白了。

更多时序图的语法请参见这里:PlantUML:时序图

非UML图

除了UML以外,PlantUML还支持以下类型的图:

  • JSON data
  • YAML data
  • Network diagram (nwdiag)
  • 线框图形界面
  • 架构图
  • 规范和描述语言 (SDL)
  • Ditaa diagram
  • 甘特图
  • 思维导图
  • Work Breakdown Structure diagram
  • 以 AsciiMath 或 JLaTeXMath 符号的数学公式
  • Entity Relationship diagram

由于篇幅所限,这里不会介绍所有图形的语法。思维导图是我们很常用的一种描述图形,所以这里仅以它为例进行一些介绍。

思维导图

思维导图以@startmindmap@endmindmap作为开始和结束的标记。嵌套关系以*的数量为层级。

下面的示例描述了2021年的24节气:

@startmindmap

* 2021年24节气
** 一月
*** 1月6日 小寒
*** 1月20日 大寒
** 二月
*** 2月3日 立春
*** 2月18日 雨水
** 三月
*** 3月5日 惊蛰
*** 3月30日 春分
** 四月
*** 4月4日 清明
*** 4月20日 谷雨
** 五月
*** 5月5日 立夏
*** 5月21日 小满
** 六月
*** 6月5日 芒种
*** 6月21日 夏至
** 七月
*** 7月7日 小暑
*** 7月22日 大暑
** 八月
*** 8月7日 立秋
*** 8月23日 处暑
** 九月
*** 9月7日 白露
*** 9月23日 秋分
** 十月
*** 10月8日 寒露
*** 10月23日 霜降
** 十一月
***  11月7日 立冬
*** 11月22日 小雪
** 十二月
*** 12月7日 大雪
*** 12月21日 冬至

@endmindmap

最后我们得到的图形如下所示:

更多语法

本文当然不打算把官方用户指南照抄一遍,所以上文仅仅是给出几个最简单的示例让读者看到PlantUML的基本使用方法。更多的内容请阅读官方的文档手册,其中有详细的各种图的编写语法。

如果觉得读官方手册太枯燥的话,也可以通过一些实际的示例来进行学习:Real World PlantUML。这个网站包含了很多具体的示例以及对应的结果展示,找到和你类似的场景,然后拷贝源码进行修改即可。

样式定制

PlantUML的默认样式未必符合你的胃口。好消息是它提供了非常多的参数来定制化外观。具体可以看这里:All Skin Parameters

但对于软件工程师来说,自己配一整套样式可能并非我们所擅长。这个时候你自然会想到,有没有别人已经配置好的主题我可以直接使用。答案是肯定的:puml-themes。点击链接可以看到这个几个主题的示例。在github上下载源文件之后通过-I参数指定主题文件便可以使用(注:这个参数需要指定主题文件的完全路径,不支持~)。例如:

java -jar plantuml.jar -I/Users/paul/puml-themes/themes/bluegray/puml-theme-bluegray.puml sequence.uml

我们便得到了下面这样的图:

对于样式,应当能找到很多别人配置好的主题(例如:Sublime就有很多界面和代码高亮的不同配色)。但可惜的是,我没能在网上找到更多好的选择(还有些主题只支持部分类型的图)。如果你知道,请告诉我。

为了使自己不陷入选择困难症,所以通常我会在UML图中加上这样一句,以使用纯黑白的配色:

skinparam monochrome true

这种配色虽然不如上面的靓丽,但看起来更正式,适用的场景会更广泛些。

另外,PlantUML默认生成的图片分辨率太低,稍微放大之后就会变得模糊。所以通常我会在文件开头添加一行scale 2表示生成图像需要放大两倍。为了获得高分辨率,本文贴出的图片都是放大了的。

与Sublime集成

前面说了,PlantUML可以和很多软件集成在一起。但是为了方便很多像我一样使用Sublime的开发者,这里专门提一下如何将PlantUML与Sublime集成在一起。

之所以选择Sublime作为我的主力编辑器,自然也是因为它的跨平台特性。除此之外我觉得它还有其他几个优势:

  1. 启动速度极快,不用等待慢腾腾的进度条(对比一下VS Code,Eclipse,Android Studio,Atom就知道了)
  2. 插件丰富,这个很主观,但至少我需要的插件都能找到
  3. 代码扫描机制自动且效率高(不用像Scitool Understand那样每次代码变动都要全盘扫描所有代码),鼠标悬停就可以跳转到函数定义和调用处
  4. 定制功能强大,例如,很简单就可以与PlantUML集成
  5. 没有太多多余的,我不需要的东西

Sublime有一个专门插件支持PlantUML:Plant​Uml​Diagrams。但我个人并不太喜欢这个插件。原因是它的配置方式过于死板,只支持配置PlantUML的jar包路径或者服务端地址。这样做的后果是:如果你想传递不同的参数给plantuml.jar(例如:通过-I使用定制的主题)那就没法做到了。

除了这个插件,还有一种更简洁的与Sublime集成的方式:Sublime支持自定义编译方式,你可以添加一个新的Build System来运行plantuml.jar生成UML图。在Sublime中操作步骤如下:

Tools -> Build System -> New Build System...

然后输入:

{
	"shell_cmd": "/usr/bin/java -jar /Users/paul/Developer/plantuml.jar \"$file\""
}

这段配置应该很容易理解,就是运行指定路径的plantuml.jarjava命令和jar包路径请根据你机器上的实际路径修改。$file变量代表了当前编辑的文件。这样配置好之后,可以为其命名为“PlantUML”保存。

在这之后,直接通过Command + B(Linux和Windows上是Ctrl + B)命令便可以调用我们自定义的编译命令了,你会在修改文件的同目录下找到生成的图片文件。

导出

除了PNG图片格式之外,PlantUML还支持很多其他类型的格式导出。其参数和格式如下:

-tpng		To generate images using PNG format (default)
-tsvg		To generate images using SVG format
-teps		To generate images using EPS format
-tpdf		To generate images using PDF format
-tvdx		To generate images using VDX format
-txmi		To generate XMI file for class diagram
-tscxml		To generate SCXML file for state diagram
-thtml		To generate HTML file for class diagram
-ttxt		To generate images with ASCII art
-tutxt		To generate images with ASCII art using Unicode characters
-tlatex		To generate images using LaTeX/Tikz format
-tlatex:nopreamble	To generate images using LaTeX/Tikz format without preamble

这对于要编码的软件开发者来说就太方便了,因为我们可以直接将画的UML图通过ASCII的形式导出,然后贴到代码中。例如,通过java -jar ~/Developer/plantuml.jar -ttxt sequence.uml命令将得到下面的结果(删除了多余的部分):

       ,--------.          ,------.
       |Computer|          |Server|
       `---+----'          `--+---'
 checkEmail|                  |    
 ---------->                  |    
           |                  |    
           | sendUnsentEmail ,-.   
           | --------------->|X|   
           |                 `-'   
           |                  |    
           |                  |    
           |                  |    
           |    newEmail     ,-.   
           | --------------->|X|   
           |                 `-'   
           |     response     |    
           | <- - - - - - - - -    
           |                  |    
           |  downLoadEmail  ,-.   
           | --------------->|X|   
           |                 `-'   
           |                  |    
           |                  |    
           |                  |    
           | deleteoldEmail  ,-.   
           | --------------->|X|   
           |                 `-'   
           |                  |    
           |                  |    
       ,---+----.          ,--+---.
       |Computer|          |Server|
       `--------'          `------'

通过git管理UML图,通过ASCII码的形式描述复杂算法的时序。做到这些之后,是不是又为软件开发流程提升了一些些效率。所谓高效人士,只不过是平时一点点的积累了这些好工具,好习惯罢了。

古人云:

不积跬步,无以至千里;
不积小流,无以成江海。

参考资料与推荐读物


原文地址:《使用PlantUML绘图》 by 保罗的酒吧
 Contents