Obsah
1. Tvorba sekvenčních diagramů v Pythonu s využitím knihovny Napkin
3. Vygenerování sekvenčních diagramů přímo v PlantUML
4. Složitější příklady sekvenčních diagramů
5. Zobrazení doby zpracování dotazu a čekání na odpověď
6. Sekvenční diagramy a knihovna Napkin
8. Sekvenční diagram vygenerovaný knihovnou Napkin
9. Vykreslení diagramu přes lokální instalaci PlantUML
10. Vykreslení diagramu knihovnou Napkin (přes PlantUML na serveru)
11. Nepatrně složitější diagram: three-way handshake
12. Zobrazení doby zpracování popř. čekání na odpověď
13. Reprezentace několika dotazů v sekvenčním diagramu
14. Specifikace opakujících se sekvenčních operací (loop)
15. Vnořené cykly (loop) v diagramu
16. Seskupení zpráv v diagramu
17. Poznámky přiřazené k uzlům, zprávám či skupinám
18. Repositář s demonstračními příklady
19. Odkazy na články s tématem programové tvorby grafů a diagramů
1. Tvorba sekvenčních diagramů v Pythonu s využitím knihovny Napkin
Na předchozí článek o knihovně Rhizome dnes navážeme. Popíšeme si totiž knihovnu nazvanou Napkin. Tato knihovna je určena pro vykreslení takzvaných sekvenčních diagramů, které jsou definovány v UML (Unified Modeling Language). Důležité přitom je, že pro popis sekvenčních diagramů je použit přímo univerzální programovací jazyk Python a nikoli specializovaný jazyk (DSL – Domain-Specific Language). A jaké jsou výhody použití Pythonu namísto DSL? Především je Python podporován mnoha integrovanými vývojovými prostředími (IDE), programátorskými textovými editory i dalšími nástroji, mezi něž patří například lintery apod. Navíc může být popis sekvenčních diagramů kódem napsaným v Pythonu pro mnoho vývojářů idiomatický a tudíž může být tvorba diagramů rychlejší a vedoucí k menšímu množství chyb.
Obrázek 1: Jednoduchý diagram tříd vytvořený nástrojem PlantUML zmíněným v navazující kapitole.
Vzhledem k tomu, že výstupem z Napkinu je popis sekvenčního diagramu ve formátu akceptovaného nástrojem PlantUML, zaměříme se v úvodních kapitolách na popis alespoň základních možností tohoto nástroje – ovšem nejedná se o nosné téma celého článku.
Obrázek 2: Třída s atributy a metodami, které mají různá přístupová práva. Povšimněte si oddělení atributů od metod. To je ve skutečnosti provedeno automaticky.
2. Nástroj PlantUML
Dále popsaná aplikace Napkin je postavena na nástroji nazvaném PlantUML (http://plantuml.sourceforge.net/). PlantUML dokáže na základě textového popisu UML diagramu vytvořit bitmapový obrázek (PNG) či vektorovou kresbu (SVG) s tímto diagramem, přičemž uživatel může do jisté míry ovlivnit způsob jeho vykreslení, přidat popis hran, seskupit uzly v diagramu, změnit barvy hran i uzlů, přidat do uzlů ikony apod. V současné verzi PlantUML jsou podporovány následující typy UML diagramů:
- Diagram aktivit
- Sekvenční diagram
- Diagram tříd
- Diagram objektů
- Diagram komponent
- Diagram užití
- Stavový diagram
- Diagram načasování operací (ve vývoji)
- Deployment diagram (pravděpodobně nemá ekvivalentní český překlad)
Obrázek 3: Jednoduchý diagram aktivit vygenerovaný nástrojem PlantUML.
Ve skutečnosti sice specifikace UML popisuje i další typy diagramů, ovšem PlantUML s velkou pravděpodobností dokáže pokrýt většinu potřeb analytiků i programátorů, protože v nabídce podporovaných diagramů jsou zastoupeny všechny tři kategorie: popis struktury informačního systému, popis chování informačního systému a popis interakce či komunikace jak v rámci systému, tak i mimo něj.
PlantUML je naprogramovaný v Javě, ovšem podobně jako tomu bylo v případě již popsaného nástroje Ditaa se jedná o relativně malý program, který pro svůj běh nevyžaduje enormní množství zdrojů (diskový prostor, RAM atd.). Pro uživatele PlantUML je na adrese http://sourceforge.net/projects/plantuml/files/plantuml.jar/download k dispozici spustitelný Java archiv, dále je vhodné si stáhnout referenční příručku k jazyku z adresy http://plantuml.sourceforge.net/PlantUML_Language_Reference_Guide.pdf.
Obrázek 4: Jednoduché rozvětvení reprezentované v diagramu aktivit vytvořeného nástrojem PlantUML.
3. Vygenerování sekvenčních diagramů přímo v PlantUML
Nástroj PlantUML podporuje mnoho typů diagramů (vypsali jsme si je v předchozí kapitole). Mezi ně patří například stavové diagramy, které dokážou názorně popsat stavy systému i možné přechody mezi jednotlivými stavy. Ovšem v mnoha případech vzniká potřeba podrobněji popsat i interakci mezi popisovaným systémem a jeho okolím, interakci mezi dvěma nebo více moduly systému či (na té nejpodrobnější úrovni) interakci probíhající mezi jednotlivými objekty, z nichž se systém skládá. Pro tento účel slouží v jazyku UML především sekvenční diagramy (sequence diagrams), v nichž lze velmi názorným způsobem naznačit časovou posloupnost posílání zpráv mezi různými typy objektů, popř. k zobrazené posloupnosti zpráv přidat další komentáře, ikony a značky. Jeden z typických a poměrně často v praxi používaných příkladů použití sekvenčních diagramů je popis komunikace s využitím síťových i jiných protokolů. Ostatně právě na síťovém protokolu (navázání spojení a zrušení spojení) si sekvenční diagramy ukážeme prakticky v navazujících dvou kapitolách.
Nejjednodušší sekvenční diagram je možné v nástroji PlantUML deklarovat následujícím způsobem. S využitím symbolu -> je naznačeno poslání zprávy mezi dvojicí objektů, v tomto případě mezi klientem a serverem. Sekvenční diagram neobsahuje žádné počáteční ani koncové pseudostavy, což je jeden z rozpoznávacích znaků mezi sekvenčním diagramem a stavovým diagramem. Proto také při odstranění pseudostavů může PlantUML automaticky zaměnit stavový diagram za diagram sekvenční, což je samozřejmě chyba (způsobená tím, že se PlantUML snaží o automatické doplnění kontextových informací):
@startuml Client -> Server: SYN @enduml
Obrázek 5: Sekvenční diagram vytvořený na základě prvního demonstračního příkladu.
Druhý příklad je nepatrně složitější a ukazuje způsob navázání komunikace v protokolu TCP (tzv. three-way handshake), přesněji řečeno ideální stav, kdy se navázání spojení podaří:
@startuml Client -> Server: SYN Server -> Client: SYN-ACK Client -> Server: ACK @enduml
Obrázek 6: Sekvenční diagram vytvořený na základě druhého demonstračního příkladu.
Deklarace předchozího diagramu byla pravděpodobně pro mnoho vývojářů poněkud nešikovná, protože se na druhém řádku prohodila jména komunikujících objektů. To lze snadno napravit, protože symbol -> je možné nahradit symbolem <-, který (samozřejmě) značí poslání zprávy opačným směrem, ostatně tímto směrem bude mířit i šipka ve výsledném diagramu:
@startuml Client -> Server: SYN Client <- Server: SYN-ACK Client -> Server: ACK @enduml
Obrázek 7: Sekvenční diagram vytvořený na základě třetího demonstračního příkladu.
Podívejme se ještě na nepatrně složitější příklad: ukončení spojení, tentokrát způsobem označovaným four-way handshake (spojení v TCP ukončují a vzájemně si ho potvrzují obě strany). Jednotlivé zprávy byly navíc automaticky očíslovány, což zajistilo uvedení klíčového slova autonumber:
@startuml autonumber Client -> Server: FIN Client <- Server: ACK Client <- Server: FIN Client -> Server: ACK @enduml
Obrázek 8: Sekvenční diagram vytvořený na základě čtvrtého demonstračního příkladu.
4. Složitější příklady sekvenčních diagramů
Sekvenční diagramy mohou být i poměrně rozsáhlé, což je ukázáno na dalším příkladu získaném z reálného projektu (resp. z fáze jeho prvotního návrhu). Povšimněte si možnosti specifikace stylu vykreslení uzlů i tvaru uzlů (actor, database, participant):
@startuml header Sequence diagram for data flow in Content Service footer Copyright © 2020 Red Hat, Inc. Author: Pavel Tisnovsky actor "Author of\nrule content" as author database "Internal\nGitLab\nrepository\n<&fork>" as gitlab database "External\nGitHab\nrepository\n<&fork>" as github participant "OpenShift\ntooling" as openshift #ffa0a0 participant "Insights\nContent Service" as content_service #a0a0ff participant "Smart\nProxy" as smart_proxy #a0a0ff collections "REST API\nconsumers" as consumers == New content or update of existing content == author -> gitlab: Merge into\nmain branch == Data synchronization to Content Service == gitlab -> github: New commit(s) github -> github: Merge commit gitlab -> openshift: Webhook - new changes openshift -> github: Clone\nrepository github -> openshift: Repository content openshift -> openshift: Rebuild image note right: OpenShift rebuild Insights\nContent Service image\nwith new rules content included openshift -> openshift: Restart pod openshift -> content_service: Set replicas > 0 note right: Now the Content Service\nexposes new rules content\nvia its REST API == Synchronization between Smart Proxy and Content Service == smart_proxy -> content_service: Get new content\nfor all rules note right: Smart proxy needs\nto periodically\nupdate its cache content_service -> smart_proxy: Here's\nrequired\ncontent smart_proxy -> smart_proxy: Update content\nin cache == Providing recommendations to consumers == consumers -> smart_proxy: Get\nrecommendations\norg ID\ncluster ID smart_proxy -> smart_proxy: Merge results\nwith content smart_proxy -> consumers: Recommendations\nfor cluster @enduml
Obrázek 9: Sekvenční diagram vykreslený podle předchozího popisu.
V sekvenčních diagramech je možné použít i ikony (Kafka, Kubernetes atd.):
@startuml !include <cloudinsight/kafka> !include <kubernetes/k8s-sprites-unlabeled-25pct> header Sequence diagram for the whole CCX (external) data pipeline footer Copyright © 2020 Red Hat, Inc. Author: Pavel Tisnovsky participant "Smart\nProxy" as smart_proxy #a0a0ff participant "3Scale" as 3scale box "Insights operator" #ddffdd participant "<$master>\nControl logic" as operator database "IO memory cache" as cache end box entity "CRD" as crd collections "OCP\nWebConsole" as console == Pulling data from Smart Proxy == operator -> 3scale: Get\nrecommendations\norg ID\ncluster ID 3scale -> smart_proxy: Get\nrecommendations\norg ID\ncluster ID smart_proxy -> 3scale: Recommendations\nfor cluster 3scale -> operator: Recommendations\nfor cluster == Exposing recommendations == operator -> cache: Store\nrecommendations cache -> crd: Expose\nrecommendations == Pulling from OCP WebConsole == console -> crd: Read\nrecommendations crd -> console: Here are\nrequired data @enduml
Obrázek 10: Sekvenční diagram vykreslený podle předchozího popisu.
5. Zobrazení doby zpracování dotazu a čekání na odpověď
Často se setkáme s požadavkem, aby se v sekvenčním diagramu zobrazila doba zpracování dotazu (požadavku). Tato doba se v diagramech znázorňuje vertikálním sloupečkem, který spojuje šipky s dotazem a odpovědí. V PlantUML se požadovaného výsledku dosáhne použitím klauzulí activate jméno_uzlu a deactivate jméno_uzlu, což je ukázáno v následujícím příkladu odvozeném od příkladu předchozího:
@startuml !include <cloudinsight/kafka> !include <kubernetes/k8s-sprites-unlabeled-25pct> header Sequence diagram for the whole CCX (external) data pipeline footer Copyright © 2020 Red Hat, Inc. Author: Pavel Tisnovsky participant "Smart\nProxy" as smart_proxy #a0a0ff participant "3Scale" as 3scale box "Insights operator" #ddffdd participant "<$master>\nControl logic" as operator database "IO memory cache" as cache end box entity "CRD" as crd collections "OCP\nWebConsole" as console == Pulling data from Smart Proxy == operator -> 3scale: Get\nrecommendations\norg ID\ncluster ID activate 3scale 3scale -> smart_proxy: Get\nrecommendations\norg ID\ncluster ID activate smart_proxy smart_proxy -> 3scale: Recommendations\nfor cluster deactivate smart_proxy 3scale -> operator: Recommendations\nfor cluster deactivate 3scale == Exposing recommendations == operator -> cache: Store\nrecommendations cache -> crd: Expose\nrecommendations == Pulling from OCP WebConsole == console -> crd: Read\nrecommendations activate crd crd -> console: Here are\nrequired data deactivate crd @enduml
Obrázek 11: Sekvenční diagram vykreslený podle předchozího popisu.
Naprosto stejným způsobem lze v diagramu naznačit dobu čekání na odpověď. V takovém případě se jedná o sloupeček zobrazený na straně dotazujícího uzlu. Povšimněte si, že příkazové dvojice activate/deactivate jsou vnořeny a typicky se nepřekrývají (proto je možné, jak si ukážeme dále, použít blok with):
@startuml !include <cloudinsight/kafka> !include <kubernetes/k8s-sprites-unlabeled-25pct> header Sequence diagram for the whole CCX (external) data pipeline footer Copyright © 2020 Red Hat, Inc. Author: Pavel Tisnovsky participant "Smart\nProxy" as smart_proxy #a0a0ff participant "3Scale" as 3scale box "Insights operator" #ddffdd participant "<$master>\nControl logic" as operator database "IO memory cache" as cache end box entity "CRD" as crd collections "OCP\nWebConsole" as console == Pulling data from Smart Proxy == operator -> 3scale: Get\nrecommendations\norg ID\ncluster ID activate operator activate 3scale 3scale -> smart_proxy: Get\nrecommendations\norg ID\ncluster ID activate smart_proxy smart_proxy -> 3scale: Recommendations\nfor cluster deactivate smart_proxy 3scale -> operator: Recommendations\nfor cluster deactivate 3scale deactivate operator == Exposing recommendations == operator -> cache: Store\nrecommendations cache -> crd: Expose\nrecommendations == Pulling from OCP WebConsole == console -> crd: Read\nrecommendations activate console activate crd crd -> console: Here are\nrequired data deactivate crd deactivate console @enduml
Obrázek 12: Sekvenční diagram vykreslený podle předchozího popisu.
6. Sekvenční diagramy a knihovna Napkin
Nyní již máme k dispozici všechny informace potřebné pro porozumění sekvenčním diagramům v nástroji PlantUML. Ve druhé části článku se tedy zaměříme na popis knihovny Napkin určené pro programovací jazyk Python. Tato knihovna, která je dostupná jak na GitHubu, tak i na PyPi, umožňuje definici sekvenčních diagramů přímo v Pythonu s využitím bloků with, které reprezentují kontext, v němž je zpráva poslána popř. zpracovávána. Interně se diagramy převádí z Pythonu právě do PlantUML a následně jsou vykresleny – implicitně tak, že se definice diagramu pošle na PlantUML server, který vrátí vykreslený výsledek. V případě potřeby je však pochopitelně možné použít i lokální instalaci PlantUML.
7. Instalace knihovny Napkin
Instalace knihovny Napkin je snadná (alespoň v porovnání s PlantUML), protože je distribuována formou standardně připraveného balíčku pro jazyk Python a je dostupná na PyPi. Instalaci provedeme nástrojem pip popř. pip3 a pochopitelně je podporována i instalace do virtuálního prostředí Pythonu (virtualenv):
$ pip3 install --user napkin
Průběh instalace naznačuje, že Napkin má jen minimální závislosti:
Collecting napkin Downloading https://files.pythonhosted.org/packages/f4/71/d00d15190bd5a2e630ead71158aca8d5784abc02452df66f8645cf59d055/napkin-0.6.8-py3-none-any.whl Requirement already satisfied: requests in /usr/lib/python3.8/site-packages (from napkin) Requirement already satisfied: chardet<3.1.0,>=3.0.2 in /usr/lib/python3.8/site-packages (from requests->napkin) Requirement already satisfied: idna<2.8,>=2.5 in /usr/lib/python3.8/site-packages (from requests->napkin) Requirement already satisfied: urllib3<1.25,>=1.21.1 in /usr/lib/python3.8/site-packages (from requests->napkin) Installing collected packages: napkin Successfully installed napkin-0.6.8
Po instalaci zjistíme, kde je umístěn spustitelný skript představující vstupní bod do Napkinu:
$ whereis napkin napkin: /home/ptisnovs/.local/bin/napkin
Nakonec otestujeme, jestli je možné Napkin spustit z příkazové řádky a mimochodem zjistíme i povolené přepínače:
$ napkin --help usage: napkin [-h] [--output-format {plantuml,plantuml_png,plantuml_svg,plantuml_txt}] [--output-dir OUTPUT_DIR] [--version] [--server-url SERVER_URL] srcs [srcs ...] Generate UML sequence diagram from Python code positional arguments: srcs Python file or directory containing diagram functions optional arguments: -h, --help show this help message and exit --output-format {plantuml,plantuml_png,plantuml_svg,plantuml_txt}, -f {plantuml,plantuml_png,plantuml_svg,plantuml_txt} --output-dir OUTPUT_DIR, -o OUTPUT_DIR --version show program's version number and exit --server-url SERVER_URL (only for plantuml_png/svg format) Default is the public server Supported output formats: plantuml : PlantUML script (default) plantuml_png : PlantUML script and PNG image plantuml_svg : PlantUML script and SVG image plantuml_txt : PlantUML script and ASCII art text
8. Sekvenční diagram vygenerovaný knihovnou Napkin
Podívejme se nyní na způsob definice sekvenčního diagramu s využitím prostředků programovacího jazyka Python. Diagram se dvěma komunikujícími objekty a jednou zprávou může být zapsán následujícím způsobem:
1 import napkin 2 @napkin.seq_diagram() 3 def client_server_1(c): 4 client = c.object('Client') 5 server = c.object('Server') 6 with client: 7 server.SYN()
Jednotlivé části skriptu:
- Budeme používat dekorátor z balíčku napkin
- Dekorace funkce představující zápis komunikace mezi objekty
- Ze jména funkce se odvodí jméno souboru s výsledným diagramem (parametr je doplněn před dekorátor)
- První komunikující objekt se specifikací jeho jména tak, jak bude zobrazeno v diagramu
- Druhý komunikující objekt se specifikací jeho jména tak, jak bude zobrazeno v diagramu
- Začátek komunikace inicializovaného objektem klient
- Poslání zprávy objektu server se zapisuje formou volání funkce
Vytvoření deklarace diagramu v jazyku akceptovaném nástrojem PlantUML:
$ napkin client-server-1.py Load file : client-server-1.py File generated : ./client_server_1.puml
Výsledný soubor vypadá následovně:
@startuml participant Client participant Server Client -> Server : SYN() @enduml
9. Vykreslení diagramu přes lokální instalaci PlantUML
V případě, že máte k dispozici lokálně nainstalovaný nástroj PlantUML, je možné si nechat vygenerovat diagram v rastrovém formátu PNG následujícím příkazem (pro zajímavost je cesta k JAR archivu s PlantUML nestandardní):
$ java -jar ~/tools/plantuml.jar client_server.puml
Obrázek 13: Výsledek předchozího příkazu.
Alternativně je možné získat výstup ve vektorovém formátu SVG:
$ java -jar ~/tools/plantuml.jar client_server.puml -tsvg
Poslední možností je výstup do textového formátu (ASCII art):
$ java -jar ~/tools/plantuml.jar client_server.puml -ttxt
Výsledek v tomto případě vypadá následovně:
┌──────┐ ┌──────┐ │Client│ │Server│ └──┬───┘ └──┬───┘ │ SYN() │ │ ────────────────> ┌──┴───┐ ┌──┴───┐ │Client│ │Server│ └──────┘ └──────┘
10. Vykreslení diagramu knihovnou Napkin (přes PlantUML na serveru)
Implicitně ovšem knihovna Napkin nepoužívá lokální instalaci PlantUML, ale posílá definici sekvenčního diagramu na veřejně dostupný server, jenž vykreslení diagramu provede a pošle zpátky výsledek. Pokud tedy váš diagram neobsahuje žádná citlivá data, je vykreslení diagramu ještě jednodušší (a server je navíc pro většinu případů i dostatečně rychlý).
Vytvoření popisu diagramu v jazyku podporovaného PlantUML:
$ napkin --output-format plantuml client-server-1.py Load file : client-server-1.py File generated : ./client_server.puml
Vykreslení diagramu do rastrového obrázku PNG:
$ napkin --output-format plantuml_png client-server-1.py Load file : client-server-1.py File generated : ./client_server.puml, ./client_server.png
Vykreslení diagramu do vektorové kresby SVG:
$ napkin --output-format plantuml_svg client-server-1.py Load file : client-server-1.py File generated : ./client_server.puml, ./client_server.svg
Výstup do textového souboru (ASCII art):
$ napkin --output-format plantuml_txt client-server-1.py Load file : client-server-1.py File generated : ./client_server.puml, ./client_server.txt
Výsledek je v tomto případě totožný s lokálním renderingem:
┌──────┐ ┌──────┐ │Client│ │Server│ └──┬───┘ └──┬───┘ │ SYN() │ │ ────────────────> ┌──┴───┐ ┌──┴───┐ │Client│ │Server│ └──────┘ └──────┘
11. Nepatrně složitější diagram: three-way handshake
Vyzkoušejme si nyní vytvořit nepatrně složitější diagram s již zmíněným three-way handskakem. První varianta tohoto diagramu může vypadat takto (jednotlivé zprávy jsou zdánlivě nezávislé):
import napkin @napkin.seq_diagram() def client_server_2(c): client = c.object('Client') server = c.object('Server') with client: server.SYN() with server: client.SYN_ACK() with client: server.ACK()
Obrázek 14: Výsledek předchozího příkazu.
Výsledek v textovém formátu:
┌──────┐ ┌──────┐ │Client│ │Server│ └──┬───┘ └──┬───┘ │ SYN() │ │ ────────────────> │ │ │ SYN_ACK() │ │ <──────────────── │ │ │ ACK() │ │ ────────────────> ┌──┴───┐ ┌──┴───┐ │Client│ │Server│ └──────┘ └──────┘
12. Zobrazení doby zpracování popř. čekání na odpověď
Víme již, že v sekvenčním diagramu lze zobrazit i dobu zpracování požadavku popř. dobu čekání na odpověď. K tomuto účelu slouží vertikální prázdné sloupečky. V nástroji Napkin můžeme tyto sloupečky přidat s využitím konstrukce with v Pythonu:
import napkin @napkin.seq_diagram() def client_server_3(c): client = c.object('Client') server = c.object('Server') with client: with server.SYN(): with client.SYN_ACK(): server.ACK()
Obrázek 15: Výsledek v grafické podobě.
Výsledek v textové podobě:
┌──────┐ ┌──────┐ │Client│ │Server│ └──┬───┘ └──┬───┘ │ SYN() ┌┴┐ │ ──────────────>│ │ │ │ │ ┌┴┐ SYN_ACK() │ │ │ │ <─────────────│ │ └┬┘ └┬┘ │ ACK() │ │ ────────────────> ┌──┴───┐ ┌──┴───┐ │Client│ │Server│ └──────┘ └──────┘
Odlišný sekvenční diagram, ovšem založený na stejných zprávách (je použitých v jinak navrženém bloku with):
import napkin @napkin.seq_diagram() def client_server_4(c): client = c.object('Client') server = c.object('Server') with client: with server.SYN(): client.SYN_ACK() server.ACK()
Obrázek 16: Výsledek v grafické podobě.
Výsledek v textové podobě:
┌──────┐ ┌──────┐ │Client│ │Server│ └──┬───┘ └──┬───┘ │ SYN() ┌┴┐ │ ──────────────>│ │ │ └┬┘ │ SYN_ACK() │ │ <──────────────── │ │ │ ACK() │ │ ────────────────> ┌──┴───┐ ┌──┴───┐ │Client│ │Server│ └──────┘ └──────┘
A konečně explicitní zápis situace, kdy se zpráva vrátí, tj. jedná se o reakci na zprávu předchozí:
import napkin @napkin.seq_diagram() def client_server_5(c): client = c.object('Client') server = c.object('Server') with client: with server.SYN(): c.ret("SYN_ACK") server.ACK()
Obrázek 17: Povšimněte si, že odpověď je zobrazena čárkovanou šipky.
Odlišný typ šipky je použit i v textové podobě diagramu:
┌──────┐ ┌──────┐ │Client│ │Server│ └──┬───┘ └──┬───┘ │ SYN() ┌┴┐ │ ──────────────>│ │ │ └┬┘ │ SYN_ACK │ │ <─ ─ ─ ─ ─ ─ ─ ─ │ │ │ ACK() │ │ ────────────────> ┌──┴───┐ ┌──┴───┐ │Client│ │Server│ └──────┘ └──────┘
13. Reprezentace několika dotazů v sekvenčním diagramu
Pochopitelně nám nic nebrání použít v jednom diagramu několik dotazů, které například mohou reprezentovat rozdílné chování systému pro různé vstupy. Podívejme se na jeden z možných příkladů – přihlašování uživatele, které může být buď úspěšné nebo naopak neúspěšné:
import napkin @napkin.seq_diagram() def request_response_1(c): client = c.object('Client') frontend = c.object('"Front end"') backend = c.object('"Back end"') with client: with frontend.request("login='foo'", "password='bar'"): with backend.try_login("'foo'", "'bar'"): c.ret("login ok") c.ret("login ok") with client: with frontend.request("login='foo'", "password='xyzzy'"): with backend.try_login("'foo'", "'bar'"): c.ret("login failed") c.ret("login failed")
Výsledek bude vypadat následovně:
Obrázek 18: Diagram vytvořený předchozím příkladem.
Prakticky stejného výsledku dosáhneme i tímto kódem:
import napkin @napkin.seq_diagram() def request_response_2(c): client = c.object('Client') frontend = c.object('"Front end"') backend = c.object('"Back end"') with client: with frontend.request("login='foo'", "password='bar'"): with backend.try_login("'foo'", "'bar'"): c.ret("login ok") c.ret("login ok") with frontend.request("login='foo'", "password='xyzzy'"): with backend.try_login("'foo'", "'bar'"): c.ret("login failed") c.ret("login failed")
Obrázek 19: Diagram, který by měl být shodný s diagramem předchozím.
V textové podobě bude diagram vypadat takto:
┌──────┐ ┌─────────┐ ┌────────┐ │Client│ │Front end│ │Back end│ └──┬───┘ └────┬────┘ └───┬────┘ │ request(login='foo', password='bar') ┌┴┐ │ │ ────────────────────────────────────>│ │ │ │ │ │ │ │ │ │try_login('foo', 'bar') ┌┴┐ │ │ │ ─────────────────────> │ │ │ │ │ └┬┘ │ │ │ login ok │ │ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ └┬┘ │ │ login ok │ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ │ │ │ │request(login='foo', password='xyzzy')┌┴┐ │ │ ────────────────────────────────────>│ │ │ │ │ │ │ │ │ │try_login('foo', 'bar') ┌┴┐ │ │ │ ─────────────────────> │ │ │ │ │ └┬┘ │ │ │ login failed │ │ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ └┬┘ │ │ login failed │ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ ┌──┴───┐ ┌────┴────┐ ┌───┴────┐ │Client│ │Front end│ │Back end│ └──────┘ └─────────┘ └────────┘
14. Specifikace opakujících se sekvenčních operací (loop)
Do sekvenčních diagramů lze vložit i symboly reprezentující opakování (loop). V knihovně Napkin je toto chování zajištěno kontextem c.loop():
import napkin @napkin.seq_diagram() def request_response_3(c): client = c.object('Client') frontend = c.object('"Front end"') backend = c.object('"Back end"') with client: with frontend.request("login='foo'", "password='bar'"): with backend.try_login("'foo'", "'bar'"): c.ret("login ok") c.ret("login ok") with c.loop(): with client: with frontend.request("login='foo'", "password='xyzzy'"): with backend.try_login("'foo'", "'bar'"): c.ret("login failed") c.ret("login failed")
S výsledkem:
Obrázek 20: Diagram s cyklem.
Textová podoba výsledku:
┌──────┐ ┌─────────┐ ┌────────┐ │Client│ │Front end│ │Back end│ └──┬───┘ └────┬────┘ └───┬────┘ │ request(login='foo', password='bar') ┌┴┐ │ │ ────────────────────────────────────>│ │ │ │ │ │ │ │ │ │try_login('foo', 'bar') ┌┴┐ │ │ │ ─────────────────────> │ │ │ │ │ └┬┘ │ │ │ login ok │ │ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ └┬┘ │ │ login ok │ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ │ │ │ │ │ │ ╔═══════╤════╪═══════════════════════════════════════╪═════════════════════════╪══════════════╗ ║ LOOP │ │ │ │ ║ ╟───────┘ │ │ │ ║ ║ │request(login='foo', password='xyzzy')┌┴┐ │ ║ ║ │ ────────────────────────────────────>│ │ │ ║ ║ │ │ │ │ ║ ║ │ │ │try_login('foo', 'bar') ┌┴┐ ║ ║ │ │ │ ─────────────────────> │ │ ║ ║ │ │ │ └┬┘ ║ ║ │ │ │ login failed │ ║ ║ │ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ║ ║ │ └┬┘ │ ║ ║ │ login failed │ │ ║ ║ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ ║ ╚════════════╪═══════════════════════════════════════╪═════════════════════════╪══════════════╝ ┌──┴───┐ ┌────┴────┐ ┌───┴────┐ │Client│ │Front end│ │Back end│ └──────┘ └─────────┘ └────────┘
15. Vnořené cykly (loop) v diagramu
Cykly mohou být i vnořené. Bez podrobnějších popisů se podívejme na příklad, v němž je naznačeno, že se přihlašovací procedura může opakovat (což sice není příliš logické, ovšem jedná se pouze o ilustrační příklad):
import napkin @napkin.seq_diagram() def request_response_4(c): client = c.object('Client') frontend = c.object('"Front end"') backend = c.object('"Back end"') with client: with frontend.request("login='foo'", "password='bar'"): with backend.try_login("'foo'", "'bar'"): c.ret("login ok") c.ret("login ok") with c.loop(): with client: with frontend.request("login='foo'", "password='xyzzy'"): with c.loop(): with backend.try_login("'foo'", "'bar'"): c.ret("login failed") c.ret("login failed")
Obrázek 21: Diagram s vnořenými cykly.
Opět si ukažme textovou podobu diagramu:
┌──────┐ ┌─────────┐ ┌────────┐ │Client│ │Front end│ │Back end│ └──┬───┘ └────┬────┘ └───┬────┘ │ request(login='foo', password='bar') ┌┴┐ │ │ ────────────────────────────────────>│ │ │ │ │ │ │ │ │ │try_login('foo', 'bar') ┌┴┐ │ │ │ ─────────────────────> │ │ │ │ │ └┬┘ │ │ │ login ok │ │ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ └┬┘ │ │ login ok │ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ │ │ │ │ │ │ ╔═══════╤════╪═══════════════════════════════════════╪═════════════════════════╪════════════════════════╗ ║ LOOP │ │ │ │ ║ ╟───────┘ │ │ │ ║ ║ │request(login='foo', password='xyzzy')┌┴┐ │ ║ ║ │ ────────────────────────────────────>│ │ │ ║ ║ │ │ │ │ ║ ║ │ │ │ │ ║ ║ │ ╔═══════╤═════╪═╪════════════════════════╪══════════════╗ ║ ║ │ ║ LOOP │ │ │ │ ║ ║ ║ │ ╟───────┘ │ │ │ ║ ║ ║ │ ║ │ │try_login('foo', 'bar') ┌┴┐ ║ ║ ║ │ ║ │ │ ─────────────────────> │ │ ║ ║ ║ │ ║ │ │ └┬┘ ║ ║ ║ │ ║ │ │ login failed │ ║ ║ ║ │ ║ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ║ ║ ║ │ ╚═════════════╪═╪════════════════════════╪══════════════╝ ║ ║ │ └┬┘ │ ║ ║ │ login failed │ │ ║ ║ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ ║ ╚════════════╪═══════════════════════════════════════╪═════════════════════════╪════════════════════════╝ ┌──┴───┐ ┌────┴────┐ ┌───┴────┐ │Client│ │Front end│ │Back end│ └──────┘ └─────────┘ └────────┘
16. Seskupení zpráv v diagramu
Cyklům je podobná i značka pro seskupení zpráv v diagramu. V knihovně Napkin se pro tento účel používá konstrukce with c.group(„Popis skupiny“) tak, jak je to naznačeno v dalším (již předposledním) demonstračním příkladu:
import napkin @napkin.seq_diagram() def request_response_5(c): client = c.object('Client') frontend = c.object('"Front end"') backend = c.object('"Back end"') with c.group("Successful login"): with client: with frontend.request("login='foo'", "password='bar'"): with backend.try_login("'foo'", "'bar'"): c.ret("login ok") c.ret("login ok") with c.group("Failed login"): with client: with frontend.request("login='foo'", "password='xyzzy'"): with c.loop(): with backend.try_login("'foo'", "'bar'"): c.ret("login failed") c.ret("login failed")
Obrázek 22: Diagram se seskupenými zprávami.
Opět si ukažme textovou podobu diagramu:
┌──────┐ ┌─────────┐ ┌────────┐ │Client│ │Front end│ │Back end│ └──┬───┘ └────┬────┘ └───┬────┘ │ │ │ ╔════════════╪══════╤════════════════════════════════╪═════════════════════════╪══════════════╗ ║ SUCCESSFUL LOGIN │ │ │ ║ ╟──────────────request(login='foo', password='bar') ┌┴┐ │ ║ ║ │ ────────────────────────────────────>│ │ │ ║ ║ │ │ │ │ ║ ║ │ │ │try_login('foo', 'bar') ┌┴┐ ║ ║ │ │ │ ─────────────────────> │ │ ║ ║ │ │ │ └┬┘ ║ ║ │ │ │ login ok │ ║ ║ │ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ║ ║ │ └┬┘ │ ║ ║ │ login ok │ │ ║ ║ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ ║ ╚════════════╪═══════════════════════════════════════╪═════════════════════════╪══════════════╝ │ │ │ │ │ │ ╔════════════╪══╤════════════════════════════════════╪═════════════════════════╪════════════════════════╗ ║ FAILED LOGIN │ │ │ ║ ╟─────────────request(login='foo', password='xyzzy')┌┴┐ │ ║ ║ │ ────────────────────────────────────>│ │ │ ║ ║ │ │ │ │ ║ ║ │ │ │ │ ║ ║ │ ╔═══════╤═════╪═╪════════════════════════╪══════════════╗ ║ ║ │ ║ LOOP │ │ │ │ ║ ║ ║ │ ╟───────┘ │ │ │ ║ ║ ║ │ ║ │ │try_login('foo', 'bar') ┌┴┐ ║ ║ ║ │ ║ │ │ ─────────────────────> │ │ ║ ║ ║ │ ║ │ │ └┬┘ ║ ║ ║ │ ║ │ │ login failed │ ║ ║ ║ │ ║ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ║ ║ ║ │ ╚═════════════╪═╪════════════════════════╪══════════════╝ ║ ║ │ └┬┘ │ ║ ║ │ login failed │ │ ║ ║ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ ║ ╚════════════╪═══════════════════════════════════════╪═════════════════════════╪════════════════════════╝ ┌──┴───┐ ┌────┴────┐ ┌───┴────┐ │Client│ │Front end│ │Back end│ └──────┘ └─────────┘ └────────┘
17. Poznámky přiřazené k uzlům, zprávám či skupinám
Poslední vlastností knihovny Napkin, kterou si dnes ukážeme, je možnost přidat poznámky (komentáře) k uzlům, zprávám popř. skupinám. Prakticky každý objekt, s nímž se v knihovně Napkin pracuje, poskytuje metodu note pro přidání poznámky (viz zvýrazněné části skriptu):
import napkin @napkin.seq_diagram() def request_response_6(c): client = c.object('Client') frontend = c.object('"Front end"') backend = c.object('"Back end"') client.note("Web client") frontend.note("Web front end (JS)") backend.note("Back end (Go)") with c.group("Successful login"): with client: c.note("Login with proper name and password") with frontend.request("login='foo'", "password='bar'").note("Correct password"): with backend.try_login("'foo'", "'bar'"): c.ret("login ok") c.ret("login ok") with c.group("Failed login"): with client: c.note("Login with improper name and password") with frontend.request("login='foo'", "password='xyzzy'").note("Bad password"): with c.loop(): with backend.try_login("'foo'", "'bar'"): c.ret("login failed") c.ret("login failed") c.note("Trying again ... is meaningless")
Obrázek 23: Diagram s přidanými poznámkami.
Poznámky se pochopitelně objeví i v textovém výstupu:
┌──────┐ ┌─────────┐ ┌────────┐ │Client│ │Front end│ │Back end│ └──┬───┘ └────┬────┘ └───┬────┘ ╔═════╧══════╗ │ │ ║Web client ░║ │ │ ╚═════╤══════╝ │ │ │ ╔═════════╧══════════╗ │ │ ║Web front end (JS) ░║ │ │ ╚═════════╤══════════╝ │ │ │ ╔══════╧════════╗ │ │ ║Back end (Go) ░║ │ │ ╚══════╤════════╝ │ │ │ ╔═══════════════════╤════╪═══════════════════════════════════════╪═════════════════════════╪══════════════╗ ║ SUCCESSFUL LOGIN │ │ │ │ ║ ╟──────╔═════════════════╧═══════════════════╗ │ │ ║ ║ ║Login with proper name and password ░║ │ │ ║ ║ ╚═════════════════╤═══════════════════╝ │ │ ║ ║ │ request(login='foo', password='bar') ┌┴┐ ╔══════════════════╗ │ ║ ║ │ ────────────────────────────────────>│ │ ║Correct password ░║ │ ║ ║ │ │ │ ╚══════════════════╝ │ ║ ║ │ │ │try_login('foo', 'bar') ┌┴┐ ║ ║ │ │ │ ─────────────────────> │ │ ║ ║ │ │ │ └┬┘ ║ ║ │ │ │ login ok │ ║ ║ │ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ║ ║ │ └┬┘ │ ║ ║ │ login ok │ │ ║ ║ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ ║ ╚════════════════════════╪═══════════════════════════════════════╪═════════════════════════╪══════════════╝ │ │ │ │ │ │ ╔═══════════════╤═════════╪═══════════════════════════════════════╪═════════════════════════╪════════════════════════╗ ║ FAILED LOGIN │ │ │ │ ║ ╟──────╔══════════════════╧════════════════════╗ │ │ ║ ║ ║Login with improper name and password ░║ │ │ ║ ║ ╚══════════════════╤════════════════════╝ │ │ ║ ║ │request(login='foo', password='xyzzy')┌┴┐ ╔══════════════╗ │ ║ ║ │ ────────────────────────────────────>│ │ ║Bad password ░║ │ ║ ║ │ │ │ ╚══════════════╝ │ ║ ║ │ │ │ │ ║ ║ │ ╔═══════╤═════════════╪═╪════════════════════════╪══════════════╗ ║ ║ │ ║ LOOP │ │ │ │ ║ ║ ║ │ ╟───────┘ │ │ │ ║ ║ ║ │ ║ │ │try_login('foo', 'bar') ┌┴┐ ║ ║ ║ │ ║ │ │ ─────────────────────> │ │ ║ ║ ║ │ ║ │ │ └┬┘ ║ ║ ║ │ ║ │ │ login failed │ ║ ║ ║ │ ║ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ║ ║ ║ │ ║ │ │ │ ║ ║ ║ │ ║ ╔══════════════╧═╧════════════════╗ │ ║ ║ ║ │ ║ ║Trying again ... is meaningless ░║ │ ║ ║ ║ │ ╚══════╚═════════════════════════════════╝═══════╪══════════════╝ ║ ║ │ └┬┘ │ ║ ║ │ login failed │ │ ║ ║ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ ║ ╚═════════════════════════╪═══════════════════════════════════════╪═════════════════════════╪════════════════════════╝ ┌──┴───┐ ┌────┴────┐ ┌───┴────┐ │Client│ │Front end│ │Back end│ └──────┘ └─────────┘ └────────┘
18. Repositář s demonstračními příklady
Zdrojové kódy všech dnes popsaných demonstračních příkladů určených pro Python 3 byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs. V případě, že nebudete chtít klonovat celý repositář (ten je ovšem stále velmi malý, dnes má velikost zhruba několik desítek kilobajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
19. Odkazy na články s tématem programové tvorby grafů a diagramů
V této kapitole jsou uvedeny odkazy na předchozí články, v nichž jsme se zabývali tvorbou různých typů grafů a diagramů – a to v naprosté většině případů s využitím nějakého doménově specifického jazyka neboli DSL (Domain Specific Language) popř. nějakého univerzálního programovacího jazyka:
- Nástroje pro tvorbu UML diagramů
https://www.root.cz/clanky/nastroje-pro-tvorbu-uml-diagramu/ - Nástroje pro tvorbu UML diagramů z příkazové řádky
https://www.root.cz/clanky/nastroje-pro-tvorbu-uml-diagramu-z-prikazove-radky/ - Nástroje pro tvorbu UML diagramů z příkazové řádky (II)
https://www.root.cz/clanky/nastroje-pro-tvorbu-uml-diagramu-z-prikazove-radky-ii/ - Nástroje pro tvorbu grafů a diagramů z příkazové řádky
https://www.root.cz/clanky/nastroje-pro-tvorbu-grafu-a-diagramu-z-prikazove-radky/ - Sledování správy paměti v Pythonu s využitím nástroje objgraph
https://www.root.cz/clanky/sledovani-spravy-pameti-v-pythonu-s-vyuzitim-nastroje-objgraph/ - Programová tvorba diagramů v jazyku Clojure s využitím knihovny Rhizome
https://www.root.cz/clanky/programova-tvorba-diagramu-v-jazyku-clojure-s-vyuzitim-knihovny-rhizome/
20. Odkazy na Internetu
- Napkin na GitHubu
https://github.com/pinetr2e/napkin - Napkin 0.6.8 na PyPi
https://pypi.org/project/napkin/ - PlantUML (home page)
http://plantuml.sourceforge.net/ - PlantUML (download page)
http://sourceforge.net/projects/plantuml/files/plantuml.jar/download - PlantUML (Language Reference Guide)
http://plantuml.sourceforge.net/PlantUML_Language_Reference_Guide.pdf - Rhizome
https://github.com/ztellman/rhizome - Swagger to UML
https://github.com/nlohmann/swagger_to_uml - pydiagrams
https://github.com/billingtonm/pydiagrams - graphviz(3) – Linux man page
https://linux.die.net/man/3/graphviz - dot(1) – Linux man page
https://linux.die.net/man/1/dot - neato(1) – Linux man page
https://linux.die.net/man/1/neato - twopi(1) – Linux man page
https://linux.die.net/man/1/twopi - circo(1) – Linux man page
https://linux.die.net/man/1/circo - fdp(1) – Linux man page
https://linux.die.net/man/1/fdp - sfdp(1) – Linux man page
https://linux.die.net/man/1/sfdp - Plain-text diagrams take shape in Asciidoctor!
http://asciidoctor.org/news/2014/02/18/plain-text-diagrams-in-asciidoctor/ - Graphviz – Graph Visualization Software
http://www.graphviz.org/ - graphviz (Manual Page)
http://www.root.cz/man/7/graphviz/ - dot (Manual page)
http://www.root.cz/man/1/dot/ - dot (Manual v PDF)
https://graphviz.org/pdf/dot.1.pdf - Ditaa home page
http://ditaa.sourceforge.net/ - Ditaa introduction
http://ditaa.sourceforge.net/#intro - Ditaa usage
http://ditaa.sourceforge.net/#usage - Node, Edge and Graph Attributes
http://www.graphviz.org/doc/info/attrs.html - Graphviz (Wikipedia)
http://en.wikipedia.org/wiki/Graphviz - Unified Modeling Language
https://en.wikipedia.org/wiki/Unified_Modeling_Language - UML basics: The sequence diagram
http://www.ibm.com/developerworks/rational/library/3101.html - UML 2 State Machine Diagrams: An Agile Introduction
http://www.agilemodeling.com/artifacts/stateMachineDiagram.htm - Sequence diagram (Wikipedia)
https://en.wikipedia.org/wiki/Sequence_diagram - UML 2 Sequence Diagrams: An Agile Introduction
http://www.agilemodeling.com/artifacts/sequenceDiagram.htm - A Quick Introduction to UML Sequence Diagrams
http://www.tracemodeler.com/articles/a_quick_introduction_to_uml_sequence_diagrams/ - UML Sequence Diagrams
https://www.uml-diagrams.org/sequence-diagrams.html - Web Sequence Diagrams
https://www.websequencediagrams.com/ - Drawing sequence diagrams “napkin style”
https://modeling-languages.com/drawing-sequence-diagrams-napkin-style/ - Curated list of UML tools – 2020 edition
https://modeling-languages.com/uml-tools/#textual - Flowchart diagrams vs. UML activity diagrams
https://stackoverflow.com/questions/7081215/flowchart-diagrams-vs-uml-activity-diagrams - Kopenograms – Graphical Language for Structured Algorithms
https://kopenogram.org/Assets/Kopenograms_Graphical_Language_for_Structured_Algorithms.pdf