Často součást reálného světa reprezentujeme několika údaji různých typů. Tato abstrakce má obecný název záznam a říkáme, že jednotlivá data uchováváme v položkách záznamu.
Příkladem může být záznam o studentovi univerzity, který může obsahovat položky
jméno
příjmení
pohlaví
datum_narození
rodné_číslo
osobní číslo
...
Tímto způsobem je zřejmě vyjádřena množina hodnot, které může záznam o studentovi nabývat, jako kartézský součin množin hodnot datových typů jednotlivých položek záznamu a jde tedy o definici nového datového typu. Programovací jazyky jako Pascal a C umožňují definování datových typů záznam a jejich pojmenování. Následně je možné deklarování proměnných těchto typů.
V Javě abstrakci záznamu reprezentujeme pomocí objektů. Položky záznamu jsou reprezentovány členskými proměnnými.
Jako příklad uvedeme velice zjednodušený příklad členů rodiny, kterých záznam bude obsahovat jméno a pohlaví, co umožní vyjádřit jde-li o bratra, sestru, strýce, tetu, ap.
Ať některý z členů rodiny udržuje záznam o sobě a záznamy o svých sourozencích. Tyto informace by mohly sloužit jako výchozí data například pro aplikaci, která použitím kalendáře zjistí, má-li některý z jeho sourozenců v daném dnu svátek a automaticky vygeneruje a odešle blahopřání, například:
Milá sestro/bratře
k Tvému svátku ...
Tvá sestra/ Tvůj bratr xxx.
V našem příkladě třída Sourozenec na obr. bude reprezentovat záznam o sourozenci s dvěma členskými proměnnými, položkami záznamu, nazvané jmeno a pohlavi, konstruktorem Sourozenec( ) a metodou tiskSourozence( ), která vytiskne je-li sourozenec bratr nebo sestra a jeho nebo její jméno. Vidíme, že ani pro tisk nebudeme přistupovat k členským datům přímo požitím tečkového zápisu, ale třída bude pro tento účel obsahovat metodu, což je doporučený způsob.
Dále potřebujeme uchovávat záznamy o všech sourozencích. Dosud známá datová struktura uchovávající více datových jednotek stejného datového typu je pole.
Ve třídě Sourozenci na obr. je v prvku pole sourozenci s indexem 0 uložen pomocí konstruktoru Sourozenci( ) záznam o tom členovi rodiny, který si uchovává záznamy o svých sourozencích. Zmíněná aplikace by v tomto záznamu našla informaci, jak podepsat uvedené blahopřání. Metoda dalsiSourozenec( ) bude do dalších prvků pole sourozenci postupně ukládat jeho sourozence, pokud nějaké má.
V klientském programu MojiSourozenci na obr. je pro sourozence, který bude uchovávat záznamy o svých sourozencích, vytvořen objekt ja a dále objekt mojiSourozenci třídy Sourozenci použitím konstruktoru Sourozenci ( ) s argumentem ja. Vzniknutá datová struktura je znázorněna na obr.
. Dále jsou v programu MojiSourozenci (obr.
) vytvořeny záznamy o dvou sourozencích a pomocí metody dalsiSourozenec( ) vlozeny do objektu mojiSourozenci. Vytvořená datová struktura je na obr.
. Poznamenejme, že častěji pro vytvoření dvou nebo více sourozenců, použijeme jednu referenční proměnnou třidy Sourozenec. V našem případě, by potom první sourozenec byl přístupný pouze jako prvek pole sourozenci[1].
Klientský program MojiSourozenci pracuje s datovými členy objektů tříd Sourozenec a Sourozenci pomocí jejích členských metod. Tato koncepce umožňuje vykonání klientského programu bez jakékoliv změny, pokud budou k dispozici třídy, které implementují stejné metody.
Schéma této koncepce vychází z následující abstrakce:
rozhraní definuje poskytované metody
implementace metod rozhraní
klientský program používá metody rozhraní na práci na vyšší abstraktní úrovni
Uvedená iplementace třídy Sourozenci pro uložení sourozenců používala datovou strukturu pole. Je-li pole vytvořeno, jeho velikost se v Javě a ve většině programovacích jazyků nemůže změnit. Protože dost dobře nemusíme vědět kolik budeme mít sourozenců, musíme se zamyslet nad jiným způsobem uchovávání jejich dat.
Pro problémy kde neumíme dopředu odhadnout počet ukládaných datových jednotek, protože tyto dynamicky vznikají nebo zanikají, se používá mechanizmus jejich spojování a tyto datové struktury se nazývají spojové.
Uchovávaná datová jednotka je rozšířena o položku, nazývanou ukazatel, která ukazuje na další datovou jednotku.
Spojení datových jednotek budeme znázorňovat graficky, tak že tyto jednotky zobrazíme obdélníkem s jednotlivými položkami. Je-li položka ukazatelem, vychází z ní šipka k obdélníku, který zobrazuje datovou jednotku, na kterou ukazuje. Pokud položka neukazuje na žádnou datovou jednotku, bude označena úhlopříčkou z levého dolního rohu do pravého horního rohu.
V případě záznamů o sourozencích, spojová struktura zobrazující vytvoření záznamů o sourozencích bude mít tvar jak je znázorněno na obr. , který nazýváme lineární spojový seznam.
V Javě můžeme záznamy reprezentovat objekty, které jsou přístupné pomocí odkazů (referencí) uložených v referenčních proměnných. Spojové struktury pak můžeme vytvářet tak, že objekty budou obsahovat odkazy na jiné objekty. Takové odkazy realizují v Javě abstrakci ukazatele.
Sourozence z výše uvedeného příkladu nyní můžeme reprezentovat tak, že objekt třídy Sourozenec bude obsahovat odkaz na dalšího sourozence, ten na dalšího atd. Definice třídy Sourozenec bude tedy obsahovat navíc člen sourozenec, který je odkazem na objekt téže třídy (obr.), co umožní vytvoření (lineárního spojového) seznamu objektů třídy Sourozenec.
Třída Sourozenci uchovávající nyní sourozence použitím spojového seznamu (obr.) bude mít opět konstruktor, kterého parametr je sourozenec vytvářející si seznam svých sourozenců, metodu dalsiSourozenec( ) pro přidání dalšího sourozence na konec jejich seznamu a metodu tiskSourozencu( ) pro vytištění sourozenců. Přidáváním sourozenců se vytvoří seznam jak je znázorněno na obr.
. Jako klientský program můžeme bez jakékoliv změny použít program MojiSourozenci (obr.
) a to přímo jeho přeložený tvar MojiSourozenci.class.
Pomocí ukazatelů můžeme vytvořit „libovolné“ spojové struktury. Jako příklad modifikujeme „sourozence“ na člena rodiny tak, že jednotlivé záznamy člena rodiny budou obsahovat odkazy na jeho rodiče a na mladšího sourozence. Navrhovaná struktura bude reprezentovat sourozence seznamem počínaje nejstarším z nich a jejich přímé předky, tj. rodiče, rodiče rodičů atd. Jde o jisté zjednodušení rodinných vztahů, ale pro náš výklad vyhovuje. Třída ClenRodiny je na obr.. Kromě původních datových členů třídy Sourozenec obsahuje ukazatele na rodiče a metody pro tisk mladšího sourozence a tisk rodičů. Třída Rodina na obr.
umožňuje vytvářet objekt, který je spojovou strukturou organizovanou okolo nejstaršího sourozence, který je parametrem konstruktoru Rodina( ). Členská proměnna nejstarsiDite obsahuje odkaz na seznam sourozenců jako objektů třídy ClenRodiny počínaje nejstarším z nich. Pro každého člena rodiny umožňuje jeho metoda rodice( ) připojit rodiče. Příkladem použití těchto tříd je program MojeRodina na obr.
. Jeho vykonáním nejprve vznikne seznam tří sourozenců, dále jsou vytvořeni a připojeni rodiče nejstaršího ze sourozenců a nakonec rodiče jeho otce (obr.
).
Kód na obr. při volání metody tiskRodicu( ) nekotroluje je-li volána pro objekt třídy ClenRodiny. Kdybychom napsali prvni.rodic2.tiskRodicu nastane chyba, protože rodiče matky nejstaršího sourozence ještě nejsou připojeni, dokonce jsme je ani nevytvořili. Kód v tomto kurzu má jako prvořadý cíl demonstrovat implementaci vysvětlovaných datových struktur a algoritmů. Z důvodu jeho přehlednosti a stručnosti nebudeme ošetřovat všechny chybové stavy. Kontrola chybových stavů a vyvolání výjimek jsou však velice důležité pro tvorbu programů odolných proti chybám. Jak dokázat, že program je správný, si řekneme v následujícím článku.