Obsah
1. Nástroje pro tvorbu UML diagramů z příkazové řádky
4. Změna stylu diagramu aktivit
7. Metody a atributy v diagramu tříd
1. Nástroje pro tvorbu UML diagramů z příkazové řádky
Jazyk označovaný zkratkou UML neboli Unified Modeling Language je, jak již jeho název napovídá, unifikovaný modelovací jazyk, který má, na rozdíl od převážně textově orientovaných programovacích jazyků, vlastní grafickou syntaxi (tj. pravidla určující způsob sestavování jednotlivých elementů jazyka do větších objektů) a taktéž sémantiku (tj. jednoznačná pravidla určující jednotlivým syntaktickým výrazům jejich význam). UML se dnes používá jak v průběhu návrhu architektury rozsáhlých informačních systémů, tak i na nižší úrovni: pro popis statického popř. i dynamického chování jednotlivých modulů, z nichž se informační systém skládá (jak uvidíme dále, lze například diagram aktivit použít i namísto vývojového diagramu). Některé společnosti mají celý návrh IS postavený právě na UML, ovšem většinou se dává přednost pragmatičtějšímu přístupu a využívají se jen některé UML diagramy, které bývají doplněny například pseudokódem (navíc se ukazuje, že pro neobjektové prvky informačních systémů, například pro návrh relačních databází, nemusí být UML tím nejlepším řešením, což do značné míry souvisí se způsobem vývoje UML).
Některými nástroji určenými pro tvorbu UML diagramů jsme se již zabývali téměř před deseti lety :-) v článku, který naleznete na adrese http://www.root.cz/clanky/nastroje-pro-tvorbu-uml-diagramu/. Většina zmíněných nástrojů kupodivu stále existuje (samozřejmě v modernizované podobě), i když v některých případech došlo ke změně jejich jména. Dnes se ovšem budeme zabývat utilitou, která od uživatele nevyžaduje ani ruční kreslení diagramů s využitím grafického uživatelského rozhraní a dokonce ani nezpracovává zdrojové kódy a netvoří z nich diagram tříd. Tento relativně jednoduše použitelný nástroj se jmenuje PlantUML.
2. PlantUML
Nástroj PlantUML (http://plantuml.sourceforge.net/) dokáže na základě textového popisu UML diagramu vytvořit bitmapový obrázek s tímto diagramem, přičemž uživatel může do jisté míry ovlivnit způsob jeho vykreslení, přidat popis hran apod. V současné verzi PlantUML jsou podporovány následující typy UML diagramů: diagram aktivit, stavový diagram, diagram tříd, diagram objektů, diagram komponent, diagram užití a sekvenční diagram. Ve skutečnosti sice 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. PlantUML je naprogramovaný v Javě, ovšem podobně jako tomu bylo v případě minule 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.
3. Diagram aktivit
V diskuzi vedené pod předchozím článkem se mj. řešila i problematika (automatizovaného) vykreslování vývojových diagramů (flow chart). Klasické vývojové diagramy sice nejsou v UML přímo podporovány, ale existuje zde velmi podobný typ diagramu nazvaný diagram aktivit. Tímto diagramem je možné nahradit vývojové diagramy s větvením i programovými smyčkami. Diagram aktivit lze v PlantUML vytvořit velmi jednoduchým způsobem, což si ostatně ukážeme na několika demonstračních příkladech.
První příklad obsahuje definici diagramu aktivit, který obsahuje jen jedinou akci, tj. uzel představující většinou dále nedělený krok, který se v systému provádí. Diagram obsahuje symbol inicializace (černá tečka), koncový bod (kružnice s černou tečkou uprostřed) a uzel s prováděným krokem. Mezi symbolem inicializace a uzlem je nakreslena šipka, podobná šipka je pak nakreslena mezi uzlem a koncovým bodem. V PlantUML je tento diagram představován následujícím kódem (textovým souborem). Povšimněte si použití symbolů (*) jak pro symbol inicializace, tak i pro koncový bod:
@startuml (*) --> "Aktivita" "Aktivita" --> (*) @enduml
Deklarace začíná řádkem @startuml a končí řádkem @enduml. Zajímavé je, že není nutné uvádět typ diagramu – ten je odvozen z kontextu.
Spuštění se provede následovně:
java -jar plantuml.jar ActivityDiagram1.txt
Obrázek 1: Diagram aktivit vygenerovaný ze zdrojového kódu prvního příkladu. Ve výchozím nastavení jsou uzly umístěné pod sebe a šipky mezi nimi směřují vertikálně dolů (v případě, že není použito rozvětvení, kterým se budeme zabývat níže).
Podívejme se nyní na nepatrně složitější diagram aktivit, který obsahuje dva sekvenčně prováděné kroky. Tento diagram je možné vytvořit explicitním uvedením všech vazeb (šipek) mezi oběma uzly, symbolem inicializace i koncovým bodem. Takto deklarovaný diagram může být výsledkem práce nějakého skriptu apod.:
@startuml (*) --> "Aktivita1" "Aktivita1" --> "Aktivita2" "Aktivita2" --> (*) @enduml
Obrázek 2: Diagram aktivit vygenerovaný ze zdrojového kódu druhého příkladu.
Ve skutečnosti je ale možné popis diagramu zjednodušit způsobem naznačeným pod tímto odstavcem. Povšimněte si, že každý uzel je zde zmíněn pouze jedenkrát, protože vazba mezi uzlem „Aktivita1“ a „Aktivita2“ je naznačena samostatnou šipkou:
@startuml (*) --> "Aktivita1" --> "Aktivita2" --> (*) @enduml
Obrázek 3: Diagram aktivit vygenerovaný ze zdrojového kódu třetího příkladu. Tento obrázek je totožný s druhým obrázkem, ovšem deklarace diagramu aktivit je jednodušší.
4. Změna stylu diagramu aktivit
Jak již bylo řečeno v úvodní kapitole, je možné při tvorbě diagramů s využitím nástroje PlantUML ovlivnit způsob vykreslení diagramu. V mnoha případech se nevyhneme přidání popisu k jednotlivým šipkám diagramu aktivit, což lze zajistit zápisem poznámky do hranatých závorek:
@startuml (*) --> [začátek procesu] "Aktivita1" --> [zpracování požadavku] "Aktivita2" --> [konec procesu] (*) @enduml
Obrázek 4: Diagram aktivit vygenerovaný ze zdrojového kódu čtvrtého příkladu. Uzly jsou v tomto diagramu umístěny pod sebe.
Taktéž je možné změnit uspořádání uzlů (a tím pádem i směr šipek). Namísto symbolu → představujícího šipku je možné alternativně použít:
- -down → odpovídá běžné šipce směřující (šikmo) dolů
- -right → šipka orientovaná doprava
- → stejný význam jako má předchozí symbol
- -left → šipka orientovaná doleva
- -up → šipka orientovaná nahoru
Zkusme si nyní předchozí diagram změnit takovým způsobem, aby byly všechny uzly umístěné v jedné horizontální rovině. Úprava je ve skutečnosti velmi jednoduchá:
@startuml (*) -right-> [začátek procesu] "Aktivita1" -right-> [zpracování požadavku] "Aktivita2" -right-> [konec procesu] (*) @enduml
Obrázek 5: Diagram aktivit vygenerovaný ze zdrojového kódu pátého příkladu. Nyní jsou všechny uzly zobrazeny v jedné horizontální rovině.
5. Větvení v diagramu aktivit
Velmi důležitou součástí naprosté většiny diagramů aktivit je rozvětvení. To je reprezentováno malým kosočtvercem, takže se tento prvek diagramu podobá rozvětvení používaného v klasickém vývojovém diagramu, ovšem s tím rozdílem, že se podmínka pro rozvětvení může (ale nemusí) psát do předchozího kroku (zde si dovolím sémantiku diagramu aktivit nepatrně pozměnit, protože samotné rozvětvení není v diagramu aktivit chápáno jako samostatný krok). Pojďme si nyní ukázat, jak by se postupovalo při vytváření diagramu analogickému známému vtípku o univerzálním návodu na opravu všeho: http://joyreactor.com/post/287235. Zde se již setkáme s potřebou větvení, které se do diagramu aktivit zapisuje – což mnoho programátorů patrně potěší – pomocí slov if, then, else a endif. Jednoduché rozvětvení může být zapsáno následovně:
@startuml (*) --> "Does it move?" if "" then --> [yes] "WD-40" else --> [no] "Duct Tape" endif "WD-40" --> (*) "Duct Tape" --> (*) @enduml
Obrázek 6: Jednoduché rozvětvení reprezentované v diagramu aktivit.
Rozvětvení deklarované v předchozím diagramu se sice vykreslilo korektně (= tak, jak jsme předpokládali), ovšem následující část již způsobí problémy, neboť PlantUML bude považovat oba uzly nadepsané „Should it?“ za uzel jediný. Ostatně můžeme si to sami vyzkoušet:
@startuml (*) --> "Does it move?" if "" then --> [yes] "Should it?" else --> [no] "Should it?" endif @enduml
Obrázek 7: Namísto dvou uzlů máme uzel jediný.
Řešení tohoto problému je poměrně jednoduché. PlantUML totiž umožňuje pojmenovat jednotlivé uzly (kroky), přičemž zadaná jména se mohou odlišovat od textů zobrazených v uzlu. V našem případě tedy můžeme první uzel pojmenovat s1 a druhý s2, což je patrné z následujícího upraveného demonstračního příkladu:
@startuml (*) --> "Does it move?" if "" then --> [yes] "Should it?" as s1 else --> [no] "Should it?" as s2 endif @enduml
Obrázek 8: Dva rozdílné uzly se stejným textem.
Jednotlivé větve se samozřejmě mohou (a musí) dále rozvětvovat. V deklaraci diagramu se to zařídí jednoduše – vnořenými strukturami if-then-else-endif. Zkusme nyní upravit náš demonstrační příklad takovým způsobem, aby se použilo další rozvětvení v obou dalších větvích:
@startuml (*) --> "Does it move?" if "" then --> [yes] "Should it?" as s1 if "" then --> [yes] "No problem" as np1 else --> [no] "Use duct tape" endif else --> [no] "Should it?" as s2 if "" then --> [yes] "Use WD-40" else --> [no] "No problem" as np2 endif endif @enduml
Obrázek 9: Návod jak opravit vše, alfa verze.
Zbývá nám doplnit koncový bod, což je již snadné:
@startuml (*) --> "Does it move?" if "" then --> [yes] "Should it?" as s1 if "" then --> [yes] "No problem" as np1 np1 --> (*) else --> [no] "Use duct tape" as tape tape --> (*) endif else --> [no] "Should it?" as s2 if "" then --> [yes] "Use WD-40" as wd40 wd40 --> (*) else --> [no] "No problem" as np2 np2 --> (*) endif endif @enduml
Obrázek 10: Návod jak opravit vše, beta verze.
Pro lepší názornost je možné jednotlivé podvětve zvýraznit odsazením, které je samozřejmě taktéž podporováno:
@startuml (*) --> "Does it move?" if "" then --> [yes] "Should it?" as s1 if "" then --> [yes] "No problem" as np1 np1 --> (*) else --> [no] "Use duct tape" as tape tape --> (*) endif else --> [no] "Should it?" as s2 if "" then --> [yes] "Use WD-40" as wd40 wd40 --> (*) else --> [no] "No problem" as np2 np2 --> (*) endif endif @enduml
Obrázek 11: Návod jak opravit vše, finální verze.
Na závěr si ještě ukažme, jak se k jednotlivým uzlům mohou přiřadit poznámky (notes). Povšimněte si, že u uzlů je možné specifikovat, zda se poznámka zobrazí nalevo či napravo a taktéž to, že je možné zapisovat poznámku na větší množství řádků:
@startuml (*) --> "Some Activity" note right: This activity has to be defined "Some Activity" --> (*) note left This note is on several lines end note @endum
Obrázek 12: Poznámky přiřazené k uzlům.
6. Diagram tříd
Druhým velmi často používaným diagramem definovaným ve standardu UML je diagram tříd (class diagram). V tomto typu diagramu je možné zobrazit jednoduché i složitější vztahy mezi třídami, například fakt, že třída Boolean je potomkem třídy Object (příklad je převzatý z Javy):
@startuml Object <|-- Boolean @enduml
Obrázek 13: Vztah mezi třídami Object a Boolean zobrazený v diagramu tříd.
Můžeme samozřejmě zobrazit i vazby mezi větším počtem tříd. Povšimněte si, že nikde není zapotřebí specifikovat, že se má zobrazit diagram tříd a ne diagram aktivit: toto rozhodnutí provede PlantUML automaticky:
@startuml Object <|-- Boolean Object <|-- String Object <|-- Number Number <|-- Integer Number <|-- Double @enduml
Obrázek 14: Vztahy mezi větším počtem tříd.
7. Metody a atributy v diagramu tříd
U jednotlivých tříd je možné deklarovat jejich atributy a taktéž metody. Příklad popsaný v předchozí kapitole je možné relativně jednoduchým způsobem vylepšit o atributy a metody. Je to velmi snadné – postačuje napsat jméno třídy a za dvojtečkou pak jméno atributu či jméno metody (i se závorkami, samozřejmě i s názvy parametrů, pokud je to nutné):
@startuml Object <|-- Boolean Object: equals() Object: hashCode() Object: clone() Object <|-- String Object <|-- Number Boolean: FALSE Boolean: TRUE Number: byteValue() Number <|-- Integer Number <|-- Double String: indexOf() String: isEmpty() String: length() String: split() Integer: MAX_VALUE Integer: MIN_VALUE Double: MAX_VALUE Double: MIN_VALUE Double: POSITIVE_INFINITY Double: NEGATIVE_INFINITY Double: NaN @enduml
Obrázek 15: Atributy a metody několika tříd v hierarchii.
Pokud je nutné zvýraznit i přístupová práva k atributům, je vhodnější použít alternativní způsob zápisu metadat o třídě. Ten se podobá zápisu deklarace třídy v C++ či Javě, přičemž znaky se speciálním významem před názvem atributu určují viditelnost i přístupová práva:
@startuml class TestClass { -privateField #protectedField ~packageProtectedField +publicField } @enduml
Obrázek 16: Třída s atributy, které mají různá přístupová práva.
Pro úplnost doplňme třídu i o metody s různými přístupovými právy:
@startuml class TestClass { -privateField #protectedField ~packageProtectedField +publicField -privateMethod() #protectedMethod() ~packageProtectedMethod() +publicMethod() } @enduml
Obrázek 17: 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 provedeno automaticky.
8. Rozhraní v diagramu tříd
Poslední důležitou vlastností nástroje PlantUML při práci s diagramem tříd je vytvoření uzlů představujících rozhraní. Prvním krokem je změna stylu šipek – namísto plných úseček se použijí čárkované úsečky, a to použitím jiného symbolu pro šipky v deklaraci diagramu:
@startuml "Iterable<E>" <|.. "Collection<E>" "Collection<E>" <|.. "List<E>" "Collection<E>" <|.. "Queue<E>" "Collection<E>" <|.. "Set<E>" "Set<E>" <|.. "SortedSet<E>" @enduml
Obrázek 18: Třídy nebo rozhraní?
Ve skutečnosti je však předchozí diagram nekorektní, protože Iterable atd. jsou skutečná rozhraní a měla by být označena písmenem I (interface) a nikoli C (class). To lze velmi snadno napravit použitím slova interface:
@startuml interface "Iterable<E>" <|.. interface "Collection<E>" "Collection<E>" <|.. interface "List<E>" "Collection<E>" <|.. interface "Queue<E>" "Collection<E>" <|.. interface "Set<E>" "Set<E>" <|.. interface "SortedSet<E>" @enduml
Obrázek 19: Nyní jsou rozhraní skutečná rozhraní.
Pro úplnost si ještě ukažme poněkud složitější příklad s větším množstvím rozhraní a současně i tříd. Povšimněte si, že jedna třída může implementovat větší množství rozhraní:
@startuml interface "Iterable<E>" <|.. interface "Collection<E>" "Collection<E>" <|.. interface "List<E>" "Collection<E>" <|.. interface "Queue<E>" "Collection<E>" <|.. interface "Set<E>" "Set<E>" <|.. interface "SortedSet<E>" "List<E>" <|.. ArrayList "List<E>" <|.. LinkedList "Set<E>" <|.. HashSet "Set<E>" <|.. TreeSet "SortedSet<E>" <|.. TreeSet "Queue<E>" <|.. LinkedList @enduml
Obrázek 20: Diagram rozhraní a tříd.
9. Odkazy na Internetu
- Nástroje pro tvorbu UML diagramů (nikoli z příkazové řádky):
http://www.root.cz/clanky/nastroje-pro-tvorbu-uml-diagramu/ - 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 - 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/ - Ditaa home page
http://ditaa.sourceforge.net/ - Ditaa introduction
http://ditaa.sourceforge.net/#intro - Ditaa usage
http://ditaa.sourceforge.net/#usageNode, Edge and Graph Attributes
http://www.graphviz.org/doc/info/attrs.html - Graphviz (Wikipedia)
http://en.wikipedia.org/wiki/Graphviz