跟着ChatGPT爆火,网络上贩卖焦虑的声音此伏彼起,我等工程师、程序员似乎立刻要被一锅端起,倒入前史涡流。笔者想说,莫慌,软件开发是一门极端杂乱的学识,断然不是写两行代码那么简单。产品百万千万行的代码依然需求人来读懂和维护,架构规划、形式、扩展性依然是中心命题。UML是软件规划的利器,而PlantUML是一个很好的UML创造东西。

PlantUML简介

PlantUML是一个开源的UML绘图东西,现在在GitHub上有7.5K Stars,最早于2021年10月发布第一个正式版别,历经二十多个版别迭代,现在已非常老练。它可用于快速创立UML(统一建模言语,Unified Modeling Language)图表,生成可视模型,帮助开发者将杂乱的规划概念表示出来PlantUML 最与众不同的当地在于它供给了一套简单的纯文本语法来定义UML,堪称是UML界的Markdown,起到了桥梁和标准的效果。也便是说,你能够扫描代码来生成PlantUML,也能够解析PlantUML文本来生成代码。

PlantUML开源社区也很昌盛,其成果便是它丰厚的插件,可与Visual Studio Code,JetBrains,Eclipse,NetBeans等系列IDE集成,以便利创造和预览。它能生成的视图支撑包括SVG,PDF,PNG,EPS和LaTeX等丰厚格式,基本上应有尽有。此外,PlantUML甚至还能够支撑思想导图、甘特图、JSON、YAML的可视化,把影响力扩展到了UML之外。它已成为越来越多神级工程师、架构师、项目经理的必选东西。我在微软的开发团队出品的开源作品 Hydra Lab 就采用了PlantUML进行架构规划和协议标准可视化,用起来可谓得心应手。

关于PlantUML的细节用法,官方中文文档现已说得很清楚了,本文将不再赘述,而侧重将篇幅放在详细运用经历的共享上。

小试牛刀

PlantUML是一个基于Java的东西,需求Java运转环境。本文将基于其当前最新版别1.2023.1进行实战讲解。不过,最方便的试用PlantUML言语的办法莫过于直接运用官网的服务,各位能够测验把一下内容粘贴进输入框看效果:

以下代码呈现了类之间联络的表达,学习UML时分最容易搞混的便是各种箭头之间的差异和联络:

@startuml class-relations-1
Postman -- Postbox : associate,(关联)
Driver -- Car : drives(关联) >
Car *-- Wheel : have 4 (包括,组合) > 
Car -- Person : < owns(关联)
Folder *-- 字母F : starts with (composition,组合) >
Folder o-- File : contains (aggregation,聚合) >
Department o-- Employee : contains >
@enduml

PlantUML指北:用UML设计和规划你的项目

下面的代码展现了或许扩展、完成、依靠的类间联络:

@startuml class-relations-2
Cat -up-|> Animal : extends >
Dog -up-|> Animal : extends >
class Cat {
    -meow()
}
class Dog {
    +bark()
}
iPhone ..|> SmartPhone : realize (完成) >
DBReader ..> DB : depends on (依靠) >
@enduml

PlantUML指北:用UML设计和规划你的项目

PlantUML会主动处理图表的外观和布局,开发人员能够不用在意细节,一起他也支撑必定程度的自定义。他的语法中运用@startuml@enduml作为最初和结束的声明,此外还有@startjson@startyaml支撑JSON\YAML数据结构的可视化,@startgantt用于画甘特图,@startmindmap用于制作思想导图,@startmath(AsciiMath)或@startlatex(JLaTeXMath)用于制作数学公式,功用可谓十分强大。这儿仅展示一些示例,不再逐个赘述,官方PDF写的很清楚了。下面是一段Spring Boot配置文件的可视化:

@startyaml yaml-preview-demo
server:
  port: 9886
  compression:
    enabled: true
    min-response-size: 102400
spring:
  cache:
    type: ehcache
  application:
    name: 'my-app'
  datasource:
    driver-class-name: org.sqlite.JDBC
@endyaml

PlantUML指北:用UML设计和规划你的项目

以下是一个活动图(activity diagram)示例:

@startuml activity-diagram-preview-demo
start
if (Graphviz installed?) then (yes)
:process all\ndiagrams;
else (no)
:process only
__sequence__ and __activity__ diagrams;
endif
stop
@enduml

PlantUML指北:用UML设计和规划你的项目

更多示例能够看这儿:REAL WORLD PlantUML。

实践应用

在实践的开发场景中,咱们能够运用PlantUML语法标准在代码库中进行撰写规划,以文本的形式输出内容到.puml文件中,然后调用PlantUML来生成可视化的图表,从而便使用来在Markdown技能文档当中引证,支撑技能评审。那么,咱们是否能够主动化这一流程,形成闭环?答案当然是能够的,PlantUML供给了Java依靠库,能够在代码中直接调用。这儿演示一个小小的实例,使用Gradle创立构建使命,完成UML图片生成。

首先咱们在项目中添加PlantUML依靠:

    implementation 'net.sourceforge.plantuml:plantuml:1.2023.1'

接着,咱们在Gradle中创立一个使命,扫描特定目录下的所有puml文件,将他们交给生成逻辑(UMLImageGenerator#generateUMLImageFromFile)进行处理:

task generateUMLImage(group: 'documentation') {
    doFirst {
        def scanningDirList = ['agent/doc/UML']
        def outputDir = new File(projectDir, 'docs/images/UML')
        def generator = new UMLImageGenerator()
        scanningDirList.each {
            fileTree(new File(projectDir, it)).filter { it.name.endsWith(".puml") }.files.each {
                generator.generateUMLImageFromFile(it.absoluteFile, outputDir)
            }
        }
    }
}

详细的从文件生成图片的逻辑如下,这儿调用的中心类便是PlantUML供给的SourceFileReader API:

import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.GeneratedImage;
import net.sourceforge.plantuml.SourceFileReader;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class UMLImageGenerator {
    public void generateUMLImageFromFile(File source, File outputDir) throws IOException {
        generateUMLImageFromFile(source, outputDir, false);
    }
    public void generateUMLImageFromFile(File source, File outputDir, boolean svg) throws IOException {
        if (!source.exists()) throw new RuntimeException(source.getAbsolutePath() + " file doesn't exist");
        SourceFileReader reader = svg ?
                new SourceFileReader(source, outputDir, new FileFormatOption(FileFormat.SVG)) :
                new SourceFileReader(source, outputDir);
        List<GeneratedImage> list = reader.getGeneratedImages();
        System.out.printf("Successfully generated %d UML images.\n", list.size());
    }
}

与上逻辑写好之后,咱们在Gradle的使命列表documentation组中就能够看到这个新的使命了:

PlantUML指北:用UML设计和规划你的项目

执行这个使命,就能够看到图片图片说出到了预期的目录下。本实战案例详细代码如下:

  • generateUMLImage使命定义
  • PlantUML依靠声明
  • UMLImageGenerator.java

进一步探究

咱们是否有可能依据UML规划来生成代码?那么首先咱们需求读取出PlantUML的解析信息,顺着SourceFileReader的API咱们能够进一步找到如下调用链:

        Diagram diagram = reader.getBlocks().get(0).getDiagram();
        if(diagram instanceof net.sourceforge.plantuml.classdiagram.ClassDiagram){
            ClassDiagram classDiagram= (ClassDiagram) diagram;
            classDiagram.getLeafsvalues().forEach(leaf -> System.out.println(leaf.getCodeGetName()));
        }

这儿的net.sourceforge.plantuml.baraye.ILeaf类型的目标便是解析后的叶子实体了,包括了代码生成所需求的信息,能够使用他来生成一些接口代码,从而进一步完成从代码规划到初始完成的主动化。