IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

2. diel - Návrhové vzory GRASP - Dokončenie

V dnešnom tutoriále sa budeme zaoberať ďalšími vzormi GRASP pre priradenie zodpovednosti. Budú to napríklad Creator, High cohesion, Indirection a ďalšie.

Zoznam vzorov GRASP

Pripomeňme si vzory GRASP:

  • Controller,
  • Creator,
  • High cohesion,
  • Indirection,
  • Information expert,
  • Low coupling,
  • Polymorphism,
  • Protected variations,
  • Pure fabrication.
Vzor Controller sme prebrali v lekcii Návrhové vzory GRASP. Dnes si prejdeme zostávajúce vzory.

Creator

Vzor Creator rieši, do ktorej triedy by sme mali umiestniť kód na vytvorenie inštancie nejakej inej triedy. Majme triedu A a B. Trieda B inštanciuje triedu A ak:

  • je trieda A jej častí,
  • je trieda A jej závislosťou,
  • má pre inštanciáciu triedy A dostatok informácií,
  • trieda B obsahuje triedu A.
Trieda A je časťou triedy B

Príkladom pre situáciu, keď trieda A je časťou triedy B by mohli byť triedy Faktura a PolozkaFaktury. Jednotlivé inštancie položiek faktúry dáva zmysel vytvárať v triede Faktura, pretože je jej súčasťou. Trieda Faktura tu má za položky zodpovednosť:

Vzor Creator z GRASP - Ostatné návrhové vzory - Ostatné návrhové vzory

Trieda A je závislosťou triedy B

V tomto prípade si trieda B vytvorí triedu A, pokiaľ na ňu závisí. Príkladom by mohla byť napríklad databáza podpisov, ktorej inštanciu si vytvorí trieda Faktura, aby mohla na vygenerovanej faktúre zobraziť podpis:

Vzor Creator z GRASP a závislosti - Ostatné návrhové vzory - Ostatné návrhové vzory

Pokiaľ je daná závislosť použitá ešte inde, je výhodnejšie nevytvárať stále nové inštancie závislosti, ale použiť vzor Dependency Injection. Viac v kurze Dependency injection a softvérovej architektúry.

Trieda B má dostatok informácií pre inštanciáciu triedy A

Tu sa jedná o výber takej triedy B, do ktorej zbytočne nemusíme naťahovať ďalšie dáta, keď sú už k dispozícii niekde inde. Ako príklad si uveďme rozhodovanie, či triedu SeznamFaktur s faktúrami zákazníka inštanciujeme v triede SpravceFaktur alebo SpravceZakazniku. Pozrieme sa, ktorá z tried má všetky informácie, ktoré SeznamFaktur potrebuje. Pokiaľ tu budeme potrebovať napríklad všetky faktúry az nich vybrať faktúry od určitého zákazníka, inštanciujeme SeznamFaktur v SpravceFaktur, pretože v ňom sa faktúry nachádzajú:

Vzor Creator z GRASP a informácie - Ostatné návrhové vzory - Ostatné návrhové vzory

Trieda B obsahuje triedu A

Ak je trieda A vnorená v triede B, mala by byť aj inštancia triedy A vytváraná triedou B. Avšak vnorené triedy sa nestali príliš populárnymi.

UML diagram tejto situácie vyzerá nasledovne:

Vzor Creator z GRASP a vnorená trieda - Ostatné návrhové vzory - Ostatné návrhové vzory

High cohesion

Vysoká súdržnosť znamená, že sa naša aplikácia skladá z rozumne veľkých kusov kódu. Každý tento kód sa zameriava na jednu vec. To je aj jeden zo základných princípov samotného OOP. Vysoká súdržnosť úzko súvisí s nízkou previazanosťou (pozri ďalej). Keď združujeme súvisiaci kód na jedno miesto, znižuje sa nutnosť väzieb do ďalších častí aplikácie. Ďalším súvisiacim vzorom je Law of Deméter, ktorý v podstate hovorí, že objekt by nemal "hovoriť" s cudzími objektmi.

Príkladom vysokej súdržnosti je napríklad sústredenie funkcionality okolo užívateľov do triedy SpravceUzivatelu. Keď by sa prihlásenie užívateľa riešilo napríklad v triede SpravceFaktur, kde je prihlásenie potrebné pre zobrazenie faktúr, a zrušenie užívateľského účtu by sa riešilo v triede Uklizec, ktorá premazáva neaktívne účty, porušovali by sme High cohesion (viď ďalej). Kód, ktorý má byť spolu v triede SpravceUzivatelu, by bol rozhádzaný rôzne po aplikácii, podľa toho, kde je práve potrebný. Preto združujeme súvisiaci kód na jedno miesto, a to aj keď sa tieto metódy používajú v aplikácii hoci len raz.

Indirection

Indirection je veľmi zaujímavý princíp, s ktorým sme sa už stretli pri controlleri. Hovorí, že keď vytvoríme niekde v aplikácii umelého prostredníka, teda triedu „navyše“, môže našu aplikáciu paradoxne výrazne zjednodušiť. U kontroléra jasne vidíme, že zníži počet väzieb medzi objektmi. Za cenu pár riadkov kódu navyše podporíme znovupoužiteľnosť a lepšiu čitateľnosť kódu. Indirection je jeden zo spôsobov, ako dosiahnuť Low coupling (pozri ďalej). Príklad sme si už ukazovali pri vzore Controller.

Information expert

Informačný expert je ďalšia poučka, ktorá nám pomáha rozhodnúť sa do akej triedy pridáme metódu, atribút a podobne. Zodpovednosť má vždy tá trieda, ktorá má najviac informácií. Takej triede potom hovoríme informačný expert a práve do nej pridávame ďalšiu funkcionalitu a dáta. O podobnom princípe sme už hovorili pri vzore Creator v tejto lekcii.

Low coupling

Low coupling je v podstate to isté ako High cohesion, ale z iného pohľadu. V aplikácii by sme mali vytvárať čo najmenší počet väzieb medzi objektmi, čo dosiahneme šikovným rozdelením zodpovednosti.

Ako odstrašujúci príklad si uveďme triedu Manager, v ktorej je umiestnená logika pre prácu so zákazníkmi, s faktúrami, s logistikou, skrátka so všetkým. Takýmto objektom sa niekedy hovorí "božské" (god objects), pretože majú príliš veľkú zodpovednosť a tým pádom vytvárajú príliš veľa väzieb (takový Manager bude typicky používať veľké množstvo tried, aby mohol fungovať takto všeobecne). Asi vás už napadlo, že takýto manažér by pravdepodobne nešlo znovu použiť v inej aplikácii. V aplikácii nie je dôležitý celkový počet väzieb, ale počet väzieb medzi dvoma objektmi. Vždy sa snažíme, aby trieda komunikovala s čo najmenším počtom ďalších tried, preto by sme mali uviesť triedy SpravceUzivatelu, SpravceFaktur, SpravceLogistiky a podobne.

Tu je diagram UML:

Low coupling vzor z GRASP - Ostatné návrhové vzory - Ostatné návrhové vzory

Odstrašujúci príklad božského objektu pri nedodržiavaní Low coupling

A nemusíme zostávať len pri triedach. Low coupling súvisí tiež napríklad s ďalšími praktikami ohľadom pomenovávania metód. Metódu by sme mali pomenovávať čo najmenej slovami a bez spojky „a“. Metódy delej() alebo naparsujAZpracujAVypis() signalizujú, že toho robia príliš.

Keď sme už spomenuli božské objekty, uveďme si aj opačný problém, ktorý je takzvaný Yoyo problém (problém joja). Pri príliš drobnej štruktúre programu, príliš vysokej granularite, často aj nadužívaní dedičnosti, je v programe toľko tried, že programátor sa musí stále prepínať dovnútra nejakej triedy, zistiť ako pracuje a vrátiť sa späť. Táto akcia môže pripomínať vrhanie joja dole a hore, znova a znova. Pred dedičnosťou sa preto často preferuje skladanie objektov.

Čo sa týka väzieb medzi objektmi, mali by sme sa tiež vyvarovať cyklickým väzbám, ktoré sú všeobecne považované za zlú praktiku. To sú prípady, keď trieda A odkazuje na triedu B a tá odkazuje späť na triedu A. Tu je niekde v návrhu niečo zle. Cyklická väzba môže byť aj napriek viacerým triedam.

Polymorphism

Áno, aj polymorfizmus je návrhovým vzorom. Aj keď je nám v princípe polymorfizmus dobre známy, zopakujme pre úplnosť, že sa jedná najčastejšie o prípad, keď potomok upravuje funkcionalitu svojho predka, ale zachováva jeho rozhranie.

Z programátorského hľadiska sa jedná o prepisovanie (override) metód predka. S objektmi potom môžeme pracovať pomocou všeobecného rozhrania, ale každý objekt si funkcionalitu zdedenú od predka upravuje po svojom. Polymorfizmus nemusí byť obmedzený len na dedičnosť, ale vo všeobecnosti na prácu s objektmi rôznych typov pomocou nejakého spoločného rozhrania, ktoré implementujú. Ukážme si povestný príklad so zvieratami.
Každé zviera má metódu mluv(), ale prepisuje si ju od predka Zvire, aby vydávalo špecifický zvuk:

Zvieracie zvuky ako ukážka polymorfizmu v OOP - Ostatné návrhové vzory - Ostatné návrhové vzory

Ďalšou ukážkou sa ponúka napríklad predok pre formulárové ovládacie prvky. Každý prvok potom prepisuje metódy predka ako vykresli(), vratVelikost() a podobne podľa toho, ako konkrétne potomkovia fungujú:

Vzor Polymorphism z GRASP  - Ostatné návrhové vzory - Ostatné návrhové vzory

Protected variations

Protected variations by sme mohli preložiť ako chránené zmeny. Praktika hovorí o vytvorení stabilného rozhrania na kľúčových miestach aplikácie, kde by zmena rozhrania spôsobila nutnosť prepísať väčšiu časť aplikácie.

Uveďme si opäť reálny príklad. V systéme ITnetwork používame princíp Protected variations, konkrétne pomocou návrhového vzoru Adapter. Tým sa bránime proti zmenám, ktoré neustále vykonáva Facebook vo svojom API. Prihlasovanie cez Facebook a podobné ďalšie integrácie majú za následok zvýšenie počtu a aktivity užívateľov, bohužiaľ však za cenu prepisovania aplikácie každých niekoľko mesiacov. Pomocou rozhrania FacebookManagerInterface sa systém už nemusí nikdy meniť. Keď vyjde nová verzia, kedy Facebook zas všetko prerobí, iba sa toto rozhranie implementuje v inej triede, napríklad FacebookManagerXX, kde XX je verzia Facebook API. V systéme sa potom zmení inštancia, ktorá toto rozhranie implementuje. Rozhranie je samozrejme možné definovať aj pomocou polymorfizmu a abstraktnej triedy:

Vzor Protected variations z GRASP - Ostatné návrhové vzory - Ostatné návrhové vzory

Pure fabrication

O Pure fabrication sme už dnes tiež hovorili. Voľne preložené ako "čistý výmysel" sa jedná práve o triedy, ktoré slúžia iba pre zjednodušenie systému z hľadiska návrhu. Tak ako bol controller prípad indirection, tak je indirection prípadom Pure fabrication.

Servisné triedy mimo funkcionalitu aplikácie znižujú závislosti a zvyšujú súdržnosť.


 

Všetky články v sekcii
Ostatné návrhové vzory
Článok pre vás napísal David Hartinger
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David sa informačné technológie naučil na Unicorn University - prestížnej súkromnej vysokej škole IT a ekonómie.
Aktivity