Java - objektovo orientovaný prístup

Z Kiwiki
Skočit na navigaci Skočit na vyhledávání

Objektovo orientovaný prístup sa od tradičných postupov líši predovšetkým v tom, že ide o nový spôsob premýšľania. Ide o spôsob pohľadu na softvér pomocou abstrakcie reálneho sveta. Základným stavebným prvkom je objekt (entita), ktorá v sebe zahŕňa dátovú štruktúru popisujúcu určitú entitu, ale aj pravidlá správania sa tejto entity. Tým sa objektový prístup líši od konvenčného (štrukturovaného) programovania, kde dátová štruktúra a správanie sú spojené len veľmi voľne.

Koncepcia OOP

  • Objekty - jednotlivé prvky modelovanej reality (tak ako dáta, tak súvisiaca funkcionalita) sú v programe zoskupené do entít, nazývaných objekty. Objekty si pamätajú svoj stav a navonok poskytujú operácie (prístupné ako metódy).
  • Abstrakcia - programátor (resp. program, ktorý vytvára), môže abstrahovať od niektorých detailov práce jednotlivých objektov. Každý objekt pracuje ako čierna skrinka, ktorá dokáže vykonávať určené činnosti a komunikovať s okolím bez toho aby poznala spôsob akým je daná funkcionalita implementovaná.
  • Zapúzdrenie - zaručuje, že objekt nemôže priamo pristupovať k vnútorným položkám iných objektov, čo by mohlo viesť k nejednotnosti. Každý objekt navonok sprístupňuje rozhranie, pomocou ktorého (a nijako inak) sa s objektom pracuje.
  • Skladanie - Objekt môže obsahovať iné objekty.
  • Delegovanie - Objekt môže využívať služby iných objektov tak, že ich požiada o vykonanie operácie.
  • Dedičnosť - objekty sú organizované stromovým spôsobom, kedy objekty nejakého druhu môžu dediť z iného druhu objektov, čím preberajú ich schopnosti, ku ktorým len pridávajú svoje vlastné rozšírenie. Táto myšlienka sa obvykle implementuje pomocou rozdelenia objektov do tried, pričom každý objekt je inštanciou nejakej triedy. Každá trieda potom môže dediť od inej triedy.
  • Polymorfizmus - odkazovaný objekt sa chová podľa toho, akej triedy je inštanciou. Ak niekoľko objektov poskytuje rovnaké rozhranie, pracuje sa s nimi rovnakým spôsobom, ale ich konkrétne správanie sa líši podľa implementácie. U polymorfizmu podmieneného dedičnosťou to znamená, že na miesto, kde je očakávaná inštancia nejakej triedy, môžeme dosadiť aj inštanciu ľubovoľnej jej podtriedy, pretože rozhranie triedy je podmnožinou rozhranie podtriedy. U polymorfizmu nepodmieného dedičnosťou je dostatočné, ak sa rozhranie (alebo ich požadované časti) u rôznych tried zhodujú, potom sú vzájomne polymorfná.

Základnou paradigmou OOP[1] je snaha pri riešení úloh modelovať princípy reálneho sveta pokiaľ možno jedna ku jednej. V praktickom živote otvárame dvere stále rovnako, bez ohľadu na to, či sú drevené alebo laminované, či majú kukátko, bezpečnostnú vložku alebo retiazku. Rovnako tak sa môžeme pozerať na televíziu, prepínať programy a vcelku dobre ju ovládať, napriek tomu že nevieme vôbec nič o princípoch jej fungovania. Analogicky, pri vývoji zložitých informačných systémov môžu vývojári používať už vytvorené komponenty, podľa potreby si ich trochu upraviť alebo ich používať ako stavebnicu pre zostavovanie dômyselnejších a zložitejších objektov.

Deklarácia triedy

Trieda (objektov) nám umožňuje vymenovať vlastnosti (properties) budúcich objektov (napr. kniha má autora, vydavateľstvo, rok vydania, počet strán ...) a tiež činnosti (metódy), ktoré sa k nim vzťahujú (hľadanie slov, hľadanie v obsahu, vypísanie zoznamu obrázkov, tlačenie ...). Aby sme mohli s triedou pracovať, musíme ju pred použitím deklarovať:

class MojaTrieda{
  // telo triedy, obsahuje metody triedy a prislusne atributy 
}

Zatiaľ sme používali iba triedy s jedinou metódou main, ktorá je automaticky vykonaná po spustení triedy a všetky premenné sme deklarovali v rámci nej. Teraz v metódach začneme používať aj premenné a funkcie - metódy, definované v iných triedach tak, že si vytvoríme objekt z príslušnej nami vybranej triedy, ktorú chceme použiť.

Objekt je "výrobok" majúci triedou deklarované vlastnosti. Obsahuje premenné a pomocou neho môžeme používať aj metódy, ktoré boli zadefinované triedou. Zjednodušene môžeme povedať, že trieda je akýmsi predpisom alebo šablónou, podľa ktorej môžeme 'vyrábať' objekty. Nový objekt vytvoríme z deklarácie triedy pomocou operátora new. Pri vytváraní objektu si zvolíme meno, pod ktorým budeme objekt ďalej poznať a používať. V skutočnosti ale vytvorený objekt nie je pevne zviazaný s menom objektu, meno objektu je len akýmsi ukazovátkom - referenciou a v budúcnosti môžeme menu (referencii) priradiť úplne iný objekt.

...
MojaTrieda obj = new MojaTrieda()  // 'obj' je  ukazovátko - referencia na vytvorený objekt
...

Môžeme povedať, že vo všeobecnosti trieda popisuje

  • ako sa objekt vytvára (vyrába)
  • aké má objekt vlastnosti
  • ako sa objekt ovláda

Metódy triedy

Trieda je zovšeobecnením - abstrakciou množiny objektov, veľa objektov s rôznymi vlastnosťami môže byť 'vyrobených' z jednej spoločnej triedy. Objekt je zase inštanciou triedy.

Ako príklad môžeme uvažovať triedu, ktorá reprezentuje častice hmoty. To, čo ich charakterizuje - ich vlastnosti - budú premenné danej triedy. Na začiatok špecifikujme len polohu a rýchlosť pohybu, neskôr môžeme uvažovať aj napr. hmotnosť, náboj, energia ... Po zapísaní triedy častica bude možné vyrobiť objekty - jednotlivé častice, ktoré majú samozrejme nezávisle od seba rôzne polohy a rýchlosti. Aby sme si ukázali, ako vie nový objekt castica pracovať zo svojimi vlastnosťami, zadefinujme si aj metódu move, ktorá simuluje pohyb častice tým, že k aktuálnej polohe pripočíta zmenu polohy v dôsledku rovnomerného pohybu častice:

class Castica {
  double x, y, z, vx, vy, vz;
  
  public void move(double dt) {
    x = x + dt * vx;
    y = y + dt * vy;
    z = z + dt * vz;
  }
}

Argument metódy move udáva, o koľko sa v čase posunieme. Kľúčové slovo void hovorí, že návratový typ výsledku vykonania metódy je žiadny - "vracia nič" (nepotrebujeme ho). Ak vytvoríme nový objekt, k jeho atribútom pristupujeme pomocou bodky (.) nasledovanej požadovaným atribútom (dátami alebo metódami) podľa mena uvedeného v deklarácii triedy. Pre testovanie objektov triedy Castica použijeme už dobre známy postup s triedou ktorá obsahuje metódu main.

Ako vidíme, v triede sa združujú spoločne dáta (premenné x,y ...) a program, ktorý s tymito dátami pracuje (metóda move). Takýto postup nazývame zapúzdrenie a je jedným zo základných atribútov objektovo-orientovaného programovania.

 
class Test{
  public static void main(String[] args) {
    Castica castica = new Castica();
    castica.x = 0.1;
    castica.y = 0.2;
    castica.z = 1.1;
    castica.vx = -0.5;
    castica.vy =  0.4;
    castica.vz =  0.2;
    castica.move(0.1);
    System.out.println("Pozicia: " + castica.x + ", " + castica.y + ", " + castica.z);
  }
}

Pri volaní operátora new v našom prípade virtuálny stroj Javy vytvorí nový objekt, umiestni ho v pamäti a hodnoty všetkých premenných v objekte nastaví na štandardné hodnoty, t.j. x,y ... budú mať hodnotu 0.0. Ďalej si ukážeme, že objekt môžeme skonštruovať aj v metóde inej triedy a ako je možné priamo pri vytváraní špecifikovať vlastnosti nového objektu.

Riadenie prístupu

Doposiaľ boli všetky vnútorné premenné vytváranej triedy definované ako verejné. Znamená to, že k nim môžeme pristupvať z iných tried - napríklad ako v predchádzajúcom zdrojovom kóde - z triedy Test.

Pri definícii premenných triedy, ale aj metód triedy môžeme (a mali by sme) určiť, či sa jedná o verejné premenné/metódy alebo súkromné, ku ktorým bude povolený prístup len zvnútra aktuálnej triedy. Špecifikátor, ktorý toto určuje sa uvádza pred definíciu triednych premených, resp. metód triedy. Poznáme 3 základné metódy prístupu:

  • private - člen je prístupný len v triede, kde je definovaný
  • pretected - člen je prístupný z väčšiny podtried triedy, kde je deklarovaný. A taktiež zo všetkých tried v balíčku, kde je deklarovaný.
  • public - člen je prístupný odkiaľkoľvek.

Zmeňme definíciu triedy Castica nasledovne

class Castica {
  private double x, y, z, vx, vy, vz;
  
  public void move(double dt) {
    x = x + dt * vx;
    y = y + dt * vy;
    z = z + dt * vz;
  }
}

Definovali sme vnútorné premenné triedy Castica (x, y, z, vx, vy, vz) ako privátne. Od teraz nie je možné pristupvať k nim ako je ukázané v triede Test (na tejto stránke).

Pre prístup k týmto vnútorným premenným a k ich zmene sa používajú špeciálne metódy, ktoré majú za úlohu pristupvať k vlastnostiam objektu a meniť hodnoty vlastností objektov. Viac o týchto metódach je v časti Java - prístupové metódy.

Zánik objektu

Pri vytváraní objektov java postupne rezervuje v pamäti počítača miesto pre jednotlivé objekty. Počas práce programu môžu nastať okolnosti, kedy objekty už nie sú potrebné alebo potrebujeme uvoľniť pamäť počítača pre nové objekty. V starších programovacích jazykoch, ktoré používali koncept objektovo-orientovaného programovania (napr. C++) musel programátor veľmi starostlivo strážiť stav operačnej pamäte, aby mu v nej nezostávali zbytočné alebo 'bezprizorné' objekty. Toto bývalo obľúbeným zdrojom ťažko identifikovateľných chýb, ktoré sa často prejavovali až po nejakej dobe činnosti programu. V jave sa nám o likvidáciu nepotrebných objektov stará špeciálny mechanizmus implementovaný priamo v jave, ktorý spôsobí automatické zmazanie objektu, akonáhle zaniknú všetky väzby k tomuto objektu.

    ...
    Castica castica;   // referencia na objekt, ukazuje nikam (na null)
    ...
    castica = new Castica(-0.5, 0.4, 0.2);  // objekt 1
    ...
    castica = new Castica(-0.3, 0.1, 0.5);  // objekt 2, ta ista referencia
    ...

V poslednom riadku sme vytvorili nový objekt (2), starý objekt (1) sa stal nedostupným (nemáme k nemu žiadnu väzbu-referenciu) a preto ho java pri vhodnej príležitosti z pamäte odstráni.

Hodnoty a referencie

Premenné primitívnych dátových typov (int, double ...) sú vždy v jave reprezentované hodnotou, t.j. meno premennej priamo reprezentuje jej numerickú alebo znakovú hodnotu. Ako sme si ukázali vyššie, premenné objektových dátových typov - objekty sú reprezentované referenciou, t.j. pod menom premennej sa skrýva ukazovátko - smerník na dátovú oblasť, kde je objekt uložený. Programovacie jazyky, ktoré používajú tento koncept pri implementácii objektovo-orientovaného programovania sú označované aj ako hybridné jazyky na rozdiel od čisto objektovo-orientovaných jazykov ako napr. Smalltalk, kde sú všetky premenné bez výnimky (aj každé číslo) objektami.

Hybridný koncept značne zrýchluje a zjednodušuje štruktúru jazyka, na druhej strane, ako je zrejme z vyššie uvedeného, musíme byť opatrný pri spoločnom používaní hodnôt a referencií, pretože bez znalosti deklarácie dátového typu nevieme hodnoty a referencie odlíšiť. Zvlášť opatrným treba byť pri priraďovaní:

    double x=1.0;
    double y=x;    // kopirujeme hodnoty
    double z=y;
    ...
    Castica castica_1; 
    Castica castica_2; 
    ...
    castica_1 = new Castica(x, y, z);  
    castica_2 = castica_1;     // kopirujeme odkazy na objekty
    ...

Pri priraďovaní hodnotám premenným x,y,z typu double sa hodnoty jednoducho skopírujú. Pri priraďovaní objektom sa skopírujú odkazy a preto referencie castica_1 a castica_2 ukazuju na ten istý objekt v pamäti počítača.

Zdroje a odkazy