介绍
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
生产示例
关联关系
简单示例
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
| 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的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();
app.use(express.static('public'));
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')) { const relativePath = filePath.replace(path.join(__dirname, 'public'), '').replace(/\\/g, '/'); fileList.push(relativePath); } }); return fileList; }
app.get('/api/icons', (req, res) => { const svgFiles = getAllSvgFiles(path.join(__dirname, 'public', 'icons')); res.json(svgFiles); });
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