You are on page 1of 13

Predchádzajúca prednáška

Testovanie programu
Testami riadený vývoj softvéru (Test-driven development - TDD)
Refaktoring kódu
Polymorfizmus
Rozhranie (Interface)
Trieda implementujúca rozhranie

Prednáška 3 – polymorfizmus, rozhranie (pokračovanie)

Polymorfizmus: rôzne objekty (objekty rôznych tried) môžu prijať tú istú správu a
pritom každý z nich reaguje na prijatú správu inak (vykonáva inú metódu)

Rozhranie (interface)
- rozhranie (v širšom slova zmysle) špecifikuje interakciu objektu so svojím
okolím
- je tvorené zoznamom správ, ktoré je schopné objekt prijať a reagovať na ne
(vykonaním zodpovedajúcej metódy)
- rozhranie v užšom slova zmysle v jazyku Java (interface) predstavuje
referenčný typ (podobne ako trieda)
- rozhranie definuje množinu metód ale neimplementuje ich

Definícia rozhrania

[ public] interface Identifikátor [ extends Rozhr1, ... ]


{
// deklarácia konštánt
// signatúry metód
}

public – dostupnosť rozhrania všade (ak chýba – dostupnosť v rámci balíčka)


Identifikátor – identifikátor rozhrania (konvencia – začína veľkým písmenom)
extends Rozhr1, ... – rozhranie je potomkom rozhraní Rozhr1, ... (je
rozšírením rozhraní)
signatúra metódy
- ako hlavička metódy, ukončená bodkočiarkou, nezáleží na názve
parametrov, dôležitý je iba ich typ
- neobsahuje telo metódy (príkazy medzi { })
- všetky metódy sú implicitne public (aj keď modifikátor public nie je
uvedený)
- !!! iba metódy inštancií (nie statické ) !!!
Trieda implementujúca rozhranie

[....] class meno_triedy implements Rozhranie1 [, ...]


{
[ // deklarácia atribútov ]
[ // deklarácia konštruktorov ]
// deklarácia metód
}

- Rozhranie1 [, ...] - rozhrania, ktoré trieda implemetuje (trieda môže


implementovať viacero rozhraní
- časť deklarácia metód musí obsahovať deklarácie všetkých metód,
definovaných v rozhraniach, ktoré trieda implementuje
- signatúry implementovanej a implementujúcej metódy sa musia zhodovať

Príklad:
public interface Zobrazitelny
{
void zobraz(int kolkokrat);
}

public class Text implements Zobrazitelny


{
// atributy
// konstruktory

public void zobraz (int pocetZobrazeni)


{
// telo metody - prikazy
}

// ostatne metody
}

public class Obrazok implements Zobrazitelny


{
// atributy
// konstruktory

public void zobraz(int pocetZobrazeni)


{
// telo metody - prikazy
}

// ostatne metody
}

public class Okno


{
private Zobrazitelny [] objekty; // prvky pola typu rozhranie
// staticky typ prvkov pola je Zobrazitelny
public Okno()
{
objekty = new Zobrazitelny[50];
objekty[0] = new Text(); // dynamicky typ Text
objekty[1] = new Obrazok(); // dynamicky typ Obrazok
}

public void prekresli()


{
for (Zobrazitelny o:objekty)
if (o != null)
o.zobraz(2);
}
}

Zabezpečenie polymorfizmu
- deklarovaná premenná je typu rozhranie - jej statický typ je typu rozhranie
- pri kompilácii programu je známy iba statický typ premennej
- premenná typu rozhranie je typovo kompatibilná (vzhľadom na priradenie) s
objektami všetkých tried, ktoré rozhranie implementujú  premennej typu
rozhranie možno priradiť referenciu na objekt ľubovoľnej triedy, ktorá
implemetuje rozhranie
- dynamický typ premennej zodpovedá typu objektu, referenciu na ktorý
premenná aktuálne obsahuje
- dynamický typ premennej sa určuje počas behu programu
- statický aj dynamický typ premennej možno pomocou operátora
instanceof

public interface Zobrazitelny


public class Text implements Zobrazitelny
public class Obrazok implements Zobrazitelny

Zobrazitelny p1; //statický typ p1 - Zobrazitelny


Zobrazitelny p2; //statický typ p2 - Zobrazitelny
p1 = new Text(); //dynamický typ p1 - Text
p2 = new Obrazok(); //dynamický typ p2 - Obrazok

Výraz Hodnota
p1 instanceof Zobrazitelny true
p1 instanceof Text true
p1 instanceof Obrazok false
p1.getClass().getName() “Text”
p2 instanceof Zobrazitelny true
p2 instanceof Text false
p2 instanceof Obrazok true
p2.getClass().getName() “Obrazok”

- po prijatí správy je vykonaná metóda, zodpovedajúca aktuálnemu


(dynamickému) typu premennej – polymorfizmus (mechanizmus včasnej
vs. neskorej väzby – Early vs. Late Binding)
Príklad – riešenie úlohy zadanej v úvode predchádzajúcej prednášky:

public interface Obrazec


{
double obsah();
double obvod();
}

public class Kruh implements Obrazec


{
private double polomer;
public Kruh(double paPolomer) //Konštruktor
{
polomer = paPolomer;
}
public double obsah()
{
return Math.PI*polomer*polomer;
}
public double obvod()
{
return 2*Math.PI*polomer;
}
public String toString()
{
return String.format("Kruh (r=%5.2f)",polomer);
}
}

public class Obdlznik implements Obrazec


{
private double strana1;
private double strana2;
public Obdlznik(double paStrana1, double paStrana2) //Konštruktor
{
strana1 = paStrana1;
strana2 = paStrana2;
}
public double obsah()
{
return strana1*strana2;
}
public double obvod()
{
return 2*(strana1+strana2);
}
public String toString()
{
return String.format("Obdlznik (a=%5.2f, b=%5.2f)",strana1,strana2);
}
}

public class Stvorec implements Obrazec


{
private double strana;
public Stvorec(double paStrana) //Konštruktor
{
strana = paStrana;
}
public double obsah()
{
return strana*strana;
}
public double obvod()
{
return 4*strana;
}
public String toString()
{
return String.format("Stvorec (a=%5.2f)",strana);
}
}

public class Objednavka


{
public static final double CENA_MATERIALU = 9.43; // cena za m2
public static final double CENA_REZANIA = 0.48; // cena za 1 bm
private Obrazec [] polozky;
private int pocet;

public Objednavka(int maxPocet) // Konštruktor


{
polozky = new Obrazec [maxPocet];
pocet = 0;
}
public boolean vloz(Obrazec obrazec)
{
if ( pocet == polozky.length ) return false;
polozky[pocet] = obrazec;
pocet++;
return true;
}
public double spotrebaMaterialu()
{
double spotreba = 0;
for (int i=0; i<pocet; i++)
spotreba += polozky[i].obsah(); // polymorfizmus
return spotreba;
}
public double dlzkaRezu()
{
double dlzka = 0;
for (Obrazec o: polozky) // cyklus foreach
if (o != null)
dlzka += o.obvod(); // polymorfizmus
return dlzka;
}
public String toString()
{
String pom = "Zoznam dielcov\n";
for (int i=0; i<pocet; i++)
pom = pom+(i+1)+". "+polozky[i]+"\n";
return pom;
}
}

public class Pogram


{
public static void main(String [] args)
{
Objednavka zoznam = new Objednavka(100);

// valec dlzky 2 m, polomer 0.2 m, z oboch stran uzavrety


zoznam.vloz(new Kruh(0.2)); // dno
zoznam.vloz(new Kruh(0.2)); // vrchnak
zoznam.vloz(new Obdlznik(2.0, 2*Math.PI*0.2)); // bočný plášť

// hranol, dno – stvorec s hranou 0.5 m, vyska 1 m, bez vrchnaka


zoznam.vloz(new Stvorec(0.5)); // dno
zoznam.vloz(new Obdlznik(1.0, 4*0.5)); // bocny plast

double spotreba = zoznam.spotrebaMaterialu();


double rez = zoznam.dlzkaRezu();
double cenaMaterial = spotreba*Objednavka.CENA_MATERIALU;
double cenaRez = rez*Objednavka.CENA_REZANIA;

System.out.println("\fOBJEDNAVKA");
System.out.println(zoznam);
System.out.printf("%30s %6.2f m2 (%5.2f EUR/m2)\n",
"Celkova spotreba materialu =",spotreba,
Objednavka.CENA_MATERIALU);
System.out.printf("%30s %6.2f m (%5.2f EUR/m)\n",
"Celkova dlzka rezu =", rez,
Objednavka.CENA_REZANIA);
System.out.printf("%30s %6.2f EUR \n",
"Cena za material =",cenaMaterial);
System.out.printf("%30s %6.2f EUR \n",
"Cena za rezanie materialu =", cenaRez);
System.out.printf("%30s %6.2f EUR \n",
"Celkova cena objednavky =",
cenaMaterial + cenaRez);
}
}

Výstup programu:
OBJEDNAVKA
Zoznam dielcov
1. Kruh (r= 0.20)
2. Kruh (r= 0.20)
3. Obdlznik (a= 2.00, b= 1.26)
4. Stvorec (a= 0.50)
5. Obdlznik (a= 1.00, b= 2.00)

Celkova spotreba materialu = 5.01 m2 ( 9.43 EUR/m2)


Celkova dlzka rezu = 17.03 m ( 0.48 EUR/m)
Cena za material = 47.29 EUR
Cena za rezanie materialu = 8.17 EUR
Celkova cena objednavky = 55.46 EUR

Dedičnosť
Situácia:
1. Objekty triedy T1 majú podobné vlastnosti (atribúty, metódy) ako objekty
triedy T2. Pri deklarácii tried by sa v oboch triedach opakovali časti kódu v
nezmenenom tvare (Študent, Učiteľ – vlastnosti: meno, priezvisko,
dátum narodenia ).
2. Deklarované triedy majú známky hierarchického vzťahu medzi sebou
navzájom – objekty triedy S možno súčasne považovať aj za špecifický
prípad objektov typu T (Šofér  Zamestnanec, Termínovaný účet 
Bankový účet, Osobný automobil  Automobil, …)

Požiadavky pri návrhu tried


- re-use – znovapoužiteľnosť kódu
- snaha o ľahkú udržiavateľnosť kódu

Možnosti praktického riešenia uvedených situácií


- kompozícia – modelovanie vzťahu „MÁ“ - „HAS A“
- využitie dedičnosti (inheritance) – modelovanie vzťahu „JE“ – „IS A“
- !!! nebezpečenstvo zneužitia dedičnosti
Kedy použiť dedičnosť
Liskovej substitučný princíp (Liskov substitution
principle - LSP) - definícia relácie dedičnosti
(subtyping relation). Prvýkrát uvedený v roku 1987.
Formálna definícia z roku 1994 - Barbara Liskov
(*1939), Jeannette Marie Wing (* ????):

Let q(x) be a property provable about objects x of type T. Then q(y) should be true for
objects y of type S where S is a subtype of T.

Nech q(x) je vlastnosť dokázateľná (platiaca) pre objekty x typu T. Potom q(y) musí byť
splnená (pravdivá) pre objekty y typu S, kde S je subtyp (potomok) typu T.

(http://en.wikipedia.org/wiki/Liskov_substitution_principle):
LSP je zároveň definíciou princípu zastupiteľnosti (substitutability) objektov (objektových
premenných):

If S is a subtype of T, then objects of type T may be replaced with objects of type S.

Ak S je potomok (subtyp) T, potom objekty typu T môžu byť v programe nahradené


objektami typu S bez zmeny požadovaných vlastností programu (správnosť, …).

Dedičnosť:
- možnosť deklarovať triedy, ktoré sú v hierarchickom vzťahu predok  potomok
o predok – nadtyp, supertyp, priamy predok, absolútny predok, …
o potomok – podtyp, subtyp, priamy potomok, absolútny potomok, …
- potomok dedí od svojho predka (v závislosti od modifikátora dostupnosti)
o vnútorný pohľad
 atribúty
 metódy
o vonkajší pohľad – verejné rozhranie (správy)
- !!! potomok nededí konštruktory

Dedičnosť v jazyku JAVA

Deklarácia triedy
[....] class Trieda extends TriedaPredka [implements Rozhr1 [,...]]
{
[ // deklarácia atribútov ]
[ // deklarácia konštruktorov ]
// deklarácia metód
}

extends TriedaPredka – deklarovaná trieda Trieda je potomkom triedy TriedaPredka


(rozširuje triedu TriedaPredka)
!!! Java umožňuje iba jednoduchú dedičnosť – potomok môže mať iba
jedného predka !!!

- Potomok Trieda zdedí od predka TriedaPredka všetky atribúty, dostupné v nej budú
však iba atribúty public a protected
- V triede potomka možno deklarovať ďalšie nové atribúty
- Trieda potomka zdedí od triedy predka všetky metódy, ktoré sú public alebo
protected. Trieda môže tieto metódy
o ignorovať – metódy vôbec nie sú využívané
o využívať bez zmeny
o predefinovať (prekryť) – definovať novú funkcionalitu metódy, rozhranie metódy sa
nemení (trieda má rovnakú hlavičku)
- navyše môžu byť v triede potomka definované nové metódy
- Potomok nededí konštruktor (konštruktory) predka
o Potomok musí mať definovaný vlastný konštruktor (konštruktory)
o Každý konštruktor potomka musí mať vo svojom tele ako prvý príkaz príkaz pre
vyvolanie konštruktora predka v tvare

super(zoznam_skutočných_parametrov_konštruktora);

Dostupnosť v
Modifikátor triede balíčku u potomka všade
public Áno Áno Áno Áno
protected Áno Áno Áno Nie
--- (friendly) Áno Áno Nie Nie
private Áno Nie Nie Nie

Príklad:

public class Vozidlo


{
private int maxRychl;

public Vozidlo(int paRychl)


{
maxRychl = paRychl;
}

public int maximalnaRychlost()


{
return maxRychl;
}
}

public class Pasove extends Vozidlo


{
private int pocClankov;

public Pasove(int paMaxRychl, int paPocClankov)


{
super(paMaxRychl); // volanie konstruktora predka
pocClankov = paPocClankov;
}

public int pocetClankovPasu()


{
return pocClankov;
}
}

public class Kolesove extends Vozidlo


{
private int pocKolies;

public Kolesove(int paMaxRychl, int paPocKolies)


{
super(paMaxRychl); // volanie konstruktora predka
pocKolies = paPocKolies;
}

public int pocetKolies()


{
return pocKolies;
}
}

public class Osobne extends Kolesove

{
private int pocOsob;

public Osobne(int rych, int kolies, int paPocOsob)


{
super(rych,kolies);
pocOsob = paPocOsob;
}

public int pocetPrepravovanychOsob()


{
return pocOsob;
}
}

public class Nakladne extends Kolesove


{
private int naklad;

public Nakladne(int rychl, int kolies, int paNaklad)


{
super (rychl,kolies);
naklad = paNaklad;
}

public int prepravovanyNaklad()


{
return naklad;
}
}

public class DopravnyPodnik


{
private Vozidlo [] vozovyPark;
private int pocVoz;
public DopravnyPodnik(int maxPocetVozidiel)
{
vozovyPark = new Vozidlo[maxPocetVozidiel];
pocVoz = 0;
}
public int pocetVozidiel()
{
return pocVoz;
}
public Vozidlo vozidlo(int poradie)
{
if ((poradie<0) || (poradie>=pocVoz)) return null;
return vozovyPark[poradie];
}

public boolean pridajVozidlo(Vozidlo voz)


{
if (vozovyPark.length == pocVoz) return false;
vozovyPark[pocVoz] = voz;
pocVoz++;
return true;
}
public int prepravnaKapacita() // kolko osob mozno naraz prepravit
{
int kapacita = 0;
for (int i=0; i<pocVoz; i++)
if (vozovyPark[i] instanceof Osobne)
kapacita += ((Osobne) vozovyPark[i]).pocetPrepravovanychOsob();

return kapacita;
}

public int dopravnaKapacita() // kolko kg nakladu mozno naraz prepravit


{
int kapacita = 0;
for (int i=0; i<pocVoz; i++)
if (vozovyPark[i] instanceof Nakladne)
kapacita += ((Nakladne) vozovyPark[i]).prepravovanyNaklad();
return kapacita;
}

public class Program


{
public static void main()
{
DopravnyPodnik kovaTrans = new DopravnyPodnik(50);
kovaTrans.pridajVozidlo(new Osobne(130, 4, 5));
// osobne vozidlo
kovaTrans.pridajVozidlo(new Osobne(130, 4, 9));
// mikrobus
kovaTrans.pridajVozidlo(new Osobne(130, 4, 9));
// mikrobus
kovaTrans.pridajVozidlo(new Nakladne(130, 4, 3000));
// dodavka
kovaTrans.pridajVozidlo(new Nakladne(130, 4, 3000));
// dodavka
kovaTrans.pridajVozidlo(new Nakladne(100, 6, 7000));
// lahke nakladne auto
kovaTrans.pridajVozidlo(new Nakladne(90, 10, 10000));
// tazke nakladne auto

System.out.print("\f");
System.out.println("Firma *** kovaTrans s.r.o. ***");
System.out.println("Firma vlastni "+
kovaTrans.pocetVozidiel()+" vozidiel");
System.out.println("Prepravna kapacita firmy: "
+kovaTrans.prepravnaKapacita()+" osob");
System.out.println("Dopravna kapacita firmy: "
+kovaTrans.dopravnaKapacita()+" kg nakladu");
}
}

Výstup programu:

Firma *** kovaTrans s.r.o. ***


Firma vlastni 7 vozidiel
Prepravna kapacita firmy: 23 osob
Dopravna kapacita firmy: 23000 kg nakladu

You might also like