
Visitor PatternZadanieDáta, s ktorými pracujeme, sú popísané v niekoľkých triedach. Vzťahy medzi dátovými triedami môžu byť zložité, napríklad jedna trieda je odvodená od druhej, alebo jeden objekt obsahuje iné objekty. Na týchto dátach chceme vykonať niekoľko rôznych činností; napríklad skopírovať, uložiť do súboru, vypísať prehľadne na obrazovku, urobiť výpočet, atď. Niektoré činnosti môžu byť takisto odvodené od iných. Aby sme udržali program pekne rozdelený, tieto činnosti by nemali byť napísané v samotných dátach. Ďalej chceme, aby pridanie novej činnosti nevyžadovalo ďalšiu zmenu v dátach. PoznámkaChceme, aby sa dynamicky volala správna metóda podľa toho, (1) akú činnosť chceme urobiť, a (2) na akom objekte ju chceme urobiť. Toto sa nazýva "double dispatch". Väčšina objektovo orientovaných jazykov podporuje dynamické volanie iba podľa jedného objektu. Napríklad:
class Data {
...
}
class Odvodene_Data extends Data {
...
}
interface Cinnost {
void spracuj(Data data);
void spracuj(Odvodene_Data data);
}
class Jednoducha_Cinnost implements Cinnost {
void spracuj(Data data) {
System.out.println("Jednoducho spracovavam data.");
}
void spracuj(Odvodene_Data data) {
System.out.println("Jednoducho spracovavam odvodene data.");
}
}
class Zlozita_Cinnost extends Jednoducha_Cinnost {
void spracuj(Data data) {
System.out.println("Zlozito spracovavam data.");
}
void spracuj(Odvodene_Data data) {
System.out.println("Zlozito spracovavam odvodene data.");
}
}
class Hlavny_Program {
public static void main() {
Cinnost c1 = new Jednoducha_Cinnost();
Cinnost c2 = new Zlozita_Cinnost();
Data d1 = new Data();
Data d2 = new Odvodene_Data();
c1.spracuj(d1); // spravne: Jednoducho spracovavam data.
c1.spracuj(d2); // NESPRAVNE: Jednoducho spracovavam data.
c2.spracuj(d1); // spravne: Zlozito spracovavam data.
c2.spracuj(d2); // NESPRAVNE: Zlozito spracovavam data.
}
}
V tomto príklade sa volaná metóda dynamicky vyberie podľa typu činnosti. Hoci sú premenné "c1" a "c2" rovnakého typu "Cinnost", metóda sa vyberie podľa toho, aký objekt sa v danej premennej nachádza v čase volania; a keďže v jednej je "Jednoducha_Cinnost" a v druhej "Zlozita_Cinnost", volajú sa metódy príslušnej triedy. Pri výbere jednej z dvoch možných funkcií "spracuj" sa však zohľadňuje iba typ premennej; keďže premenná "d2" je typu "Data", zavolá sa metóda pre "Data", hoci v premenej sa práve nachádza objekt typu "Odvodene_Data". Ak by sme program zmenili tak, že nie činnosti by mali funkciu "spracuj" s parametrom dáta, ale naopak dáta by mali funkciu "spracuj_ma" s parametrom činnosť, dostali by sme opačnú situáciu. Výber metódy by dynamicky závisel od dát, ale staticky od činnosti. (Navyše, toto robiť nechceme, lebo nechceme pri pridávaní novej činnosti meniť kód pre dáta.) RiešenieAby sme zabezpečili dynamické volanie funkcie aj podľa typu dát, doplníme do dát pomocnú metódu "spracuj_ma", ktorá bude volať dynamicky príslušnú funkciu rozhrania "Cinnost". Keďže metóda sa týka rozhrania a nie triedy činnosti, pridávame ju iba raz, bez ohľadu na počet činností.
class Data {
void spracuj_ma(Cinnost cinnost) {
cinnost.spracuj(this);
}
...
}
class Odvodene_Data extends Data {
void spracuj_ma(Cinnost cinnost) {
cinnost.spracuj(this);
}
...
}
interface Cinnost {
void spracuj(Data data);
void spracuj(Odvodene_Data data);
}
class Jednoducha_Cinnost implements Cinnost {
void spracuj(Data data) {
System.out.println("Jednoducho spracovavam data.");
}
void spracuj(Odvodene_Data data) {
System.out.println("Jednoducho spracovavam odvodene data.");
}
}
class Zlozita_Cinnost extends Jednoducha_Cinnost {
void spracuj(Data data) {
System.out.println("Zlozito spracovavam data.");
}
void spracuj(Odvodene_Data data) {
System.out.println("Zlozito spracovavam odvodene data.");
}
}
class Hlavny_Program {
public static void main() {
Cinnost c1 = new Jednoducha_Cinnost();
Cinnost c2 = new Zlozita_Cinnost();
Data d1 = new Data();
Data d2 = new Odvodene_Data();
d1.spracuj_ma(c1); // spravne: Jednoducho spracovavam data.
d2.spracuj_ma(c1); // spravne: Jednoducho spracovavam odvodene data.
d1.spracuj_ma(c2); // spravne: Zlozito spracovavam data.
d2.spracuj_ma(c2); // spravne: Zlozito spracovavam odvodene data.
}
}
Toto je základná myšlienka, zvyšok sú technické vylepšenia. Ak chceme, aby funkcie vracali aj nejaké výsledky alebo hádzali výnimky, použijeme ako hlavičku: Object spracuj_ma(Cinnost cinnost) throws Exception; Object spracuj(... data) throws Exception; |
Viliam Búr [sk] domáca stránka (feed) viliam@bur.sk ICQ: 133571943 Blog: JavaScript pre začiatočníkov (3) JavaScript pre začiatočníkov (2) Linky: Sponzorované odkazy: |