Általános szoftver ismeretek
  A polimorfizmusról általában

Dupla polimorfizmus (double dispatch)



Dupla polimorfizmusról (double dispatch) akkor beszélünk, amikor

nem egy, hanem két objektum

aktuális típusa határozza meg, melyik függvény fog ténylegesen felhívódni.

A dupla polimorfizmus a többszörös polimorfizmus (multiple dispatch) egy speciális, a gyakorlat számára legfontosabb esete.

A probléma felvetése

Nézzük meg először is egy kicsit részletesebben, miről is van itt szó. Az 'egyszerű' polimorfizmusra az jellemző (amint például
itt részletesen bemutattuk), hogy a kliens oldalon felhívjuk egy kiszolgáló objektum valamely metódusát:

  static void HarcostMűködtet(Harcos H){
    H.Védekezik();
  }

és attól függően, hogy a kiszolgálónak mi a tényleges típusa, más és más fog történni:

  Harcos h1 = new Harcos();
  Harcos h2 = new GyávaHarcos();

  HarcostMűködtet(h1); // kimenet: Lövöldözöm midhalálig!
  HarcostMűködtet(h2); // kimenet: Fejvesztve menekülök!

Amint jobban megnézzük a mintapéldát, feltűnhet, hogy egy kicsit túl van egyszerűsítve (mint a bevezető mintapéldák általában): Mindegyik Harcos mindig egyformán védekezik, a körülményektől függetlenül.

Konkrétan például attól is függetlenül, hogy milyen fajta harcos támadta meg – ami nyilván nem életszerű. Ezzel a problémával találkoztunk már a függvénytúlterhelés kapcsán: ott láttunk egy mintapéldát, melyben a Harcos a támadó típusának függvényében választotta meg védekezési módszerét.

Most egy olyan függvényt szeretnénk írni, melynek két paramétere van: két szereplő, akik összecsapnak és hogy ebben a párharcban hogyan viselkednek, az mindkettőjük típusától függ:

  static void Összecsapás(Szereplő Egyik, Szereplő Másik){
     Egyik.Harcol(Másik);
  }

Például És így tovább.

Bármily egyszerűnek is látszik a dolog, hamar kiderül, hogy közvetlen, a nyelvbe beépített megoldást erre sem a Java, sem a C++ nem kínál!

Lássuk csak, miért is nem? Vegyünk egy konrét esetet:

  Szereplő sz1 = new Óriás();
  Szereplő sz2 = new Törpe();

  Összecsapás(sz1,sz2);

Az Összecsapás függvény belsejében felhívódik az első paraméter-objektum Harcol metódusa, mégpedig úgy, hogy megkapja paraméterként a függvény második paraméterét:

  static void Összecsapás(Szereplő Egyik, Szereplő Másik){
     Egyik.Harcol(Másik);
  }

Az első paraméter egy Óriás, tehát az ő valamelyik Harcol metódusa fog meghívódni (ha több van neki). De vajon melyik?

Tegyük fel, hogy az óriásnak három Harcol metódusa is van:

  class Óriás extends Szereplő{
     public void Harcol(Szereplő Ellenfél){...}
     public void Harcol(Óriás    Ellenfél){...}
     public void Harcol(Törpe    Ellenfél){...}
  }

Amikor ezt meglátjuk, elhamarkodottan megörülünk: Hát akkor minden rendben van! Létezik egy olyan metódus, amelynek a paramétere Törpe típusú, nyilván az fog majd felhívódni!

Korai az öröm. Nem az fog felhívódni, hanem az, amelyiknek a paramétere Szereplő típusú.

Tudniillik sajnos az, hogy melyik metódus fog felhívódni a három közül, már fordításkor eldől. Ezen a helyen mindkét paraméter Szereplőnek látszik, és a fordítóprogram ezt a döntést hozza:

Itt az Első paraméter-objektum Harcol nevű, Szereplő paraméterű metódusa hívódjon fel!

Hiába lesz majd futás közben a Másik nevű paraméter aktuális típusa éppen egy Törpe, ezt a kérdést a fordítóprogram nem hagyja nyitva. A virtuális függvénytábla segítségével futás közben majd az aktuális objektumtípus (Óriás) megfelelő metódusa hívódik fel – de a metódus paramétere szerint ezen a helyen már nem enged meg további elágazást a fordítóprogram. A 'metódus-tulajdonos' objektum típusa változhat, a paraméteréé viszont nem.

Legalábbis a Java és a C++ így működik. Működhetnének úgy is, hogy futás közben a hívás helyén megnézné a program, vajon van-e megfelelőbb másik metódusa is az Óriás osztálynak és akkor azt hívná fel – de nem így működnek. Valójában léteznek olyan programnyelvek, melyek támogatják a többszörös polimorfizmust, de ez a két nyelv nem tartozik közéjük.

Tehát az Összecsapás függvényben az Óriás osztálynak az a metódusa fog meghívódni, melynek paramétere egy általános Szereplő. Szeretnénk, ha a kliens függvény (Összecsapás) éppen ilyen egyszerű maradna, mint amilyen most – a kliens egyszerűsége a legfontosabb szempont, mindent a vevőért! – na de hogyan tudjuk ezt megvalósítani? Miképp tudjuk létrehozni a kétszeres polimorfizmust a háttérben úgy, hogy a kliensnek ehhez semmit se kelljen csinálnia?

Megoldási módok

Az egyik megoldási lehetőség a dinamikus polimorfizmus és a függvénytúlterhelés együttes alkalmazása.


A polimorfizmusról általában
  Általános szoftver ismeretek