Co obnáší převod UML do programovacího jazyku? 2. díl

V prvním díle jsem představil, co vlastně chci dělat a jak si práci ulehčit. Nyní konečně přistoupím k popisu vlastního řešení.

Řekli jsme si, že nemusíme implementovat celé UML, abychom měli celé UML implementované. Stačí jít o úroveň abstrakce výše (tedy na MOF) a udělat jen kus. Ten zbytek se pak zařídí tak nějak sám.

V definici standardu MOF je víceméně jasně řečeno, co stačí implementovat, abychom dokázali pracovat s libovolným standardem od OMG (uvědomuji si, že teď hodně zjednodušuji, ale výklad MOF spadá např. do školení Příprava k certifikační zkoušce OCUP 2 Advanced). Jak tedy napsat zdrojový kód, abychom byli spokojeni?

Podívejme se na základní diagram z UML standardu, který musí znát každý, kdo chce složit libovolnou zkoušku z UML. Je to obrázek, se kterým pracuji na každém školení k certifikační přípravě od úrovně Foundation přes Intermediate k Advanced.

Metamodel UML: Root diagram

Výchozím prvkem je pro všechny ostatní metatřída Element. Ta může být vlastněna maximálně jedním (jiným) elementem a dále může vlastnit libovolný počet jiných elementů (např. třída vlastní své atributy nebo balík vlastní prvky v něm obsažené). Od ní jsou odvozeny vztah (Relationship), směrový vztah (DirectedRelationShip) a komentář (Comment).

To první, co člověka napadne, je vyvodit závěr, že udělám třídu pro každou metatřídu a je hotovo. Jenže ouha. Existují metatřídy (např. specifikace hodnoty – ValueSpecification nebo klasifikátor – Classifier), a není jich málo, které mají více než jednoho předka. S tím sice dokáže vcelku dobře žít C++, ale pro mě je ten jediný jazyk C#, který vícenásobnou dědičnost rozhodně nepodporuje. Co s tím? Je zřejmé, že vícenásobnou dědičnost mohu simulovat pouze pomocí rozhraní. A opravdu, jde o cestu, kterou jsem se vydal. Každá metatřída má svůj vlastní obraz v podobě rozhraní. Pro metatřídu Element může vypadat takto:

To, co vás možná zaujalo, je typ UmlSet. Jestliže se znovu podíváte na klíčový diagram z metamodelu UML, uvidíte u asociací slova jako je union a subsets. Naštěstí k tomu nepotřebujete semestr Teorie množin (příhodně nazvané temno), ale stačí základní škola: množina se skládá z několika podmnožin. Union znační, že daný vztah je vytvořen sjednocením takovým podmnožin, a je vcelku jedno, kdo vše se přihlásí k tomu, že je danou podmnožinou.

Opět velice zjednodušeně: vztah (Relationship) není nic jiného než sjednocení podmnožin. Jenže jakých? Např. zdrojů a cílů, které definuje směrový vztah (všimněte si slov subsets relatedElement). A přesně tuto schopnost umožňuje UmlSet (UmlSet není třída z nějakého jmenného prostoru .NETu, ale vytvořil jsem si ji sám).

Pojďme však dál. Rozhraní není nic jiného než deklarace nějakých vlastností, nikoliv však jejich implementace. Jak tedy taková rozhraní implementovat?  Opět to první, co člověk napadne, je, co rozhraní, to třída. No jo, ne že by to nešlo, ale co s vícenásobnou dědičností? V tu chvilku se také dostaneme do problémů v podobě implementace téhož v několika třídách. A to není dobře.

Další možností tedy je mít jednu třídu a v ní implementovat všechna rozhraní. Pokud programujete v C#, tak víte, že rozhraní lze implementovat implicitně a explicitně. Kterou cestu zvolit?

Jak jsem již psal, jsem lenivý, tedy jsem zvolil cestu implicitní implementace. Jenže jsem záhy narazil. Podívejte se na další diagram z metamodelu pro literály: 

Metamodel UML: Literály

Myslíte, že v implicitní implementaci např. metatříd LitealString a LiteralInteger můžete mít jedinou vlastnost (property) implementující atribut value podle předpisu? Odpověď je jasná: nelze to. Takže implementace rozhraní musí být explicitní. Např. pro výchozí diagram může vypadat takto:

Mít vše v jediné třídě s sebou však nese různá úskalí. Např. taková, že dotaz na typ (variable is ICokoliv) vám vrátí true. Z toho důvodu jsem konstruktor dal jako soukromý a vytvořil statické metody pro vytvoření různých prvků (je jasné, že až na komentář jsou všechny metatřídy abstraktní, takže je špatně, že se vytváří přímé instance, ovšem pro unit testy se to hodí):

Díky tomu dotaz na typ zafunguje správně.

Závěr

A to je vlastně vše, co jsem vám chtěl ukázat. UML jako takové implementovat v programovacím jazyce možné je. Pouze to znamená poměrně hluboké znalosti nejen UML, ale i dalších prostředků (především MOF a XMI), pomocí kterých dosáhnete svého. Vždy je důležité vědět, na které úrovni abstrakce se pohybujete. Pokud si to neuvědomíte, narazíte.

Možná jste se při čtení ptali, k čemu to vlastně celé může být dobré. Upřímně, pro běžného uživatele UML k ničemu. Ovšem existují dvě skupiny čtenářů, kterým to může prospět. Tou první jsou ti, kteří se chtějí připravit k certifikační zkoušce OCUP 2 Advanced, a tou druhou ti, kteří chtějí dělat svou práci pořádně.

Zanechat odpověď

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *