Co je objekt?
Když se rozhlédneme okolo sebe vidíme mnoho příkladů objektů skutečného
světa. Je to např. pes, stůl, televizor nebo jízdní kolo. Tyto objekty
skutečného světa sdílejí dvě charakteristiky: všechny mají stav a všechny
mají chování. Např. pes má stav (jméno, barvu, rasu) a pes má chování (štěkot,
slintání apod.). Jízdní kolo má stav (dvě kola, počet převodů, rychlost
šlapání) a chování (brzdění, akcelerace, změna převodu).
Objekty skutečného světa můžeme reprezentovat pomocí programových objektů.
Můžeme chtít znázornit psa ze skutečného světa jako programový objekt v
animačním programu nebo jízdní kolo skutečného světa jako programový objekt
v elektronickém trenažéru jízdního kola. Programové objekty ale také můžeme
použít k modelování abstraktních představ. Např. událost je obecný objekt
používaný v systému GUI k reprezentaci akce stisknutí tlačítka myši nebo
klávesy na klávesnici.
Následující obrázek je častá vizuální reprezentace programového objektu.
To že programový objekt má stav a může něco dělat (má chování) je zajištěno
proměnnými (datovými složkami) a metodami v tomto objektu. Programový objekt
modelující naše jízdní kolo tedy musí mít proměnné, které indikují současný
stav kola, tj. jeho současnou rychlost, jeho právě zařazený převodový stupeň
atd. Následující obrázek ukazuje model jízdního kola jako programový objekt.
Programové jízdní kolo má také metody k brzdění, změně převodu, změně
rychlosti šlapání, atd. Jízdní kolo nemá metodu pro změnu rychlosti, neboť
rychlost kola závisí na rychlosti šlapání a zařazeném převodu. Něco objekt
jízdního kola ale nemá a něco nemůže provádět. Např. naše jízdní kolo (pravděpodobně)
nemá jméno a nemůže štěkat. Tedy ve třídě jízdního kola nejsou proměnné
nebo metody pro tento stav a chování.
Jak můžeme vidět na předchozím obrázku, proměnné objektu jsou umístěny
uprostřed (v jádru) objektu. Metody obklopují a skrývají jádro objektu
před ostatními objekty v programu. Zabalení proměnných objektu v jádru
chráněném svými metodami se nazývá zapouzdření. Zapouzdření je obvykle
použito k ukrytí implementačních detailů před jinými objekty. Když chceme
změnit ?převodový stupeň, nepotřebujeme znát jak převodový mechanismus
pracuje, stačí pouze přesunout páčku. Podobně v programu, nepotřebujeme
znát jak třída je implementována, potřebujeme pouze vědět, kterou metodu
vyvolat. Implementační detaily je tedy možno měnit bez ovlivnění ostatních
částí ?,
Tento koncepční obrázek objektu, s jádrem proměnných zabalených v ochranné
slupce metod, je ideální reprezentací objektu a je ideálem, který se návrháři
objektově orientovaných systémů snaží dosáhnout. Nicméně to nelze vždy.
Objekty mohou chtít zveřejnit své proměnné pro další objekty a tím
umožnit jiným objektům prohlížet nebo modifikovat tyto proměnné. Objekt
také může chtít skrýt metody před jinými objekty a zabránit tak těmto objektům
vyvolávat metody. Objekt má kompletní řízení nad tím, zda jiné objekty
mohou přistupovat k proměnným a metodám objektu.
Co jsou zprávy?
Samotný objekt obecně není moc užitečný a obvykle je použit jako část
větší aplikace, která obsahuje mnoho objektů. Teprve vzájemným působením
těchto objektů, program získá svoji funkčnost a složité chování. Naše jízdní
kolo stojící v garáži a které je nepoužívané, není moc užitečné. Jízdní
kolo je užitečné pouze tehdy, když jiný objekt (např. já) na něj začne
působit (např. začnu šlapat).
Programové objekty vzájemně působí na jiné objekty a komunikují s nimi
pomocí zasílání zpráv. Když objekt A chce, aby objekt B provedl jednu ze
svých metod, pak objekt A zasílá zprávu objektu B.
Někdy přijímající objekt vyžaduje nějaké informace, které určí co dělat.
Např. když chceme změnit převod našeho jízdního kola, musíme indikovat,
který převod chceme použít. Tato informace je předávána jako parametr zprávy.
Zpráva se skládá ze tří prvků:
-
Objektu, kterému je zpráva adresována (Moje kolo),
-
Jména prováděné metody (Změn převod) a
-
Parametrů vyžadovaných metodou (Nízký převod).
Tyto tři prvky jsou informace pro přijímající objekt k provedení určené
metody. Další informace nejsou vyžadovány.
Chování objektu je ovlůivňováno pomocí jeho metod a tedy (mimo přímého
přístupu k proměnným) zprávy provádějí všechno potřebné vzájemné působení
mezi objekty. Komunikující objekty nemusí být ve stejném procesu ani na
stejném počítači.
Co jsou třídy?
Ve skutečném světě, často máme mnoho objektů stejného typu. Např. naše
jízdní kolo je právě jedno z mnoha jízdních kol na světě. Pomocí objektově
orientované terminologie, můžeme říci, že objekt našeho jízdního kola je
instance
třídy objektů známých jako jízdní kola. Jízdní kola mají nějaký společný
stav (počet převodů nebo počet kol) a společné chování (změna převodu,
brzdění). Ale stav každého jízdního kola je nezávislý a může se lišit od
stavu jiných jízdních kol.
Když vyrábíme jízdní kola, pak řadu jízdních kol vytváříme podle stejného
plánu (vyrobená jízdní kola mají některé stejné charakteristiky). Bylo
by značně neefektivní vytvářet nový plán pro každé vyrobené jízdní kolo.
V objektově orientovaném programu je také možno mít mnoho objektů stejného
typu, které sdílí nějaké charakteristiky. Stejně jako u skutečných jízdních
kol, můžeme přijmout skutečnost, že programové objekty stejného typu jsou
si podobné a jsou vytvořeny podle programového plánu těchto objektů. Programový
plán pro objekty se nazývá třída.
Např. můžeme vytvořit třídu jízdní kolo, která se skládá z několika
instancí proměnných k uložení aktuální rychlosti, aktuálního převodu, atd.
Třída také deklaruje a poskytuje implementace pro metody instance, které
umožňují změnu převodu, brzdění apod. Třídu můžeme znázornit stejným obrázkem
jako objekt.
Hodnoty pro proměnné objektu jsou poskytnuty pro každou instanci třídy.
Po vytvoření třídy jízdní kolo, musíme před použitím instancí třídy tyto
instance vytvořit. Když vytváříme instanci třídy, vytváříme objekt tohoto
typu a systém alokuje paměť pro proměnné deklarované ve třídě. Pak již
můžeme vyvolávat metody objektu a provádět s objektem různé činnosti. Instance
stejné třídy sdílí implementace metod (jsou umístěny v samotné třídě).
Co je dědičnost?
Objektově orientované systémy umožňují aby třídy byly definovány pomocí
jiných tříd. Např. horská kola, závodní kola a tandemy jsou různé typy
jízdních kol. V objektově orientované terminologii, horská kola, závodní
kola a tandemy jsou všechno podtřídy (potomci) třídy jízdní kolo. Obdobně
třída jízdní kolo je nadtřída (předek) tříd horské kolo, závodní kolo a
tandem.
Každá podtřída dědí stav (ve formě instancí proměnných) od nadtřídy.
Horská kola, závodní kola a tandemy sdílejí nějaký stav (např. rychlost).
Každá podtřída také dědí metody od nadtřídy. Horská kola, závodní kola
a tandemy sdílejí nějaké chování (např. brzdění).
Podtřída ale není omezena stavem a chováním poskytnutým ji její nadtřídou.
Podtřída může k zděděným proměnným a metodám přidat své vlastní proměnné
a metody. Tandemy mají dvě sedla a dvojici řidítek; některá horská kola
mají speciální převod s malým převodovým poměrem.
Podtřídy také mohou přepisovat zděděné metody a poskytují specializovanou
implementaci těchto metod. Např. jestliže máme horské kolo se speciálním
převodem, musíme změnit metodu Změna převodu, tak aby jezdec mohl
použít tento nový převod.
Nejsme omezeni jen na jednu vrstvu dědění. Strom dědičnosti nebo hierarchie
tříd může mít libovolný počet úrovní. Metody a proměnné se dědí přes tyto
úrovně. Obecně, čím hlouběji v hierarchii tříd se třída vyskytuje, tím
více specializované je její chování.
Podtřídy poskytují specializované chování na základě společných prvků
poskytnutých nadtřídou. Pomocí použití dědičnosti, programátoři mohou mnohokrát
opětovně používat kód v nadtřídě. Programátoři mohou implementovat nadtřídy
nazývané abstraktní třídy, které definují všeobecné chování. Abstraktní
nadtřídy definují a mají částečnou implementaci chování, ale většina třídy
je nedefinovaná a neimplementovaná. Tyto detaily jsou doplněny ve specializovaných
podtřídách.
-
S OOP se nejprve budeme seznamovat na konzolových aplikacích. Určete co
provádí následující program (nejedná se o program OOP).
struct jeden_udaj
{
int hodnota;
};
int main(int argc,
char **argv)
{
jeden_udaj
pes1, pes2;
int prase;
pes1.hodnota
= 12;
pes2.hodnota
= 17;
prase = 123;
cout <<
"Hodnota pes1 je " << pes1.hodnota << endl;
cout <<
"Hodnota pes2 je " << pes2.hodnota << endl;
cout <<
"Hodnota prasete je " << prase << endl;
return 0;
}
-
Předchozí "nesmyslný" program se pokusíme převést do objektově orientovaného
programování. Po tomto převodu dostaneme:
class jeden_udaj
{
int hodnota;
public:
void nastav(int
int_hodnota);
int ziskej(void);
};
void jeden_udaj::nastav(int
int_hodnota){
hodnota =
int_hodnota;
}
int jeden_udaj::ziskej(void){
return hodnota;
}
int main(int argc,
char **argv)
{
jeden_udaj
pes1, pes2;
int prase;
pes1.nastav(12);
pes2.nastav(17);
prase = 123;
cout <<
"Hodnota pes1 je " << pes1.ziskej() << endl;
cout <<
"Hodnota pes2 je " << pes2.ziskej() << endl;
cout <<
"Hodnota prasete je " << prase << endl;
return 0;
}
Tento program dává stejné výsledky, jako předchozí program. Je zde
ale několik změn. Namísto struktury je použita třída. Jeden z rozdílů mezi
strukturou a třídou je ten, že třída začíná soukromou částí, zatímco struktura
začíná veřejnou částí. Soukromá část třídy je část datových složek, která
není přístupná z vnějšku třídy (je ukryta jakémukoli vnějšímu přístupu).
Proměnná hodnota, která je částí objektu pes1, není dostupná
z hlavního programu. Zdá se to trochu podivné deklarovat proměnnou, kterou
nemůžeme použít, ale přesně to jsme udělali. Naše třída je složena z jednoduché
proměnné nazvané hodnota a dvou funkcí, z nichž jedna se jmenuje
nastav
a druhá ziskej.
Nové klíčové slovo public zahajuje veřejnou část (tj. veřejné
rozhraní). Tato část je přístupná z hlavního programu. Obě funkce v naší
třídě leží ve veřejné části a jsou tedy přístupné pro použití z hlavního
programu. Proměnná hodnota je přístupná pouze ve funkcích definujících
veřejné rozhraní třídy. Funkce třídy se nazývají metody (nebo členské funkce),
neboť jsou částí třídy.
Jelikož jsme deklarovali dvě funkce, musíme je také definovat, tj.
určit co budou dělat. To provádíme obvyklým způsobem, s tím rozdílem, že
jméno funkce předchází jméno třídy a je odděleno dvěma dvojtečkami. Tyto
definice se nazývají implementace metod. Jméno třídy je požadováno, protože
můžeme v několika třídách použít stejné jméno funkce a překladač musí vědět,
se kterou třídou má spojit kterou implementaci. Podstatné je, že soukromá
data třídy jsou dostupná v metodách třídy pro modifikování nebo čtení,
ale soukromá data z jiných tříd zde dostupná nejsou.
S použitím terminologie OPP můžeme říci, že naše třída obsahuje jednu
proměnnou (datovou složku) a dvě metody. Metody operují na proměnných třídy.
V deklaraci typu třídy jsou uvedeny prototypy metod.
V programu (po skončení definicí) deklarujeme dva objekty třídy jeden_udaj
a nazveme je pes1 a pes2. Každý objekt obsahuje jednu datovou
složku, kterou můžeme nastavovat pomocí jedné metody a číst pomocí druhé.
Nejprve zasíláme zprávu objektu pes1 s instrukcí aby nastavil vnitřní
hodnotu na 12. Objekt pes1 má metodu nastav(), která nastavuje
jeho vnitřní hodnotu na aktuální parametr obsažený ve zprávě. Použitý zápis
se podobá přístupu k prvku struktury (jméno objektu, tečka a jméno metody).
Dále obdobně zašleme zprávu druhému objektu. Druhá metoda je také definována
pro všechny objekty a v příkazech cout je použita. Objektu je zaslána
zpráva a vrácená hodnota je vypsána.
V programu je také použita proměnná prase. Je to normální proměnná
a používáme ji běžným způsobem.
-
Do předchozího programu přidejte metodu vracející druhou mocninu uložené
hodnoty.
Do programu vložte dále několik řádků kódu pro zjištění a zobrazení druhé
mocniny uložené hodnoty.
-
Zjistěte co provádí následující konzolová aplikace:
int plocha(int obd_vyska,
int obd_sirka);
struct obdelnik {
int vyska;
int sirka;
};
struct pole {
int delka;
int sirka;
};
int plocha(int obd_vyska,
int obd_sirka) {
// plocha obdélníka
return obd_vyska
* obd_sirka;
}
int main(int argc,
char **argv)
{
obdelnik okno,
ctverec;
pole moje_pole;
okno.vyska
= 12;
okno.sirka
= 10;
ctverec.vyska
= ctverec.sirka = 8;
moje_pole.delka
= 50;
moje_pole.sirka
= 6;
cout <<
"Plocha okna je " << plocha(okno.vyska, okno.sirka) << endl;
cout <<"Plocha
ctverce je "<<plocha(ctverec.vyska,ctverec.sirka)<<endl;
cout <<"Nesmyslná
plocha je "<<plocha(ctverec.vyska, okno.sirka)<<endl;
cout<<"Špatná
plocha je "<<plocha(ctverec.vyska,moje_pole.sirka)<<endl;
return 0;
}
V tomto programu jsou poslední dva výsledky v reálném světě nesmyslné.
-
Předchozí program převedeme do OOP a dostaneme:
class obdelnik {
// jednoduchá třída
int vyska;
int sirka;
public:
int plocha(void);
// s dvěmi metodami
void inicializace(int,
int);
};
int obdelnik::plocha(void){
// plocha obdélníku
return vyska
* sirka;
}
void obdelnik::inicializace(int
nova_vyska, int nova_sirka){
vyska = nova_vyska;
sirka = nova_sirka;
}
struct pole {
int delka;
int sirka;
};
int main(int argc,
char **argv)
{
obdelnik okno,
ctverec;
pole moje_pole;
okno.inicializace(12,10);
ctverec.inicializace(8,8);
moje_pole.delka
= 50;
moje_pole.sirka
= 6;
cout <<
"Plocha okna je " << okno.plocha() << endl;
cout <<
"Plocha ctverce je " << ctverec.plocha() << endl;
//cout <<
"Nesmyslná plocha je " <<plocha(ctverec.vyska, okno.sirka)<<endl;
//cout <<
"Špatná plocha je "<<plocha(ctverec.vyska,moje_pole.sirka)<<endl;
return 0;
}
V tomto programu obdelnik je změněn na třídu se dvěmi složkami,
které jsou nyní soukromé a dvěmi metodami. Jedna z metod je použita k inicializaci
hodnot vytvořeného objektu a druhá metoda vrací plochu objektu. Pole je
ponecháno jako struktura. V programu jsou deklarovány dva objekty (okno
a ctverec), ale již nelze přiřadit hodnoty přímo jejím položkám.
Jejich inicializace nyní probíhá zasláním zpráv objektům. Nelze již provádět
nesmyslné výpočty tak, jako v předcházejícím programu.
S daty mohou být prováděny pouze ty operace, které jsou definovány
metodami (data jsou tedy chráněna před nesmyslnými operacemi). Zapouzdření
a skrývání dat spojuje data a funkce pevně dohromady a omezuje obor platnosti
a viditelnost soukromých prvků třídy.
Nové pojmy:
-
Třída je deklarace typu objektu (programový plán pro objekty stejného
typu).
-
Objekt je konkrétní instance třídy.
-
Veřejná část třídy je veřejné rozhraní třídy.
-
Soukromá část třídy není přístupná z vnějšku třídy.
-
Metoda je funkce, která je součástí třídy.