Személyes eszközök
Keresés

 

A InfoWiki wikiből



Tartalomjegyzék

Késői kötés

Az előző fejezet végén felvetett problémára nincs jó megoldás. Amennyiben az ős osztályban van olyan metódus, amely más metódusokat meghív, akkor korai kötés mellett ezek a hívások sosem fogják a gyerekosztályban esetleg újradefiniált, új verziójú metódusokat használni még akkor sem, ha ezen gyermekosztályból készítünk példányokat.

A problémát úgy is fel lehet fogni, hogy nem figyelmeztettük a fordítóprogramot arra, hogy a szóban forgó metódusokból később újabb verziók is keletkezhetnek. Ennek következtében a fordító a korai kötést használja, amely nem elég rugalmas a gyermekosztálybeli továbbfejlesztések kezelését illetően.

Jelzést kell adnunk a fordítóprogramnak, hogy valamely metódust ilyen típusúnak kezelje. Az alapértelmezett kezelés a korai kötés, mint az eddigi példák mutatták. A másik kezelési technika a késői kötés, de ennek alkalmazásához a metódust meg kell jelölni.

Virtuális metódusok

Amennyiben olyan metódus írásába kezdünk, amely

  • a gyermekosztályokban valószínűleg felülírásra kerül
  • más metódusok által meghívásra kerül, és van esélye hogy a gyermekosztályokban felülírásra kerül

ezeket már az ősosztályban is meg kell jelölni a virtual kulcsszóval.

class TRepulo
{
   virtual public void Felszall()  { ... }
   virtual public void Leszall()   { ... }
   public void Repul()     { ... }
 
   public void Gyakorlokor()
   {
     Felszall();
     Repul();
     Leszall();
   }
}

Amennyiben a fordítóprogram ilyen kulcsszóval megjelölt metódus hívására bukkan, akkor nem korai kötéssel oldja fel a metódus-hozzárendelést, hanem ezt a problémát "elhalasztja". Egy olyan kódot generál a metódushívás helyére, amely majd csak futás közben, amikor konkrétan ezt a pontot éri el a program, csak akkor dönti el, hogy melyik konkrét metódus hívódjon meg.

Az ilyen metódusokat a gyermekosztályban jogunk van felüldefiniálni, de nem alkalmazhatjuk a 'new' kulcsszót. Ha mégis megpróbálnánk, akkor a fordítóprogram hibát jelezne, mivel a 'new' kulcsszó a korai kötést is jelöli egyben. Helyette az 'override' kulcsszót kell használni:


class THelikopter:TRepulo
{
   override public void Felszall() { ... }
   override public void Leszall() { ... }
}

A fenti kód fordítása során a 'Gyakorlokor()' metódus belseje a legérdekesebb:

{
   Felszall()  -> ?? késői kötés ??
   Repul()     -> TRepulo -beli Repul()
   Leszall()   -> ?? késői kötés ??
}

Működés közben

Az alábbi kód próbálja használni a fenti osztályokat:

class FoProgram
{
   public static void Main()
   {
      TRepulo f15 = new TRepulo();
      f15.Gyakorlokor(); 
      THelikopter apache = new THelikopter();
      apache.Gyakorlokor();
   }
}

Az 'f15' példány esetén az alábbi történik:

f15.Gyakorlokor() -> TRepulo -beli Gyakorlokor()
{
  Felszall()  -> ?? késői kötés ?? -> f15 típusa szerint 
                    -> TRepulo -beli Felszall()
  Repul()     -> TRepulo -beli Repul()
  Leszall()   -> ?? késői kötés ?? -> f15 típusa szerint 
                    -> TRepulo -beli Leszall()
}

Az 'apache' esetén pedig:

apache.Gyakorlokor() -> TRepulo -beli Gyakorlokor()
{
  Felszall()  -> ?? késői kötés ?? -> apache típusa szerint 
                      -> THelikopter -beli Felszall()
  Repul()     -> TRepulo -beli Repul()
  Leszall()   -> ?? késői kötés ?? -> apache típusa szerint 
                      -> THelikopter -beli Leszall()
}

A fenti két esetet alaposabban megvizsgálva azt tapasztalhatjuk, hogy a TRepulo-beli metódushívások alkalmazkodnak az adott futásközbeni szituációhoz - a példány típusa szerinti legújabb metódus-változatok hívódnak meg. Ezt a tulajdonságát az O.O.P.-nek hívjuk sokalakúság-nak.

Szabályok

Amennyiben az ős osztályban egy metódust virtual-nak deklaráltunk, úgy a gyermek osztályban

  • nem használhatjuk a 'new' kulcsszót a felüldefiniálásra
  • kötelező használni az override kulcsszót

Ha egy virtuális metódust szeretnénk 'override' segítségével felüldefiniálni, úgy

  • nem változtathatjuk meg annak nevét, hiszen név alapján fogja azonosítani a fordító, melyik virtuális metódust is kívánjuk felüldefiniálni
  • nem változtathatjuk meg ezen metódus paraméterezését, hiszen az ős osztályból a régi paraméterezéssel fogjuk ezen új változatot is meghívni
  • amennyiben a metódus függvény, úgy nem változtathatjuk meg annak visszatérési típusát sem

Összefoglalóan tehát azt mondhatjuk el, hogy az 'override' metódusváltozatban semmit sem változtathatunk meg, csak annak törzsét.

Mivel a virtuális metódusok értelme, hogy azokat a gyerekosztályok felüldefiniálják, így a virtuális metódusok hatásköre nem lehet 'private'.


Virtuális property

Hasonlóan a metódusokhoz, property-k is lehetnek virtuálisak.

class Ember
{
    protected int _eletkor;
    public virtual int eletkor
    {
        get { return _eletkor; }
        set 
        { 
            if (value>=0) _eletkor = value;
            else throw new Exception("Hibas ertek!");
    }
    }
}

A gyermekosztályban jogunk van a property akár 'get' akár 'set', akár mindkét részét módosítani. Ha csak az egyik felét módosítjuk (pl. csak a 'get' részt), akkor a másik rész esetén az örökölt kerül be a kódba:

class GyermekEmber:Ember
{
    public override int eletkor
    {
        set 
        { 
            if (value>=0 && value<18) _eletkor = value;
            else throw new Exception("Hibas ertek!");
        }
    }
}

Amennyiben a property-t bevezető ős osztály nem dolgozta ki mindkét részt (get és set), úgy a gyermekosztály sem dolgozhatja ki a hiányzó részt!

class Computer
{
    public virtual string IP_Cime
    {
        get 
        { 
            return Operating_System.IP_Address;
        }
    }
}
// ---
class AdvComputer
{
    public override string IP_Cime
    {
        // HIBA: nem dolgozhat ki SET részt !!!
        set
        { 
            Operating_System.IP_Address = value;
        }
    }
}


Állítólag igaz történet

Objektum-orientált kód újrafelhasználása miatt érte egy kis kellemetlenség az ausztrál hadsereget.

Manapság ugye már egyre jobb helikopter-szimulátorokat csinálnak, amelyek szinte teljesen ugyanazt az élményt nyújtják, mint az igazi repülés. Domborzat, időjárás, növényzet: mind teljesen élethű. Az ausztrálok úgy gondolták, hogy az állatokat is be kellene rakni, mivel azok a menekülésükkel információt szolgáltathatnak az ellenségnek a környéken repülő helikopterről, például oly módon, hogy a zajtól csoportba verődő, nagy tömegben menekülő állatfalka futás közben port ver fel, ez messzire látszik, és elárulhatja az amúgy alacsonyan repülő helikopterek helyzetét.

A kutatás-fejlesztés főnöke üzent a programozóknak, hogy tegyenek a programba néhány kenguru-falkát is. A programozók öreg rókaként persze nem kezdtek vadul kódot írni, hanem elővettek egy már meglévő objektum-osztályt: a gyalogságot.

A menekülési algoritmus ugyanaz maradt, mindössze a bitmap képeket kellett lecserélni, a futás sebességét megnövelni, a mozgást ugrálóra alakítani és már készen is volt a kenguru-csapat.

Történt aztán egyszer, hogy amerikai katonák jöttek látogatóba az ausztrálokhoz. A helyi nagyfiúk persze egyből vakítani kezdtek az amerikaiaknak: mélyrepülésben húztak a nagy kenguru-nyáj felé, mire azok jól szétspricceltek. Az amerikai katonák elismerően bólogattak a mutatvány láttán... aztán döbbentek egy nagyot, amikor az egyik bokor mögül kinézett egy kenguru, majd előrántott egy méretes RPG rakétavetőt, vállára kapta, rövid célzás után kilőtte az egyik helikoptert.

A programozók ugyanis elfelejtették kivenni ezt a részt a kódból (az összes metódus öröklődött, köztük az 'ellenségesTámadásraReagálás(...)' rész is :).

A tanulság az ausztrál pogramozók számára az lett, hogy óvatosan kell bánni a kódok újrafelhasználásával, az amerikaiak számára pedig az, hogy az ausztrál vadvilág tényleg olyan veszélyes, mint ahogy azt beszélik.

A nagyfőnökök egyébként örültek az esetnek, mert a pilóták megtanulták a leckét: azóta mindegyikük szigorúan elkerüli a kengurukat.

Hernyák Zoltán
A lap eredeti címe: „http://wiki.ektf.hu/wiki/Mp3/ea08
Nézetek
nincs sb_18.191.171.235 cikk