1. Zapouzdření Cíl látky Tento blok nejdříve přiblíží zásadu zapouzdření a odpoutání kódu a po té na relacích, jako jsou asociace, agregace a kompozice, vysvětlí jak lze objektový zdrojový kód zapouzdřovat a odpoutávat od ostatních částí programu.
1.1. Zásada „Zapouzdření“ 1.1.1. Co je to dobře zapouzdřený kód Dobře zapouzdřený kód nám umožní se snáze odpoutat od zbytku kódu. Tím dosáhneme toho, že zapouzdřený kód můžeme libovolně měnit aniž bychom ovlivnili funkčnost spolupracujících programů.
1.1.2. Co je to odpoutání Odpoutáním se myslí odstranění přímých vazeb. Například tím, že se nebudeme obracet přímo na instance dané třídy, ale na rozhraní, které daná třída implementuje.
1.1.3. K čemu je to dobré Například k tomu, že se může snadno vyměnit konkrétní třída za jinou konkrétní třídu bez ovlivnění ostatního kódu. Tím lze efektivně měnit jednu implementaci za jinou. Například si zákazník objedná nějakou jednoduchou funkci. Po prvních zkušenostech z programem si uvědomí, že potřebuje tuto funkci rozšířit. Takže stačí vyměnit původní řešení za nové, přičemž, když byl tento kód dobře odpoután, není potřeba měnit ostatní části programu. Dalším příkladem může být například výstup dat na různá zařízení, souborů, grafických rozhraní, sítě atd. Zde je dobré upozornit na návrhové vzory, protože většina návrhových vzorů poskytuje návody jak správně zapouzdřit ty části kódu, které jsou klíčové pro zakomponování očekávaných změn.
1.2. Relace „Asociace“ 1.2.1. Co je to asociace Asociace je základní vazba mezi třídami. Je to předpis, jak budoucí objekty mohou mezi sebou navazovat spojení. Každá asociace může být upřesněna dalšími specifikacemi jako je název asociace, názvy rolí, násobnost a směrovostí (neboli řiditelností). Asociace se v diagramech UML znázorňuje čarou mezi třídami. Čára může být opatřena šipkami, které signalizují řiditelnost. Třída, u které je na konci čáry asociace šipka, je řízena druhou třídou od které vychází čára asociace. 1
Asociaci lze znázorňovat dvojím, vzájemně vylučujícím způsobem. První způsob pouze pojmenováva asociaci, kdy lze například číst podle diagramu 1.1 takto: „Jedna firma zaměstnává osoby“. Druhý způsob pojmenovává role, které mají třídy ve vzájemném vztahu, kdy lze z diagramu ??? číst toto „Osoba má zaměstnavatele“ nebo „Firma má zaměstnance“.
Obrázek 1.1. Pojmenovaná asociace
Obrázek 1.2. Asociace s pojmenovanými rolemi Násobnost uvedená na konci čáry asociace říká kolik spojení lze vytvořit z dané asociace. Právě nula v násobnosti signalizuje, že se jedná o asociaci. Protože tato nula říká, že numusí být spojení navázáno se žádným objektem. Pokud je uveden rozsah 0..*, tak tím se myslí, že lze za běhu progreamu nenavázat žádné spojení nebo až nekonečno. Pokud se místo hvězdičky uvede konkrétní číslo, bude možné navázat jenom tolik spojení, které odpovídá číslené hodnotě. Pro implementaci omezený rozsah většinou je návodem, že referenční hodnoty na připojené objekty budou umístněny v poli.
1.2.2. Implementace asociace Jednoduchou asociaci implementujeme jako referenční proměnnou v klientovi (m_Ucet). Spojení mezi objekty vznikne až v okamžiku nastavení referenční proměnné při běhu programu. Kdykoliv tuto referenční proměnnou můžeme nahradit jinou referencí a tím se přepnout na jiný objekt.
Obrázek 1.3. Diagram „návrhu asociace“ 2
Výpis 1.1. Implementace asociace public class Bankomat { private Ucet mUcet; public Bankomat(){ mUcet= null; } public void nastavUcet(Ucet ucet){ mUcet= ucet; } public double vyberPenize(double castka){ if (mUcet != null) if (mUcet.vyber(castka)) return castka; return 0.0; } public double dejZustatek(){ if (mUcet != null) return mUcet.getZustatek(); else return 0.0; } } Atribut mUcet pro uložení reference na instanci třídy typu Ucet Konstruktor třídy Bankomat se při vytváření instance neví s jakým objektem bude spolupracovat, proto je reference v atributu mUcet konstruktorem nastavena na null. Poznámka: V Javě je to nadbytečný příkaz, v jiných programovacích jazycích to však, tak nemusí platit a je nutné ukazatele na neexistujicí objekt explicitně nastavit na null. Teprve zavoláním metody nastavUcet() se vytvoří spojení na objekt typu Ucet. Tím se realizuje implementace asociační relace. Dobře navrhnuté metody, které pracují s asociační vazbou, mají v sobě kontroly, zda je již navázáno spojení s asociovaným objektem. Nelze se spolehnout, že klient tento kontrakt dodrží. Kontraktem je, že před metodami vyberPenize() nebo dejZustatek() je zavolána metoda nastavUcet().
1.2.3. Ukázka použití asociovaných objektů Na následujícím obrázku je sekvečním diagramem ukázáno, jak lze využít asociační vazbu.
3
Obrázek 1.4. Sekvenční diagram instancí asociovaných tříd
1.2.4. Jak asociace plní zapouzdření Tak, jak je asociace použita v předchozích příkladech, tak zásadu zapouzdření příliš neplní. Proč: 1. Odkrývá implementaci spojení na konkrétní třídu Ucet. Lepším řešením by bylo, kdyby se v metodě nastavUcet() použil nějaky datový typ v podobě rozhraní nebo abstraktní třídy. 2. Odpoutání kódu tíd Bankomat a Ucet je minimální, takže při změně metod v třídě Ucet hrozí, že se bude muset opravit též kód třídy Bankomat. 3. U asociace se přímo předpokládá, že reference na jeden objekt může sdílet, a tím s ním manipulovat, více klientů.
1.3. Relace „Agregace“ 1.3.1. Co je to agregace Agregace je vazbou mezi celkem a jeho součástí, kdy celek využívá služby jiných objektů tj. součástí, přičemž tyto součásti mohou být existovat i když celek zanikne. Dále může dojít ke sdílení součástí i jinými objekty, pak se jedná o sdílenou agregaci. Příkladem první varianty může být vztah počítače a tiskárny. Tiskárna může s počítačem tvořit jeden celek, ale při poruše počítače, lze tiskárnu připojit k jinému počítači. 4
Příkladem druhé varianty, tj. sdílené agregace, může být počítač, který umožní sdílet připojenou tiskárnu i jiným počítačům přes síťové připojení. Na diagramu 1.5 je agregace znázorněna čarou, která začíná u třídy, která je celkem, kosočtvercem a končí u třídy, která je součastí celku, šipkou. Specifikace relace agregace je stejná jako u asociace, tj. název agregace nebo názvy rolí a též multiplicita. Diagram 1.5 lze číst takto: „Počítač má jednu tiskárnu“.
Obrázek 1.5. Agregace v UML
1.3.2. Implementace agregace Implementace agreace je ve většině programovacích jazyků stejná jako implementace asociace. V podstatě jde o to, že třída celku má v sobě kód, který vytvoří instance všech součástí. Programovací jazyky nemají pro implementaci agregace žádný speciální příkaz. Proto je implementace agregace vyloženě záležitostí programátorské techniky a je tedy plně v rukách programátora. Diagram 1.6 je rozšířením příkladu implementace asociace z přechozí kapitoly. Bankomat musí nejdříve požádat instanci Banka o referenci na instanci Ucet a po té teprve může Bankomat odečíst požadovanou částku z instance Ucet.
Obrázek 1.6. Diagram tříd s ukázkou sdílené agregace 5
Podle diagramu 1.6 je na výpisu kódu 1.2 ukázáno, jak by taková implementace sdílené agregace mohla vypadat v programovacím jazyku Java. Výpis 1.2. Příklad implementace agregace v Java public class Banka { private Ucet mUcet; public Banka(){ ... } public void vytvorUcet(int cislo){ mUcet= new Ucet(cislo); } public Ucet dejUcet(){ return mUcet; } public void zrusUcet(){ mUcet= null; } ... } Třída Banka, jako celek, si vytváří (agreguje) svoji součást, to je instanci třídy Ucet. Metoda dejUcet() poskytuje referenci na svoji součást jinému objektu. Metoda zrusUcet() odstraní referenci instanci třídy Ucet, ale tento objekt nemusí zaniknout, pokud nějaký jiný objekt si tuto referenci nevyzvedl a drží ji. Tato metoda není dobrý nápad a ve správně navržené třídě by vůbec neměla být.
1.3.3. Ukázka práce agregovaných objektů Na diagramu 1.7je znázorněna jedna z možných sekvencí řazení metod volání objektů při sdílené agregací. Na diagramu 1.8 je zachyceno totéž, jenomže v komunikačním diagramu.
6
Obrázek 1.7. Sekvenční diagram Komunikační diagram 1.8 znázorňuje objekty a jejich propojení jako síť. Po spojeních, což jsou čáry spojující objekty, putují zprávy, které jsou na diagramech znázorněny jako malé šipky, ke kterým jsou přiřazeny názvy metod.
7
Obrázek 1.8. Komunikační diagram
1.3.4. Jak agregace plní zapouzdření Agregace plní daleko lépe zásadu zapouzdření, ale vzápětí ji porušuje ve sdílené variantě tím, že zveřejňuje vnitřní propojení na své součásti.
1.4. Relace „Kompozice“ 1.4.1. Co je to kompozice Nejsilnější formou relace mezi třídami je kompozice. V rámci kompozice je celek se složen ze svých součástí a při zániku celku zanikají i tyto součásti. Součásti kompozice tedy nemohou existovat samostatně. Na diagramu 1.9 je kompozice znázorněna čarou, která začíná u třídy, která je celkem, vyplněným kosočtvercem a končí u třídy, která je součastí celku, šipkou. Specifikace relace kompozice je stejná jako u agregace a asociace, tj. název kompozice nebo názvy rolí a též multiplicita. Diagram 1.9 lze číst takto: „Faktura má jednu nebo více položek“ .
Obrázek 1.9. Kompozice v UML 8
1.4.2. Implementace kompozice K implementaci 1.10 kompozice se dá říct to samé jako u agregace. Na implementaci kompozice programovací jazyky nemají žádný speciální příkaz a proto jeji realizace je závislá pouze na programátorovi, respektive na autorovi návrhu. Na diagramu je znázorněn kompoziční vztah mezi třídou Banka a třídou Ucet. O proti obdobným diagramům asociace nebo sdílené agregace je patrné, že žádná z metod neumožňuje přímý přístup k referenci na instanci třídy Ucet. Metody třídy Banka vloz(), vyber() a dejZustatek() zprostředkovávají operace změn stavu instance (objektu) třídy Ucet.
Obrázek 1.10. Diagram na kompozici tříd Banka a Ucet Z důvodu vysvětlení principu kompozice je násobnost instance Uctu pouze jedna, i když je jasné, že by měla být jedna až n.
1.4.3. Ukázka použití kompozice Na sekvenčním diagramu 1.11 je znázorněno, že všechny operace s objektem účtu U1 se provádějí přes objekt banky KB. Objekt banky si nejdříve vytvoří při svém vlastním vytvoření objekt účtu U1. Při zániku objektu banky KB dojde též ke zániku objektu učtu U1, což je na diagramu znázorněno křížky na konci životních drah těchto objektů.
9
Obrázek 1.11. Chování objektu v kompozici
1.4.4. Jak kompozice plní zapouzdření Kompozice ze všech relací nejlépe plní zásadu zapouzdření. Důvod je jasný. Klient nic neví o tom jaká je vnitřní implementace kompozice.
10