Dione
Z. KotalaP. Toman: Java
Predchozi (Obsah) Dalsi

10. Základní pojmy z OOP

Tato kapitola je určena těm, kteří nemají zkušenosti se žádným objektovým jazykem a stručně vysvětluje základní pojmy z objektově orientovaného programování (OOP). Ostatním postačí, když se seznámí s kap. 10.5..

10.1. Objekt

Každý větší program se skládá z několika modulů - jeden se stará o výstup na obrazovku a zpracovává pokyny od uživatele, další provádí výpočty, ještě jiný posílá data po síti atd. Každý tento modul je naprogramován do značné míry samostatně, nejen proto, aby se v něm autor po půl roce sám vyznal, ale zejména se již hotové (a odladěné!) moduly dají použít v dalších programech.

Priklad 10.1.
Modulem může být například standardní knihovna pro práci se soubory, která obsahuje funkce pro otevření, čtení, zápis do souboru atd.

V Turbo Pascalu a jazyce C je tato knihovna realizována tak, že údaje o fyzickém souboru (pozice pro čtení a zápis, odkaz na v vyrovnávací paměť atd.) jsou uloženy v datové struktuře - proměnné typu soubor (FILE). Tato proměnná se v programu používá jako argument při volání funkcí knihovny.

Nevýhodou ovšem je, že s obsahem struktury zmíněné proměnné mohou manipulovat kromě knihovny, které to přísluší, i jiné části programu a v případě přepsání údajů může později dojít k jeho zhroucení apod. Bezpečnější by bylo, kdyby k údajům této proměnné měly přístup výhradně funkce příslušné knihovny. Řešením je uzavřít údaje spolu s příslušnými funkcemi do jednoho modulu a místo datové struktury použít pouze odkaz (referenci).

OOP jde v tomto směru ještě dále a s každým souborem (resp. s daty o souboru) přímo sváže funkce, které s ním mohou výlučně manipulovat. Vše si lze představit následovně:

objekt.gif

Objekt je runtime entita (1) skládající se z proměnných, zvaných členské proměnné (member variables), a příslušejících funkcí, zvaných metody (methods). V členských proměnných je obsažen stav objektu, tj. vše, co si objekt "pamatuje", a metody vyjadřují jeho chování, tedy vše, co objekt "umí".

Priklad 10.2.
Myšlenka objektů byla převzata z reálného světa. Jedním z nejjednodušších reálných objektů je obyčejný vypínač - jeho stavem je: poloha (vypnuto, zapnuto) a chováním: zapnutí a vypnutí. I zde je vše uzavřeno uvnitř a nemáme možnost přímo (bez šroubováku) manipulovat se stavem vypínače a přivodit si úraz.

Uzavřenost (encapsulation) je jednou z nejdůležitějších vlastností objektů a má kromě bezpečnosti ještě výhodu skrytí vnitřní implementace a modularity - softwarový objekt může být při zachování rozhraní metod uvnitř zcela přeprogramován a může se libovolně měnit vnitřní struktura dat (členských proměnných), ale není třeba přizpůsobovat okolí.

Pozn.: V programech se uzavřenost úplně striktně nedodržuje - například pokud objekt reprezentuje pouze datovou strukturu (nemá metody), nebo z důvodu rychlosti - a je možné povolit přímý přístup k libovolným členským proměnným (viz 11.4.). To by se však mělo týkat pouze těch proměnných, jejichž změnou zvenčí nelze uvést objekt do nekonzistentního ("nesmyslného") stavu - např. u souboru nastavení pozice pro čtení na zápornou hodnotu.

Pozn.: Jeden objekt se samozřejmě může obsahovat další, vše záleží na zvolené rozlišovací schopnosti. Například pro některé aplikace lze počítač reprezentovat jedním objektem, pro jiné se bude skládat z dalších (motherboardu, harddisku, videokarty, řadiče atd.).

10.2. Zpráva

Samotné objekty vydělené ze světa by byly k ničemu, a proto spolu komunikují pomocí tzv. zasílání zpráv.

Zpráva je obecný pojem, jímž se označuje zaslání požadavku mezi objekty bez ohledu na způsob přenosu, (2) jehož výsledkem je volání metody cílového objektu. Zprávu obecně tvoří:

  • jméno objektu, kterému je obsah určen,

  • jméno metody, která se má vykonat,

  • parametry metody (data).

Mechanismus zasílání zpráv tak umožňuje, že se komunikující softwarové objekty mohou nalézat ve dvou různých procesech, programech nebo dokonce na dvou různých počítačích. Pro jednotlivé situace však není třeba objekty přizpůsobovat.

10.3. Třída

Většina podstatných jmen v našem jazyce označuje třídu živých, neživých nebo abstraktních objektů. Například vaše škodovka a sousedův Mercedes jsou objekty patřící do třídy aut. Třídou se v podstatě rozumí obecné vlastnosti, které objekt musí mít, aby do ní mohl být zařazen - neboli: třída je popisem, vzorem, předlohou celé skupiny podobných objektů.

V programovacím jazyce je třída (class (3) ) přesně popsána. Z hlediska syntaxe je třída obyčejnou deklarací datové struktury (podobně jako record v Pascalu nebo struct v C) rozšířenou o metody. Třída představuje objektový typ, objekt se nazývá instancí třídy.

Priklad 10.3.
Realizace vypínače z příkladu 10.2. v Javě může vypadat takto:

public class Vypinac {
   private boolean poloha = false;

   public void zapni() {
      poloha = true;
   }

   public void vypni() {
      poloha = false;
   }

   public void vypisStav() {
      if (poloha)
         System.out.println("Zapnuto.");
      else System.out.println("Vypnuto.");

   }
}

Třída Vypinac obsahuje veřejně přístupné (public) metody zapni() a vypni() a soukromou (private) členskou proměnnou poloha. Podle této třídy může program vytvořit teoreticky libovolné množství nezávislých instancí:

public class Program {
   public static void main(String[] args) {
      Vypinac prvni = new Vypinac();   // vytvoření vypínače
      Vypinac druhy = new Vypinac();   // vytvoření vypínače

      prvni.zapni();

      prvni.vypisStav();
      druhy.vypisStav();
   }
}

Dosud vše nasvědčuje tomu, že třída je pouhou deklarací objektů a v programu fyzicky neexistuje - existují až její konkrétní objekty, které mají svůj stav uložen v členských proměnných. I samotná třída však může mít proměnné (class variables) a metody (class methods), nazývané též statické.

Statické proměnné existují jen v jednom exempláři a všechny objekty dané třídy je sdílí - jsou tedy jakousi obdobou globálních proměnných pro danou třídu.

Statické metody (4) mají tu výhodu, že je lze volat, i když (ještě) neexistuje žádná instance třídy - viz metoda main v příkladu 4.2..

10.4. Dědičnost

Jedna třída obvykle nestačí pro dostatečně přesnou klasifikaci podobných objektů, například do třídy aut patří dodávky, nákladní, osobní auta atd. Přitom podtřídy mají vlastnosti třídy nadřazené. Tuto myšlenku převzalo i OOP.

Objektové jazyky umožňují konstruovat třídy tak, že od první (nejobecnější) se mohou odvozovat další přidáním nebo překrytím (předefinováním) metod (a příp. přidáním proměnných). Toto odvozování je možné dále opakovat a výsledné třídy jsou pak uspořádány do stromové hierarchie:

strom.gif

Následník dané třídy ve stromu se nazývá potomek (subclass) a předchůdce rodič (superclass), podobně jako v rodokmenu.

Třída v daném uzlu s výjimkou kořene dědí (tj. přejímá, jako by byly znovu definovány) všechny proměnné a metody rodiče (5) a tím, nepřímo, i všech nadřazených tříd. Při procházení stromu shora dolů tak získáváme čím dál specializovanější třídy.

Díky dědičnosti se při programování lze vyhnout mnohým chybám (odvozená třída používá již odladěný kód z rodiče) a ušetří se i opětovné psaní kódu (metody rodičovské třídy není třeba znovu definovat).

Pozn.: Na rozdíl od některých jiných jazyků (např. C++) Java nepodporuje tzv. vícenásobnou dědičnost, kdy může třída mít více než jednoho rodiče, neboť to obvykle přináší větší obtíže než výhody (např. narůstá komplexnost zdrojového textu). Případ, kdy by třída měla mít vlastnosti více rodičů, Java obchází pomocí rozhraní (interface), viz 11.6..

Pozn.: V Javě jsou všechny třídy uspořádány do jediného stromu, jehož kořenem je třída Object (viz 11.5.). Pokud v programu není uveden rodič nové třídy, je jím automaticky právě Object. (6)

10.5. Vícetvarost

V předchozí kapitole bylo naznačeno, že pokud daná třída dědí metody rodiče, jsou tyto přejaty a chovají se jako by byly v této třídě znovu definovány.

To v podstatě vyjadřuje vícetvarost (polymorphism), která říká, že jméno akce (metody) je sdíleno třídami ve stromu dědičnosti a ta je implementována způsobem, který ji na dané úrovni přísluší.

Stoprocentně to ovšem platí pouze o Javě, nikoliv například o Turbo Pascalu či C++. V praxi jde o to, jak se ve zděděných metodách volají překryté (předefinované) metody potomků:

Priklad 10.4.
Máme třídu Bod, jejíž instance představují body na obrazovce. Třída Bod obsahuje dvě metody:

  • zobraz() - zobrazí bod na aktuálních souřadnicích,

  • posun() - přesune bod o zadaný úsek (změní jeho souřadnice) a následně zavolá metodu zobraz().

Třída Kruh je potomkem třídy Bod, dědí tedy obě uvedené metody. Metoda zobraz() je však ve třídě Kruh překryta - zobrazí kruh.

Při volání metod pak v Javě dostaneme výsledek, který bychom asi očekávali:

  • metoda posun() třídy Bod změní souřadnice a zobrazí bod,

  • metoda posun() třídy Kruh změní souřadnice a zobrazí kruh.

V C++ či Turbo Pascalu dopadne výstup "kupodivu" takto:

  • metoda posun() třídy Bod změní souřadnice a zobrazí bod,

  • metoda posun() třídy Kruh změní souřadnice a zobrazí bod!

V programu je totiž kód metody posun() obou tříd sdílen a instance obou tříd tedy používají fyzicky tutéž metodu. V C++ je v metodě posun() "napevno" přeloženo volání metody zobraz() (7) - třídy Bod, a proto dojde k uvedenému výsledku. Oproti tomu v Javě se v metodě posun()za běhu programu rozhoduje, která z metod zobraz() se bude volat (8) .

Jméno metody posun() je (v případě Javy) sdíleno oběma třídami a ta funguje způsobem, který ji na dané úrovni přísluší (vícetvarost).

Pozn.: V C++ či Turbo Pascalu lze dosáhnout stejného výsledku jako v Javě tím, že je metoda (zobraz()) deklarována jako virtuální. Pak se její volání provádí nepřímo přes tzv. tabulku virtuálních metod. Z pohledu C++ jsou tedy všechny nestatické metody v Javě virtuální.


  • (1) Softwarové objekty existují pouze za běhu programu - podobně jako běžné lokální proměnné.
  • (2) To je analogické zaslání dopisu, kdy komunikující také nevědí, kudy a jak bude dopis doručen.
  • (3) Terminologie týkající se pojmů třída, objekt a dalších není bohužel v OOP jednotná. V Turbo Pascalu má například object význam třídy, která se v C++ a Javě deklaruje jako class. Tento sborník se drží pojmů používaných v Javě.
  • (4) Kód statických metod i metod objektů je samozřejmě v programu obsažen pouze jednou.
  • (5) Z procesu dědění je možné některé proměnné či metody vyloučit (viz 11.3.).
  • (6) To je významná vlastnost, která například umožňuje vytvořit zásobník, frontu, hašovací tabulku a další datové struktury, které mohou obsahovat instance libovolných tříd - při zachování typové kontroly!
  • (7) Tzv. časná vazba (early binding).
  • (8) Tzv. pozdní vazba (late binding).

Predchozi
Converted by Selathco v0.9 on 25.09.1999 19:46
Dalsi