Používanie obojsmerných závislostí v objektovom modeli je jednou z príčin vzniku ťažko udržiavateľného softvéru. Tento článok sa zaoberá tým, kedy obojsmerné závislosti vznikajú, prečo sa im treba vyhýbať a ako.
Kedy vzniká obojsmerná závislosť
Typickým predstaviteľom obojsmernej závislosti medzi dvoma objektami je obojsmerná asociácia:
Uvedený model vychádza z týchto používateľských požiadaviek:
- Na faktúre musí byť zobrazená adresa fyzickej osoby, pre ktorú je faktúra vystavená.
- Systém musí vedieť vrátiť zoznam faktúr, ktoré boli vystavené pre danú fyzickú osobu.
Analytik častokrát v prvom priblížení takýto model zakreslí, aby zachytil reálne vzťahy v doméne. Chybou je, keď sa nad daným modelom hlbšie nezamyslí a pustí takýto model do implementácie.
Prečo sa vyhýbať obojsmernej závislosti
Závislosť triedy Faktúra na triede Fyzická osoba znamená, že vždy, keď sa zmení implementácia triedy Fyzická osoba, môže to mať vplyv na funkčnosť triedy Faktúra. Pri obojsmernej závislosti platí aj veta obrátená, t.j. zmena triedy Faktúra môže mať dopad na triedu Fyzická osoba. Z pohľadu zmien v systéme sú teda triedy Faktúra a Fyzická osoba tá istá „jednotka na zmenu“ – vždy sa menia súčasne.
Čím viac obojsmerných asociácií systém má, tým ťažšie je možné systém modifikovať:
V takto navrhnutom systéme úprava jednej triedy môže viesť ku zmene fungovania hociktorej inej triedy. Napríklad zmena v triede Položka zmluvy môže poškodiť fungovanie triedy Položka faktúry. Používanie obojsmerných asociácií vedie k antipatternu známemu ako „Big Ball of Mud“. Ak je systém dostatočne veľký, akákoľvek zmena v ňom je vysoko riziková, pretože môže mať úplne nečakané dôsledky v zdanlivo vzdialenej a doménovo nesúvisiacej časti systému. Navyše sú takéto vzájomné väzby neintuitívne a žiaden programátor ani analytik s nimi nebude počítať.
Preto je nutné vyhýbať sa používaniu obojsmerných asociácií.
Ako sa vyhnúť obojsmernej závislosti
Obojsmerná závislosť sa síce v používateľských požiadavkách môže objaviť, ale objektový svet ponúka niekoľko techník, ako sa jej zbaviť. Toto sú najčastejšie:
- Otočenie závislostí (Dependency inversion)
- Využitie repozitory (stavebný blok z Domain Driven Design)
- Použitie asociatívnej triedy
Otočenie závislostí (Dependency inversion)
Jednu zo závislostí je možné otočiť. Ak napríklad faktúra potrebuje informácie o fyzickej osobe, je možné zaviesť abstrakciu Subjekt fakturácie. Diagram potom vyzerá takto:
Po tejto zmene už trieda Faktúra nezávisí na triede Fyzická osoba, takže akákoľvek zmena v triede Fyzická osoba neovplyvní triedu Faktúra. Trieda Fyzická osoba musí iba dodržať rozhranie Subjekt fakturácie, čiže vedieť poskytnúť informácie o adrese pre faktúru. Z pôvodnej obojsmernej závislosti teda ostala iba závislosť triedy Fyzická osoba na triede Faktúra. Zmeny v triede Fyzická osoba sa zastavia na rozhraní Subjekt fakturácie.
Využitie repozitory
Požiadavka č. 2 na systém hovorí, že k danej fyzickej osobe musí vedieť systém poskytnúť zoznam faktúr. Požiadavka však nemusí byť implementovaná ako asociácia medzi dvomi triedami, ale ako parameter v repository:
Implementácia rozhrania Faktúra Repository musí poskytnúť metódu na vyhľadanie faktúr na základe fyzickej osoby. V tomto prípade trieda Faktúra síce pozná triedu Fyzická osoba, ale naopak to už neplatí: trieda Fyzická osoba nepotrebuje poznať triedu Faktúra. Zodpovednosť za vrátenie správneho zoznamu faktúr pre danú fyzickú osobu prešla na repository.
Použitie asociatívnej triedy
Pokiaľ v doméne existuje vhodný pojem, ktorý popisuje asociáciu medzi faktúrou a fyzickou osobou, je možné vytvoriť triedu reprezentujúcu asociáciu medzi nimi:K faktúre je nutné pristupovať cez tzv. „Fakturačnú zložku“, ktorá pozná fyzickú osobu uvedenú na faktúre a vie teda do triedy Faktúra poslať informácie o adrese fyzickej osoby. Takže hoci medzi triedami Faktúra a Fyzická osoba existuje nepriamy vzťah, nie sú na sebe vôbec závislé.
Ktorá závislosť je správna?
Z úvah vyplýva, že závislosti je možné otočiť, ba dokonca zbaviť sa ich úplne. Faktúra môže byť závislá na Fyzickej osobe, alebo Fyzická osoba na Faktúre, alebo je možné zaviesť asociatívnu triedu, ktorá závislosti prevezme na seba.
Smer závislosti je vždy potrebné nastaviť tak, aby trieda, ktorá sa bude v doméne meniť častejšie, závisela na triede, ktorá je v doméne dlhodobo stabilná. Správny smer závislosti sa teda bude odvíjať od typu domény:
1 – Závislosť Faktúra -> Fyzická osoba
Ak je implementovanou doménou doména fakturácie, potom bude Fyzická osoba stabilnejším pojmom ako Faktúra. Pojem „stabilita“ sa v tomto význame chápe ako počet požiadaviek, s ktorými bude používateľ prichádzať. V doméne fakturácie budú prichádzať nové požiadavky skôr na triedu Faktúra, než na triedu Fyzická osoba. Princíp stability hovorí, že trieda, ktorá je menej stabilná (Faktúra), má byť závislá na triede, ktorá je stabilnejšia (Fyzická osoba). Preto smer závislosti v doméne fakturácie pôjde od Faktúry k Fyzickej osobe.
2 – Závislosť Fyzická osoba -> Faktúra
V doméne CRM systému (customer relationship management) je kľúčovým pojmom zákazník, resp. fyzická osoba. Dáta o faktúre budú možno prichádzať z nejakého ďalšieho systému (účtovníctvo). Požiadavky na zmenu CRM systému budú smerované viac na zákazníka než na faktúru. Preto je pre doménu CRM vhodné zachovať závislosť fyzickej osoby na faktúre.
3 – Asociatívna trieda
Asociatívna trieda je šikovné riešenie, ktoré by však nemalo ignorovať doménu. Ak v doméne existuje pojem ako „Fakturačná zložka“, treba ho využiť a naplniť jeho zodpovednosť. Ak takýto pojem v doméne nie je, nemal by ho analytik svojvoľne vytvárať. Samozrejme, vždy je vhodné preskúmať, či takýto pojem v doméne náhodou neexistuje v nejakej implicitnej podobe – t.j. používateľ ho síce fyzicky nemá, ale logicky s ním pracuje.
Záver: pozor na závislosti
Tento článok sa venuje obojsmerným asociáciám, ktoré sú najčastejšou formou obojsmernej závislosti. Avšak dve triedy môžu mať na sebe obojsmernú závislosť aj vtedy, keď nemajú obojsmernú asociáciu. Platia však pre ne rovnaké úvahy.
Odkazy na ďalšie zaujímavé čítanie
- Princíp stability
- Dependency inversion
- Interface segregation
- Repository
- Predošlý článok o typoch závislostí