Kontext
Objekt, třída
 Vytisknout studijní materiál

Objekt, třída

Uvedené koncepce pro tvorbu programů byly postačující pro malé až střední programy. V návrhu programovacího jazyka SIMULA 67, se uvádí, že pro zvládnutí rozsáhlých problémů je obecně nutná jejich dekompozice na menší části, které by byly lidskou myslí zvládnutelné. Otázkou je, jaký mechanizmus by měl pro tento účel poskytovat programovací jazyk. Odpověď dal sám programovací jazyk SIMULA 67. Základní myšlenka je vytvořit podle programu, co jak víme je opis datových struktur a algoritmu, tedy akcí které se mají nad daty vykonat, několik instancí, které můžou koexistovat a vzájemně reagovat. V specifikaci jazyka SIMULA 67 se uvádí: „Ústředním novým pojmem v SIMULE 67 je objekt “.

Objekt je samostatný program mající vlastní lokální data a akce definované deklarací třídy.

Říkáme, že je instancí třídy. Z pohledu datových typů, můžeme na uvedený mechanizmus pohlížet tak, že jde o jejich zobecnění s tím, že hodnoty jsou seskupením hodnot dat objektu a operace jsou definovány metodami třídy. Podobně jako hodnota patří nějakému datovému typu, objekty patří nějaké třídě.

V Javě se třída skládá z kolekce datových položek (proměnných obsahujících data) a kolekce metod, které vykonávají akce nad těmito položkami. Datové položky a metody se nazývají členy třídy.

Uvedené pojmy si ilustrujeme na zjednodušeném příkladu automobilu. Pro reprezentaci reálných automobilů a jejich provozu deklarujeme třídu, kterou nazveme Auto0, obr.. Z množství datových položek, které by opisovali reálné auto, se omezíme na obsah nádrže a spotřebu paliva na 100km. Jeho provoz, akci nad datovými položkami, budeme opisovat metodou ujelo( ) s parametrem ujeté vzdálenosti. Poznamenejme, že pokud půjde o metodu objektů, které budou podle tohoto opisu vytvářené, hlavička metody neobsahuje klíčové slovo static. Po přeložení programu Auto0 můžeme vytvářet její instance, objekty třídy Auto0.

Vraťme se k analogii algoritmu a receptu z článku 1.1. Pokud procesor = kuchař/kuchařka vykoná nad daty = suroviny předepsané akce = příkazy programu, vznikne třeba dort. Recept ovšem ještě není žádný dort. Podobně i přeložená třída Auto0 ještě není reprezentací žádného auta, tím bude až objekt vytvořený jako instance třídy Auto0.

Objekt třídy se dynamicky vytvoří pomocí operátoru new, za kterým následuje jméno třídy a vrátí referenci (odkaz) na nově vytvořený objekt. Hodnotu reference uchováváme v referenční proměnné třídy objektu , kterou deklarujeme podobně jako jiné proměnné. V Javě tomuto pro náš příklad odpovídá

Auto0 a;

a = new Auto0( );

nebo stručněji

Auto0 a = new Auto0( );

Proměnná a nyní obsahuje referenci na objekt třídy Auto0. Z důvodu stručnosti vyjadřování budeme mluvit o autě a nebo o objektu a, i když přesně řečeno a je proměnná obsahující referenci na objekt třídy Auto0. Graficky budeme znázorňovat vztah mezi referenční proměnnou a objektem šipkou z obdélníku znázorňujícího referenční proměnnou k obdélníku, který vyjadřuje objekt a jeho datové členy, obr..

Vzhledem na výše uvedenou myšlenku dekompozice, budeme jej používat v jiné třídě, kterou nazveme MojeAuto, obr.. Takovou třídu budeme označovat jako klientský nebo aplikační program. Třída MojeAuto je tedy aplikační, klientskou třídou třídy Auto0. Členy objektu jsou přístupné pomocí tzv. tečka zápisu, tj. referencí na objekt následovanou tečkou a jménem člena.

V programu MojeAuto vytiskneme hodnoty datových členů. Tyto jsou po vytvoření objektu nulové. Dále je nastavíme na hodnoty 5,0 pro spotřebu a 20,0 pro obsah nádrže a pro kontrolu je opět vytiskneme. Nakonec zavoláme metodu ujelo( ) s parametrem 200,0, která sníží obsah nádrže o spotřebované palivo a vytiskneme změněný obsah nádrže.

Zásadní myšlenka v uvedeném schématu je ta, že objekt osahuje nejen data charakterizující jeho stav ale současně i akce, které nad nimi pracují. Na druhé straně jazyky jako Algol 60, Fortran, Pascal, C organizovali program do dat a procedur (metod) pracujících nad daty, které byly jejich parametry. Tento způsob si můžeme ilustrovat na předcházejícím příkladě a je uveden na obr.. Vidíme, že automobil je opět reprezentován objektem, nyní pouze s datovými členy a tento je parametrem metody pro změnu obsahu nádrže současně s parametrem ujeté vzdálenosti. Takový styl programování se nazývá procedurální na rozdíl od důsledné dekompozice na objekty, které s daty vhodným způsobem sdružují potřebné metody, kdy mluvíme o objektově orientovaném programování, kterého se budeme dále přidržovat.

Java umožňuje při vytváření objektu inicializovat jeho datové členy a to metodami, které mají stejné jméno jako třída objektu, které se nazývají konstruktory. Konstruktor je možné přetížit, co umožňuje inicializovat objekt různými způsoby. Přetížené metody mají stejné jméno, ale liší se počtem nebo typem nebo pořadím formálních parametrů.

Třída Auto na obr. je rozšířením třídy Auto0. Navíc obsahuje datový člen kapacitaNadrze, který je konstantní. Obsahuje konstruktor Auto( ), který umožňuje inicializovat datové členy kapacitaNadrze a spotreba na hodnoty parametrů metody Auto( ). To, že jde o členy vytvářeného objektu vyjadřuje klíčové slovo this, co je vlastně reference na vytvářený objekt. Tento konstruktor je dále přetížen tak, aby bylo možné vytvořit objekt, kterého datové členy jsou inicializovány stejně jako jiný objekt třídy Auto, což je možné využít budeme-li mít více stejných aut. Následující přetížení konstruktoru (bez parametrů) je možno použít pro objekt, který má mít datové členy inicializované na nějaké typické hodnoty, v našem případě je to kapacita nádrže 24,0 a spotřeba 8,0. Dále jsme přidali metodu pro doplnění nádrže doplnNadrz( ), která vrací množství paliva, které je načerpáno, aby nádrž byla plná a metodu dojezd( ), která vrací dojezd auta. abychom věděli kolik můžeme ještě s autem ujet.

Na obr. je v klientské třídě MojeAuta vytvořeno auto a0 a auto a1 a demonstrováno, že datový člen kapacitaNadrze je neměnný. Na obr. je pokračování klientské třídy MojeAuta, ve které jsou dále vytvořena auta a2, a3 a a4. Auto a4 je různé od auta a2 i když má stejné charakteristiky, tj. stejné hodnoty členských proměnných, ale proměnné odkazují na různé objekty. Naproti tomu auto a11 je totéž auto jako auto a1, obě proměnné mají stejnou hodnotu a tedy odkazují na tentýž objekt. Na obr. je dokončení programu MojeAuta, kde přiřazení hodnoty null referenčním proměnným způsobí, že paměť obsazená odpovídajícím objektem může být uvolněna. Zdůrazněme, že na to, aby paměť obsazena objektem mohla být uvolněna, objekt nesmí být referencován žádnou referenční proměnnou. Uvolňování se děje bez zásahu programátora, což je rozdíl od mnoha jiných jazyků a tento způsob se nazývá garbage collection.

V programu MojeAuta na obr. jsme s členskou proměnnou obsahNadrze objektu třídy Auto pracovali dvěma způsoby. Pro tisk jsme k ní přistupovali přímo pomocí tečka notace. Pro nastavení začáteční hodnoty obsahu nádrže a její změnu v důsledku ujetí nějaké vzdálenosti nebo doplnění nádrže jsme s ní pracovali voláním metod objektu třídy Auto. Objektově orientovaná metodologie preferuje práci s datovými členy objektu pomocí metod. Tedy i pro prosté přiřazení nové hodnoty nebo zjištění hodnoty datového členu se musí napsat a používat odpovídající metoda. Na to, aby použití takových metod bylo opravdu nutné a "nedisciplinovaní" programátoři nevolili tečka notaci, datový člen se označí modifikátorem přístupu private. Naopak metody pro použití datových členů jsou obecně označeny modifikátorem přístupu public. V našich jednoduchých příkladech, z důvodů stručnosti tento modifikátor můžeme vynechat.

Nyní je zřejmé proč metoda main( ) musí být označena public a static (metoda třídy, ne objektu třídy).

Snad nejdůležitějším důvodem přístupu k datovým členům pouze pomocí členských metod je možnost nahradit tuto třídu jinou její implementací, bez nutnosti úpravy klientských tříd, kterých může být velké množství.

Uvažujme třídu X, která pracuje s časem vyjadřeným ve tvaru

byte hodiny, minuty, sekundy;

a klientské třídy, které k těmto datovým členům přistupují přímo. Výpočet rozdílu dvou časů není v tomto tvaru jednoduché. Odečtěte například 4hod 20min 30s - 2hod 30min 40s. Jiný programátor se rozhodne třídu X nahradit implementací, ve které se čas bude vyjadřovat v sekundách. Nyní je výpočet rozdílů dvou časů jednoduchý a rychlý. Všechny klientské třídy se staly nefukční. Řešením je právě použít v obou případech členskou metodu, která poskytuje čas ve tvaru hodiny, minuty, sekundy. V původním tvaru třídy X by to bylo pouhé kopírování hodnot na odpovídající parametry členské metody. Ve druhém případě by členská metoda obsahovala přepočet času vyjádřeného v sekundách do tvaru hodiny, minuty, sekundy. Důležité je uvědomit si, že pokud klientské třídy budou používat pro přístup k časovým hodnotám pouze členskou metodu, která má samozřejmě v obou případech stejné jméno a parametry, klienské třídy při záměně třídy X za novou, a možná efektivnější implementaci, zůstatnou nezměněny a funkční.

Uvedený přístup dále umožňuje vývoj klientských programů nezávisle na implementaci pouřívaných tříd, co umožňuje urychlit vývoj rozsáhlých programových systémů

Kontextem tohoto kurzu tedy je řešení problémů, programovací jazyky, počítače a metodologie programování. Poznatky z těchto oblastí, tak jak jsme je naznačili v této kapitole, budeme respektovat, nikoliv však studovat. Obsahem kurzu je studium datových struktur a algoritmů, jejich programová realizace v jazyce Java na tradičních počítačích a použití na řešení problémů. V závěru se budem věnovat otázkám, jaké problémy jsou na počítačích řešitelné a časově zvládnutelné.