JAKÉ JSOU DOPORUČENÉ POSTUPY PŘI DĚLENÍ SYSTÉMU NA MENŠÍ MODULY RNDr. Ilja Kraval, srpen 2009 http://www.objects.cz
ÚVOD
Mezi nejdůležitější kapitoly našeho pobytového školení návrhu IS pomocí UML patří bezesporu kapitola „Vzor Modulární nůžky“. V této kapitole je pečlivě a podrobně rozebrán způsob, jak se přesně postupuje při rozdělení systému na menší části, tedy moduly (podle technologie se jedná o následné rozložení tříd do assembly v .NET, do prvků package v prostředí JAVA, na unity v Pascalu resp. library v C++ aj.). Mimochodem právě moderní technologie komponent v .NET a v prostředí JAVA, které umožňují linkování modulů systému ex-post po kompilaci, nutně předpokládají provedení dobrých střihů systému (a také proto byly tyto technologie zavedeny). Pokud se nám dobrý střih systému nepodaří, hrozí informačnímu systému jedna z nejhorších bohužel velmi často se vyskytujících nemocí, a tou je syndrom „molochálního“ dále již nedělitelného systému. Takovýto „molochální systém“ obsahuje rozsáhlé a bohužel nerozdělitelné moduly. V důsledku to znamená vznik příliš velkých celků kódu podléhajících změnovému řízení. Systém pak na první pohled vypadá jako jeden velký „zašmodrchanec“, který nelze rozplést. Navíc uvedená situace je také velmi nepříjemná i pro vedoucího projektu. Vývoj „molochálního“ systému se velmi špatně řídí z těchto důvodů: 1. Nad velkou částí kódu pracuje najednou příliš mnoho vývojářů. Vznikají tak „tlačenice a kolize“ při zamykání v nástrojích pro řízení verzí (Subversion, CVS, PVS, VSS apod.), Jako perlička, zažil jsem v jedné firmě situaci, kdy nad jedním celkem kódu pracovalo více než 15 vývojářů současně! Dovedete si představit, co se dělo v nástroji pro řízení verzí a jak vypadala tak zvaná „konečná kompilace“ prováděná jednou týdně jako
http://www.objects.cz strana 2
svátek (což však moc velkým svátkem nebylo, protože většinou kompletace a kompilace dopadla špatně). 2. Každé, tedy i malé změny kódu se týkají stále jednoho a téhož velkého kusiska kódu. Z toho důvodu je toto kusisko stále vývojově otevřeno a nikdy není alespoň dočasně uzavřeno. Jinak je tomu u systému rozděleného na menší části systému. Tam by se každá změna týkala pouze dané omezené části a nikoliv ostatních částí systému. Molochální systém se také obtížně testuje, protože nikdy není žádná část dokončena aspoň tak, aby se mohla prohlásit za „jakž takž uzavřenou“, konečně zkompilovanou a testovatelnou jako ohraničená a stabilní část systému. Proto je zájmem i vedoucího projektu, aby se při návrhu systému vzor Modulární nůžky důsledně a pečlivě aplikoval. Tento článek vznikl na základě zkušeností z nedávné praxe a pojednává o doporučeních při používání tohoto vzoru.
VZOR MODULÁRNÍ NŮŽKY
Vzor Modulární nůžky není příliš složitý svou myšlenkou, ale mnohem hůře se aplikuje v praxi. Jeho použití vyžaduje dosti dobrý přehled o třídách v systému, což umožňuje pouze modelování systému pomocí UML, zde specificky pomocí diagramu tříd. Z dlouholetých zkušeností a z praktických konzultací mohu konstatovat, že aplikace tohoto vzoru bez použití modelování tříd pomocí UML je poměrně pracnou záležitostí. Dokonce se dá tvrdit, že nasazení tohoto vzoru přímo z kódu je prakticky neschůdným procesem.
O CO JDE VE VZORU MODULÁRNÍ NŮŽKY
Základní myšlenka je jednoduchá a lze ji formulovat jako následující matematickou úlohu: Díky vztahům mezi třídami v modelu tříd IS vznikají sekundární vztahy závislosti (tj. odvozené Dependency), které se sice nemalují, ale jsou v modelu zřetelně viditelné. Například pokud prozkoumáme vztah „Auto má Barvu“ (slangově nazývaná číselníková vazba):
strana 2
http://www.objects.cz strana 3
Auto
+mB
Barv a
1
Obrázek 1 Čiselníková vazba Auto má Barvu Zřetelně vidíme, že díky tomuto vztahu třída Auto „potřebuje“ třídu Barva (a nikoliv naopak). Pokud tedy naprogramujeme systém, lze část systému obsluhující Barvy naprogramovat zvlášť, tedy jako „odseknutou“ od části systému obsluhující instance Aut, přičemž část systému Auta potřebuje část systému obsluhující Barvy. Zadání úlohy u vzoru Modulární nůžky zní takto: Na základě směrových vztahů mezi třídami je třeba rozdělit třídy do prvků typu Package tak, aby nedocházelo k cirkulárním vztahům závislosti mezi jednotlivými prvky typu Package. Na předešlém obrázku je patrný možný střih systému takto:
Obrázek 2 Prvky typu Package a jejich závislost odvozená z předešlého vztahu
Jako příklad necirkulárního rozdělení tříd do prvků typu Package si představme rozložení tříd, které vzniklo jako výsledek vývojových prací takto:
strana 3
http://www.objects.cz strana 4
C
A
B
D
Obrázek 3 Necirkulární rozložení tříd do prvků typu Package
Všimněme si, že v tomto případě je zřejmé, jak vypadají vrstvy: Nejhlubší, tedy nejcentrálnější vrstvu, zde reprezentuje prvek typu Package s názvem A, nad ním jsou dva prvky B a C jako vyšší vrstva. Jako satelitní vrstva se jeví skupina tříd v prvku typu Package s názvem D. Ale všimněte si, jak by se situace u předešlého obrázku změnila, kdyby se přišlo na to, že v prvku D existuje třída, která je závislá na nějaké třídě v prvku A. Díky tomu by vznikla nová závislost takto:
strana 4
http://www.objects.cz strana 5
C
A
B
D
Obrázek 4 Objevení dalšího vztahu závislosti výrazně mění situaci z pohledu vrstev
Z pohledu na vrstvy se jedná o úplně jinou situaci! Zřetelně již nyní vidíme pouze dvě vrstvy: Vnitřnější jako A + B + D a více vnější C. Závěr: Necirkulární rozložení tříd do prvků typu Package vytváří velmi transparentní pohled na vrstvy tříd v systému a je základem pro dobré rozdělení systému na menší části.
ZÁSADY A DOPORUČENÍ PŘI NASAZENÍ VZORU MODULÁRNÍ NŮŽKY
Pro dobré zvládnutí střihu u aplikace vzoru Modulárních nůžek je třeba dobře znát možné vztahy mezi třídami a z těch je nutné vycházet. Tyto vztahy přímo implikují vztahy závislostí mezi třídami. Poznámka: Mimochodem návrhem vztahů mezi třídami je věnován na našich školeních jeden a půl dne včetně velkého množství příkladů z praxe. Pro účely našeho článku stačí zdůraznit některé důležité zásady postupu střihu u vzoru Modulární nůžky.
strana 5
http://www.objects.cz strana 6
Vyjmenujme si hlavní zásady pro zavedení dobrého střihu systému pomocí vzoru Modulární nůžky:
1. DODRŽENÍ SMĚROVOSTI U VZTAHU GENERALIZACE
U vztahu typu Generalizace (v OOP dědičnost) musí být dodržen vztah závislosti od potomka k předkovi a nikoliv naopak, a to až po realizaci do samotného kódu. Znamená to, že potomek v Generalizaci na jedné straně používá předka, ale naopak předek nesmí v žádném případě nic „vědět“ o potomkovi. Musím podotknout, že jsem se už v praxi několikrát setkal se zašpiněním tohoto evidentně směrového vztahu, kdy se do třídy předka umístila funkcionalita, která používala něco z kódu potomka. Klasickým příkladem je umístění vzoru Factory (tj. výběr a spuštění konstruktoru podle dohodnutého kódu třídy, tedy převodník „kód třídy versus volání konstruktoru“) do statické metody předka. Tím pádem předek nežádoucně obsahuje konstruktory potomků a směr vazby závislosti je nesprávně zaveden i obráceně.
2. POUŽÍVÁNÍ SMĚROVOSTI U VZTAHU TYPU ASOCIACE
U vztahů typu Asociace musí být zásadně používána směrovost konců Asociace (tzv. navigability), tedy musí být udán směr „kdo koho ve vztahu Asociace používá“. K tomu účelu se dá využít v EA směr vazby Diraction, tedy „šipkovitost“ (v EA Diraction mezi Source – Destination). Platí určitá dohoda (mimo syntaxi UML), že neoznačení směru vazby šipkou znamená obousměrnost vazby a tedy že dvojí šipka se nepoužívá. Tuto vlastnost směrovosti použití v Asociaci mnohdy autoři diagramu tříd v UML bohužel vynechávají. Z praxe jsem vypozoroval, že se většinou jedná o návrháře tzv. „dataře“, kteří mají bohaté zkušenosti s relační databází, což je vysvětlením jejich postupu. Úskalím relační databáze je pro technologa ta skutečnost, že povaha vazby je v relační databázi automaticky symetrická, protože je dána rovnicí a shodou hodnot ve sloupcích („where něco rovná se něco“). Tím pádem relační databáze až tak nezdůrazňuje směr vazby. Oproti tomu „objektoví“ návrháři, tj. ti, kteří mají bohaté zkušenosti s OOP, nemají se směrovostí problémy, protože v objektech je směrovost vazby dobře viditelná. Například pokud objekt A používá objekt B přes interface, tak z toho vůbec neplyne, že objekt B používá objekt A.
strana 6
http://www.objects.cz strana 7
3. NASAZENÍ VZORU OBSERVER (RESP. LISTENER) ANEBO JINÝ ZPŮSOB VYVOLÁNÍ A ZPRACOVÁNÍ UDÁLOSTÍ
Postup střihu systému pomocí vzoru Modulární nůžky sice nevypadá nikterak složitě, ale je třeba vědět, že bez nasazení vzoru OBSERVER jej nelze s úspěchem použít. Poznámka: V Javě se tento vzor nazývá LISTENER a v C# jej reprezentuje mechanismus použití delegátů. Vzor OBSERVER umožňuje řešit situaci, kdy prvek z jedné třídy, například Auto, sice používá jednosměrně druhý prvek z jiné třídy, například Barva (mimochodem tím je jasný směr použití od Auta k Barvě), ale prvek, který používá (zde Auto), musí zareagovat na změnu stavu u prvku, který je použit (zde Barva). V tomto případě je nežádoucí, aby se na straně systému prvku používaného (Barva) zavolala a oslovila přímo některá z funkcionalit na straně prvku, který používá (Auto), protože by tak došlo k nežádoucí zpětné referenci a závislosti „zpět“. Jako příklad bych uvedl klasický případ u přecenění Zboží. Nechť nějaká část systému, například Objednávky, používá část systému Zboží. Směr vazby je zřejmý, od Objednávek do Zboží. Dále nechť nastane situace, kdy dojde k přecenění Zboží a je třeba ihned v té chvíli spustit nějakou funkcionalitu na straně systému Objednávky. V tomto případě není žádoucí, aby se tato funkcionalita, která obsluhuje Objednávky, zavolala „napřímo“. Použije se vzor OBSERVER, který se dá technologicky realizovat různými způsoby (klasicky jako polymorfismus v OOP, pomocí delegátů v C#, pomocí ukazatelů na funkce v C++, použitím Message Queue Serveru v třívrstvové architektuře aj.) Dojde tak k odstranění nežádoucí funkcionální zpětné vazby.
4. NASAZENÍ CHYBĚJÍCÍ ASOCIAČNÍ TŘÍDY TAM, KDE TŘEBA
Čistě technicky vzato, každou vazbu asociace můžeme v konečném důsledku nahradit použitím asociační třídy, která původní dva prvky také propojí a současně z hlediska vztahu závislosti od sebe oba prvky odstíní. Ne vždy je však tento krok „zasunout asociační třídu“ žádoucí. Nemá například smysl rozdělovat vztah „faktura má řádky faktury“, protože se jedná o evidentní vztah kompozice, a není proto třeba oba prvky od sebe oddělit, budou vždy žít spolu. Technicky vzato však lze od sebe oba prvky oddělit vsunutím asociační třídy
strana 7
http://www.objects.cz strana 8
s názvem „příslušnost řádku faktury k faktuře“. Na toto pravidlo si vzpomeneme, pokud je žádoucí a potřebujeme od sebe oddělit dvě části systému a některá z asociací tomu brání.
5. DODRŽOVÁNÍ ČISTOTY POJMŮ
S tímto požadavkem bývají nejčastější problémy. Pokud se totiž nedodržují zásady návrhu IS a nepoužívá se návrh IS pomocí modelování v UML tím správným způsobem, vznikají nepříjemné problémy typu „kam vlastně umístit danou informaci“. Upozorňuji, že se nejedná o problém technologický (tj. problém do jaké tabulky umístíme uvažovaný sloupec), ale o problém na analytické úrovni. Logicky vzato je zřejmé, že chybné umístění informace do nesprávné pozice automaticky vede k problémům střihu u Modulárních nůžek. Pokud totiž danou informaci potřebujeme a je umístěna chybně, musíme pro ni jít někam, kam jsme vůbec původně chodit nemuseli. Důsledkem neustálého opakování tohoto nesprávného umístění informace je to, že nakonec na úrovni modulů dojde k zašmodrchanému provázání „všeho se vším“. Mimochodem to je důvod, proč mi vždy přeběhne mráz po zádech, když slyším argument „přece není problém přidat sloupec“. Anebo podobně, smutný je pohled do databáze, když se zjistí, že sloupce vznikaly metodou „padni kam padni“. Důsledkem nedodržení čistoty pojmů je samozřejmě bez vulgarit řečeno „nepořádek v systému s následnou vysokou entropií“. Jedná se vlastně o nedodržování opětovné použitelnosti (reuse) na úrovni modulů, což vede nakonec ke špatné a vůbec nesmyslné architektuře komponent.
6. ZNALOST PROBLÉMU U „DETAILU FIRMY“
S předešlým problémem čistoty pojmů v IS souvisí také další doporučení, které si probereme v dalším pokračování článku příště.
Pobytový kurz OOP a UML podzim 2009 má ještě volná místa! Na cenově výhodná školení OOP a UML v Praze na podzim 2009 se hlásí první účastníci, neváhejte s registrací!
strana 8