Obsah

AP4, IN4 Objektově orientované programování

Zadání

(základní pojmy OOP, zapouzdření, dědičnost, polymorfizmus, objektové programování v imperativním jazyce, spolupráce objektů. Událostmi řízené programování. Výjimky)

Úvod

Objektově orientované programování je metoda vytváření software. OOP vzniklo v 70. letech 20. století, oproti strukturovanému programování nedekomponuje problém shora dolů (rozdělení na menší celky, viz ap3), ale vytváří stavební objekty, pomocí nichž se program modeluje (styl zdola nahoru). Lépe odpovídá uspořádání skutečného světa. Mezi OO programovací jazyky patří Smalltalk, C++, Java, Python apod.

Příklady v této sekci jsou v syntaxi jazyka Java pokud není uvedeno jinak.

Objektový model

Objekt - má svůj obraz v reálném světě, má vlastnosti a chování reprezentovány atributy a metodami. Objekt navenek zpřístupňuje rozhraní, pomocí kterého se s objektem pracuje. Veřejné rozhraní objektu je dáno veřejnými metodami objektu.

Třída je typ objektu, instance je konkrétní výskyt objektu. Příklad ukazuje vytvoření instance třídy Person:

Person kuba = new Person("Kuba");    //novy objekt kuba je instanci tridy Person 

Když je objekt vytvářen, volá se také automaticky speciální funkce třídy nazývaná konstruktor (užívaná k inicializaci členů třídy).

Jednotlivé Atributy a metody jsou vlastnosti instancí objektu, nikoliv tříd.

kuba.name         //atribut instance uchovavajici jmeno 
kuba.getName()    //metoda vracejici jmeno 

Speciálním případem třídy je abstraktní třída. Je to třída která obsahuje nejméně jednu abstraktní metodu a z této abstraktní třídy není dovoleno vytvářet instance. Teprve třída, která je jejím potomkem (viz dále) a implementuje všechny abstraktní metody, může vytvářet instance. Neabstraktní metody abstraktní třídy jsou společné všem potomkům.

public abstract class SqlOperations { 
    abstract void sqlConnect(); //tato metoda bude rozdilna pro Oracle, MySQL... 
} 
 
public class MySqlOperations extends SqlOperations{ 
    void sqlConnect() { 
        //implementace podle potreby 
    } 
}

Interface (rozhraní) je množina hlaviček metod, které mohou být implementovány třídou. Pokud třída implementuje rozhraní, musí doimplementovat všechny jeho metody. Pokud tedy existuje rozhraní

public interface ElectricalDevice { 
    public void switchOn(); 
    public void switchOff(); 
} 

a nějaká třída jej implementuje, můžeme si být jisti, že instance dotyčné třídy obsahují metody switchOn() a switchOff(). V C++ interface není.

if (microwave instanceof ElectricalDevice) {    //trida Microwave implementuje ElectricalDevice 
    microwave.switchOn(); 
} 

V některých jazycích (Java, platforma .NET) se automaticky stará o destrukci nepotřebných objektů garbage collector. Objekty jsou rušeny, jakmile na něj neexistuje žádný odkaz (např. při konci bloku, kde byl objekt definován, nebo přiřazením hodnoty odpovídající null). Při zániku objektu je volána automaticky speciální funkce destruktor.

Koncepty OOP

Objektové programování je charakterizováno především následujícími koncepty:

Atributy by měly být privátní (jinak porušení zapouzdření), přístup k nim by měl být povolen pouze přes metody (viz Spolupráce objektů). Příklad:

public class Person(){ 
    private String personName; 
    public void changePersonName(String newName){ 
        //tato metoda umoznuje kontrolu jmena pred jejim prirazenim atributu 
        //k samotne personName neni z vnejsku tridy pristup 
        if (allowNewName(newName)) { 
            personName = newName; 
        } 
    } 
} 

Dědičnost odpovídá vztahu generalizace specializace (automobil → nákladní automobil lze dědit, protože nákladní automobil je speciální případ automobilu, ovšem bod → bod+poloměr dědit nelze, protože kruh není speciální případ bodu). V Javě je možné, aby třída byla přímým potomkem pouze jedné třídy.

Obecně rozlišujeme 3 druhy polymorfismu

  1. objektový polymorfismus - v objektovém programovacím jazyku pod pojmem polymorfismus rozumíme to, že dva objekty rozšiřující třídu různým způsobem implementují stejnou abstraktní metodu (1)
  2. překrytí(override) - objekt přetěžuje(znovu definuje) poděděnou metodu (2)
  3. pretížení (overload) - stejně nazvaná metoda má stejnou obecnou funkcionalitu, ale může pracovat s různými parametry (např. zásobník.push(X) s parametry int X, bool X, string X bude fungovat vždy jako zásobník) (3)
abstract class Zvire(){ 
 
     public Zvire(){} 
 
     abstract void makeNoise(); 
 
     public void runAway(){ 
        System.out.print("Zvire utika pryc."); 
     } 
} 
 
public class Kocka() extends Zvire{ //Kocka dědí metody třídy Zvire, jde o dedičnost 
     public Kocka(){ 
     } 
 
     public void makeNoise(){//**(1)** 
        System.out.print("Mnau mnau."); 
     } 
} 
 
public class Ptak() extends Zvire{ 
      public Ptak(){} 
 
      public void makeNoise(){//**(1)** 
         System.out.print("Pip pip."); 
      } 
 
      @Override 
      public void runAway(){ //**(2)** 
         System.out.print("Zvire odletlo pryc."); 
      } 
 
      public makeNoise(string zvuk){ //**(3)** 
         System.out.print("Pip pip."+zvuk); 
      } 
 
     public makeNoise(int times){ //**(3)** 
         System.out.print("Pip pip "+times.toString()+" krát"); 
      } 
} 

Spolupráce objektů

Pokud objekty implementují rozhraní, tak z vnějšku s tímto objektem komunikují ostatní objekty pomocí rozhraní.

Spolupráce objektů je úzce svázaná s Objektově orientovaným návrhem. V návrhu se definují vazby mezi jednotlivými třídami, tj. volání metod mezi třídami, přistupování k atributům tříd atd. Pokud je dobře provedený návrh, tak je možné z tohoto návrhu rovnou vygenerovat balíčky, rozhraní a třídy včetně metod a atributů a programátor pak jen naimplementuje jednotlivé metody.

Funguje to podobně jako volání metod v rámci jedné třídy - vytvoříme si instanci třídy (nebo použijeme vhodnou existující), zavoláme metodu a dostaneme návratovou hodnotu(pokud je void, tak jen provede kód a nevrátí nic). Samozřejmě je třeba myslet i na výjimky.

Většina jazyků již má hotové nejrůznější balíčky objektů, které můžeme použít při běžné práci - například při komunikaci s databází, tvorbu GUI atd.

Výjimky

Reagují na chybu. Měly by se používat výhradně k ošetření výjimečných situací, nejsou standardní prostředek řízení programu. V Javě se výjimky dědí stejně jako ostatní třídy, programátor může vytvářet vlastní.

Rozlišují se hlídané a nehlídané výjimky:
* Hlídaná výjimka - pokud může vzniknout, musíme na ni zareagovat, reagují na věc, kterou programátor nemůže ovlivnit, chybu okolního prostředí. V Javě potomci třídy Exception. Příklady:

 IOException, CertificateExpiredException... 

* Nehlídaná výjimka - taktéž běhová, runtime výjimka. Označují chybu vzniklou kdekoliv v kódu, reagují na chybu programátora (i v případě nekontrolování vstupu uživatele). Typicky se z nich nelze zotavit. V Javě potomci třídy RuntimeException. Příklady:

IllegalArgumentException, NullPointerException, IndexOutOfBoundsException...

Při výjimce může program skončit, pokusit se z výjimky zotavit, poslat ji volající funkci, vyhodit jinou výjimku (třeba více obecnou).

Ošetření výjimky:

try { 
    //hlidany blok 
} catch (TypVyjimkyException promennaVyjimky) { 
    //osetreni vyjimky 
} catch (ObecnejsiTypVyjimkyException promennaVyjimky) { 
    //osetreni vyjimky. specialnejsi predchazi obecnejsi 
} finally {    //v C++ finally neni 
    //blok provedeny at je vyjimka vyhozena nebo ne 
    //typicky zavreni otevreneho souboru, ukonceni sitoveho spojeni... 
    //vyse zminene by klidne mohlo byt realizovano i v catchi, ale 
    //finally slouzi spise k osetreni predcasneho ukonceni behu (return;) v try bloku 
} 

Propagace výjimky: k ošetření dojde o jednu nebo více úrovní výše

public class Evil{ 
 
  public static void main(String[] args) { 
      try { 
         DoEvilWithFive();                             
      } catch(Exception e) { //zachytí obecnou vyjímku       
         System.out.println("You are such an evil man.");   
      }  
  } 
 
  public void DoEvilWithFive() throws DivideByZeroException{ 
     this.doEvil(5); //vyhodí vyjímku která je vypropagována do main 
  } 
 
  public void doEvil(int number) throws DivideByZeroException{  
     int evil; 
     evil = number/0; //vyhodí vyjímku dělení nulou, která je vypropagována do metody DoEvilWithFive() 
  } 
} 

Událostmi řízené programování

Základní princip reakce objektového programu na vnější akci. Používá se např. u programů s GUI – běh programu je řízen událostmi.
Objekty GUI mají nadefinovány události (eventy; např. onClick, onMouseOut, onLoad, onClose…) a programátor k nim programuje metody – eventListenery – obsahující příkazy (např. volání privátních metod) které na událost reagují.
Události však nejsou pouze u objektů GUI, používají se u veškerých objektů, kde může vzniknout událost (object.onUnload, timer.onTimeout, http.onResponse, thread.onDispose…). Událostmi řízené programování bývá vícevláknové, ale většinou spouštění vláken řídí běhové prostředí a my se o vlákna starat nemusíme. Existují ale případy, kdy je vhodné si separátní vlákna spustit samostatně (např. z důvodu využití výkonu vícejádrových procesorů).

//kód je v C# 
public class GUI: Form { 
    private Button tlacitko; 
 
    private void GUI(){ 
        this.tlacitko = new Button(); 
        this.tlacitko.Text = "Spusť poplach"; 
        this.tlacitko.Click += new System.EventHandler(this.tlacitko_Click_Handler); 
    } 
 
    private void tlacitko_Click_Handler(object sender, EventArgs e){ 
        MessageBox.Show("Poplach, poplach, poplach"); 
    } 
} 

Několik rozdílů mezi C++ a Javou a C#

Každý z těchto jazyků vzniknul za jiným účelem - C++ jako objektová verze nízkoúrovňového jazyka C, Java jako abstrahovaná od platformy pomocí Java Virtual Machine. Několik důležitých rozdílů mezi těmito jazyky:

C++:

Java

C#

//kód je v C# 
public class test 
{ 
    private string attribute = null; 
 
    //nastavovani vnitrni hodnoty attribute (get a set muzou obsahovat libovony kod) 
    public string ManualProperty 
    { 
        get { return attribute; } 
        set { attribute = value; } 
    } 
 
    //vyse uvedeny kod bez pouziti vnitrni promenne 
    public string AutomaticProperty 
    { 
        get; 
        set; 
    } 
} 

Seznam předmětů

PB161: Programování v jazyce C++ PB162: Programování v jazyce Java

Stránky k předmětu PB161 Stránky k předmětu PB162

Použitá literatura

OOP (Wiki CS) Objektový návrh v Javě (FI Wiki) Výjimky (PDF) Událostmi řízené programování

Vypracuje

Kuba Talaš (icq 257208851)
Vesměs hotovo, případně se vyjádřete v diskuzi nebo přímo upravte wikistránku.
Iniciativě se meze nekladou ;)