0%

PlantUML

介绍

plantuml官方文档

依赖关系、关联关系、聚合关系、组合关系、实现关系、继承关系

参考文献

关系区分点
依赖关系uses-a关系,方法参数、返回值,
关联关系成员变量
聚合关系has-a关系,成员变量。关联关系双方是平级的,是个体和个体的关系,聚合关系双方不是平级的,是整体和部分的关系。
组合关系组合关系是一种强聚合的关系,组合关系与聚合关系的区别在于:聚合关系中部分离开整体仍可存活,组合关系中部分离开整体没有意义,比如:人由身体、四肢等部分组成 ,它们的关系为组合关系。
实现关系实现接口。implements
继承关系继承基类。extends

依赖关系

简单示例

A使用了类B,类A依赖类B

代码详情
1
2
3
4
5
6
public class A{
public void functionl(B b){}
public B function2{}
}

public class B{}

plantUML示例

A依赖B

A依赖B的plantUML
1
A ..> B :依赖

生产示例

关联关系

简单示例

B类作为A类的成员变量,如果互为成成员变量则为双向关联,否则为单向关联

代码详情
1
2
3
4
5
public class A{
public B b;
}

public class B{}

plantUML示例

A关联B

A依赖B的plantUML

生产示例

聚合关系

简单示例

plantUML示例

A聚合B

聚合的plantUML

生产示例

来看一下 java.util.ArrayList类的实现。ArrayList内部使用一个数组来存储元素,这种关系可以看作是聚合关系。

代码详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.util.Arrays;

public class MyArrayList<E> {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_CAPACITY = 10;

public MyArrayList() {
elements = new Object[DEFAULT_CAPACITY];
}

public void add(E e) {
if (size == elements.length) {
ensureCapacity();
}
elements[size++] = e;
}

public E get(int index) {
if (index >= size || index < 0) {
throw new IndexOutOfBoundsException();
}
return (E) elements[index];
}

private void ensureCapacity() {
int newSize = elements.length * 2;
elements = Arrays.copyOf(elements, newSize);
}

public int size() {
return size;
}
}

在这个简化的 MyArrayList例子中,elements数组是存储在 MyArrayList对象中的元素集合。尽管这些元素(E类型的对象)由 elements数组持有,它们可以独立存在于 MyArrayList之外。这显示了一种聚合关系。

组合关系

简单示例

plantUML示例

A组合B

组合的plantUML

生产示例

来看一下 java.util.HashMap类的实现。HashMap使用内部类 Node来存储键值对,这种关系可以看作是组合关系。

代码详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import java.util.Objects;

public class MyHashMap<K, V> {
static class Node<K, V> {
final int hash;
final K key;
V value;
Node<K, V> next;

Node(int hash, K key, V value, Node<K, V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
}

private Node<K, V>[] table;
private int capacity = 16;

public MyHashMap() {
table = new Node[capacity];
}

public void put(K key, V value) {
int hash = hash(key);
int index = hash % capacity;

Node<K, V> newNode = new Node<>(hash, key, value, null);
if (table[index] == null) {
table[index] = newNode;
} else {
Node<K, V> current = table[index];
while (current.next != null) {
current = current.next;
}
current.next = newNode;
}
}

public V get(K key) {
int hash = hash(key);
int index = hash % capacity;

Node<K, V> current = table[index];
while (current != null) {
if (current.hash == hash && Objects.equals(current.key, key)) {
return current.value;
}
current = current.next;
}
return null;
}

private int hash(Object key) {
return key == null ? 0 : key.hashCode();
}
}

MyHashMap类中,Node内部类是用来存储键值对的节点。每一个 Node对象是 MyHashMap对象的一部分。如果 MyHashMap对象销毁,它持有的所有 Node对象也将被释放。这是一种组合关系,因为节点的生命周期完全依赖于 MyHashMap对象的生命周期。

实现

plantUML示例

代码详情
1
2
3
4
5
6
7
8
9

class A implements B{

}

interface B{

}

代码详情

继承

plantUML示例

代码详情
1
2
3
4
5
6
7
8
9

class A extends B{

}

class B{

}

代码详情

通用

TRICK

  • -> 默认是水平排列元素,-->垂直排列元素
  • 使用 together {}可以让together里面的元素,对齐在一起
  • 使用 A --[hidden]d> E,可以做到让E在A下面,并且没有关联线

模块化画图

类图

在全景图中 !include subpart.puml,直接include即可

序列图

使用 !includesub,甚至可以一个子模块不同部分作为全景图同一个子部分,如下面子模块1的示例

全景图 all.puml :

1
2
3
4
5
6
@startuml

title all
!includesub test2.puml!BASIC1
!includesub test1.puml!BASIC
@enduml

子模块1 test1.puml

1
2
3
4
5
6
7
8
9
10
11
@startuml

A -> A : stuff1
!startsub BASIC
B -> B : stuff2
!endsub
C -> C : stuff3
!startsub BASIC
D -> D : stuff4
!endsub
@enduml

子模块2 test2.puml

1
2
3
4
5
6
7
8
9
10
@startuml
'https://plantuml.com/sequence-diagram

autonumber
!startsub BASIC1
A -> A: stuff2
!endsub

@enduml

序列图中表示多线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58


@startuml
|M9VRU2jBoP| M9VRU2jBoP

|5XrELwnzlas|5XrELwnzlas

|acvn3T7Z2y9| acvn3T7Z2y9

title h1fsfCzv6O

|5XrELwnzlas|
start
note
h1fsfCzv6O
end note
fork
|5XrELwnzlas|
:DSmPrVSLfYBxQSbb3tn6;
-> <color: red> M3vxJNuWPTW;

|M9VRU2jBoP|
:uMnpXE5sRL5Kc1UT5iry;

|5XrELwnzlas|
:Zb6OF5R e0Zngc4u9yw XpaApaep;


fork again
|5XrELwnzlas|
:N4T5hA7MgLbYozVhX4V1;
-[#green,dashed]-> <color: red>7JGUBlcqSz3D;

|M9VRU2jBoP|
:m3aKwP8VPykB8lXtQOsu;
-[#green,dashed]->

|5XrELwnzlas|
:UacF z6SCtaZwiCNfWA;
-[#green,dashed]->

fork again
|5XrELwnzlas|
:A01X8G7yk5SJTj9u8C5dFO;
-> <color: red>TIBbVUbfS0e9I7ZLHBxz;

|acvn3T7Z2y9|
:UFNYl7GMjhDOpzTidTc0R;

|5XrELwnzlas|
end fork
|5XrELwnzlas|
:lPNzre71O cdH3lhBOCBfU4;

stop


@enduml

序列图中指定顺序

participant Last order 30 越小越靠左

如何移除类图中没有绑定的组件

remove @unlinked

legend

图例标注

代码详情
1
2
3
4
legend
<font color=purple>紫色:待确认</font>
<font color=#c55c2d>橙色:语法关键字</font>
end legend

note

floating note right,右侧标记

活动图(程序流程图)

  • :]:方框,操作
  • :;:开始,结束

泳道图

在线上写字:->你要说明的内容的内容;

技巧:可以遵循下面的思路进行编写

代码详情
1
2
3
4
5
6
7
<!-- 泳道的名称 -->
|某个服务名|
<!-- 具体的流程步骤 -->
:步骤;
<!-- 线的颜色/样式/在线上写字 -->
-[#green,dashed]-> <color: red>网络io;

泳道图加序列图

序列图

分割符:== Initialization ==

给组件别名,并可以指定组件的顺序:

组件、服务: participant "synology photos" as sp

角色、小人:actor 运营 as 运营

数据库:database mysql

队列、mq:queue rocketmq

序列图中使用图标

代码详情

箭头颜色

和样式:

1
-[#green,dashed]-> <color: red>网络io;

表示并行操作

1
2
3
fork

end fork {和}

类图

调整类图的方向

代码详情

给类图加上颜色强调

代码详情
1
2
3
class 表 #line:green;back:lightblue{
ziduan 字段
}

脑图

自定义颜色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<style> 
mindmapDiagram {
.A { BackgroundColor lightgreen }
.B { BackgroundColor lightblue }
.C { BackgroundColor red }
.D { BackgroundColor pink } }

</style>
* A
** B
*** C
**** D

* E <<A>>
** F <<C>>
*** G
**** H <>

* I <<A>>
** J
*** K
**** L <>
**** M

* N <<A>>
** O <<C>>
*** P <<D>>
**** Q
**** R
** S
*** T <<D>>
**** U

footer 仅做演示
@endmindmap

箭头颜色

时序图

定义线条样式、变量、函数

预览

应用例子

预览

例子

非常优秀的plantuml的示例

icons

icon的使用

Azure

如何预览azure的icons

官网,将icon下载之后,将解压的文件夹放在public下

即如下的文件组织

代码详情
1
2
3
4
5
6
7
8
9
10
11
+ svg-preview
+ public
+ icons
+ ai machine learning
+ 00028-icon-service-Batch-AI.svg
+ 00030-icon-service-Machine-Learning-Studio-(Classic)-Web-Services.svg
+ analytics
+ 00009-icon-service-Log-Analytics-Workspaces.svg
+ 00039-icon-service-Event-Hubs.svg
+ server.js
+ index.html

server.js的内容

代码详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
const express = require('express');
const fs = require('fs');
const path = require('path');

const app = express();

// Serve static files from the "public" directory
app.use(express.static('public'));

// Function to get all SVG files
// function getAllSvgFiles(dir, fileList = []) {
// const files = fs.readdirSync(dir);
// files.forEach(file => {
// const filePath = path.join(dir, file);
// if (fs.statSync(filePath).isDirectory()) {
// getAllSvgFiles(filePath, fileList);
// } else if (filePath.endsWith('.svg')) {
// fileList.push(filePath.replace('public', '')); // Make path relative to the public directory.
// }
// });
// return fileList;
// }
function getAllSvgFiles(dir, fileList = []) {
const files = fs.readdirSync(dir);
files.forEach(file => {
const filePath = path.join(dir, file);
if (fs.statSync(filePath).isDirectory()) {
getAllSvgFiles(filePath, fileList);
} else if (filePath.endsWith('.svg')) {
// Convert to relative web path and replace backslashes (Windows) with forward slashes
const relativePath = filePath.replace(path.join(__dirname, 'public'), '').replace(/\\/g, '/');
fileList.push(relativePath);
}
});
return fileList;
}

// Endpoint to get all the SVG file paths
app.get('/api/icons', (req, res) => {
const svgFiles = getAllSvgFiles(path.join(__dirname, 'public', 'icons'));
res.json(svgFiles);
});

// Serve the index.html at the root
app.get('/', (req, res) => {
console.log(`path ${path.join(__dirname, 'index.html')}`);
res.sendFile(path.join(__dirname, 'index.html'));
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});

index.html的内容

代码详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SVG Preview</title>
<style>
body {
font-family: Arial, sans-serif;
}
.svg-container {
flex: 0 1 200px;
margin: 10px;
border: 1px solid #ddd;
padding: 10px;
text-align: center;
}
.svg-container img {
height: 80px;
width: 80px;
}
#svg-previews {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
</style>
</head>
<body>
<h1>SVG Previews</h1>
<div id="svg-previews"></div>
<script>
window.onload = async function() {
const svgPreviews = document.getElementById('svg-previews');

const response = await fetch('/api/icons');
const iconPaths = await response.json();

iconPaths.forEach(path => {
const container = document.createElement('div');
container.className = 'svg-container';

const img = document.createElement('img');
img.src = path;
img.alt = path;

const label = document.createElement('p');
label.textContent = img.alt;

container.appendChild(img);
container.appendChild(label);
svgPreviews.appendChild(container);
});
};
</script>
</body>
</html>

svg-preview根路径执行如下命令

代码详情
1
2
3
4
npm init -y
npm install express
node server.js

访问 http://localhost:3000

有绝大部分的logo库,icon