1. fejezet, Objektum orientált programozás
Az Objektum orientált programozás lényegét gyűjtöttem össze ebben a jegyzetfüzetben. Nem etalon az OOP-hez, csupán egy jegyzetfüzet, amivel hatékonyan megértheted az OOP-t. Pascal, PHP, Java és Ruby nyelveken keresztül próbáljuk szemléltetni az egyes meghatározásokat.
Az OOP gyakorlatilag a Darwinizmushoz hasonló, ezért az élővilágból veszünk példákat. Így, ha már tanultál biológiát, könnyen megérted az osztályok, az ős és leszármazott fogalmát, az öröklődést, az objektumokat, a tulajdonságokat és azok láthatóságát, a metódusokat, a polimorfizmust, az egységbe zárást és az absztrakciót. Ide sorolom még az interfészek (szabad fordításban felületek) megismerését is, ami gyakran az OOP nyelvek része. Az OOP-hez gyakran alkalmazzák még a Unified Modeling Language diagrammokat, röviden az UML modellezést.
Ha a törzsfejlődést vesszük alapul, elsőként a tudósok a látható különbségek alapján rendszertanilag elkülönítették az állatokat, növényeket és gombákat. Ezeket nevezzük programozásban osztályoknak. Ezek egyetlen osztály leszármazottjai, amelyet élőlények gyűjtőnéven nevezünk. Az élőlények osztályt ősosztálynak tekinthetjük, ami minden élő szervezet őse. Léteznek olyan programnyelvek, ahol több őse is lehet egy osztálynak, ám a gyakorlat azt mutatja, hogy egy ősosztály alkalmazásával megoldható a világ modellezése. Az élőlények osztály egy absztrakt metódust tartalmazzon az élőlény színére vonatkozólag. Ennek visszatérési értéke bármilyen szöveges megjegyzés lehet, de a kiolvasására használjunk virtuális és egyben absztrakt metódust. Az absztrakt jelölés olyan metódus deklaráció, amelynek nincs implementációja az adott osztályban. Ezért magát az osztályt is absztraktnak kell jelölnünk. Az absztrakt osztályok nem példányosíthatóak, mert van absztrakt, meg nem valósított metódusuk. Ennek jelentőségét - a késői kötést (late binding vagy dynamic binding) és a polimorfizmust - később tárgyaljuk.
Fókuszáljunk kicsit a gombákra. A gombák közül ismerjünk fel toxikus és ehető gombákat. A toxikus jelleg legyen egy osztály tulajdonság a gombák osztályban, alapértelmezettként None (üres) értéket adjunk neki. A mérgező gombákat leszármaztatva ezt az értéket felüldefiniáljuk a termelt méreg megnevezésével. Az osztály konstans olyan érték, amelynek olvasásakor minden példány, minden objektum ugyan azt az értéket adja vissza, és az nem változtatható meg semmilyen úton-módon, csak leszármaztatással. Az egyes nyelvi megvalósításokban ezt az értéket példány létrehozása nélkül is kiolvashatjuk az osztály nevének megadása után. Sőt, léteznek statikus metódusok is, amelyek statikus állandókkal és lokális változókkal vagy paraméterekkel dolgozhatnak. Erre később nézünk példát. Most inkább határozzuk meg az objektumok és metódusok fogalmát.
Hogy valóban ehető-e egy gomba, azt a környezetében található toxikus gombák befolyásolják. Ezért a fogyaszthatóság értéke legyen egy tulajdonsága a gombák osztályában. Ez már példányonként változó érték, de legyen rejtett, egy metóduson keresztül olvasható és egy másikon keresztül változtatható (getter/setter). A módosító metódusnak adjuk paraméterül a környező gombák dinamikus tömbjét, vagy láncolt listáját, amin végigszaladva megállapíthatja magáról, hogy fogyasztható-e. Hogy megemlítést nyert, mindjárt nézzük is meg a metódus és tulajdonság fogalmát.
A tulajdonságokat a procedurális nyelvekben globális és lokális változók testesítik meg. Az OOP-ben az objektumok tulajdonsgai tárolják ezeket az értékeket. Minden objektumnak más és más értékű tulajdonságai lehetnek. Külső elérésük az objektum metódusain keresztül (ritkább esetben közvetlenül a tulajdonság olvasásával) történik. Vagy közvetlenül, vagy metódusokon keresztül olvashatók és írhatók, ám az OOP-ban a tulajdonságok elrejtésének elve (egységbe zárás) szerint a tulajdonságokat csak metódusokon keresztül kezelhetnénk. A különböző nyelvi megvalósítások szabadabban értelmezik ezt a tervezési és megvalósítási óvatosságot, és megengedik a publikus tulajdonságok létrehozását is.
A procedurális nyelvekben függvényeket és eljárásokat használva foghatunk egybe, alakíthatunk többször felhasználhatóvá egy-egy programrészt, probléma megoldást. Az OOP-ban ezeket az osztályok metódusaiban tehetjük meg, amelyeknek lehetnek visszatérési értéke is, és több előnyös tulajdonságai is. Ilyen például a final (Java) és a virtual (OO Pascal) metódus jellemzők. A final meghatározza, hogy leszármaztatott osztály nem definiálhatja újra az adott metódust, az változtathatatlanul marad minden leszármaztatottban. Finalként deklarálhatunk az objektumoknak tulajdonságot is, hasonló hatással. A virtual később játszik szerepet a leszármaztatások során, egyes nyelvi megvalósításokban teljesen el is hagyták ez a kulcsszót. Ezeket szintén tárgyaljuk majd a késői kötés kapcsán, és itt nézzük meg majd az override és overload jellemzőket is. Most elégedjünk meg azzal, hogy két speciális metódust kiemelünk még. Ezek a konstruktor és destruktor metódusok.
Az osztályokból megteremtődött egyedeket objektumoknak nevezzük az OOP-ban. Az osztályból objektumot gyártó metódust konstruktornak nevezzük. Itt inicializálhatók az objektum alapértelmezett tulajdonságai, létrehozhatók olyan objektumok is, amiket később használni fogunk (pl.: dinamikus tömbök, láncolt listák, fa szerkezetek). A konstruktor fogadhat paramétereket is, amiknek az értékeit az objektum tulajdonságainak beállítására használjuk, vagy számítás alapjául szolgálhatnak. Visszatérési értéke az osztályból előállított objektum. Nyelvi megvalósítástól függően egy kulcsszó előzi meg, ami többnyire a new, azaz új.
Az objektum tulajdonságok, állapot leírók elrejtését egységbe zárásnak nevezik.
Az objektumok megsemmisülését az osztály destruktor metódusa végzi. Itt szüntethetjük meg a konstruktorban vagy más metódusokban létrejött, egységbe zárt objektumokat, és például itt zárhatunk le adatbázis kapcsolatokat is. Ezen kívül a destruktor metódusnak nincs paramétere, se visszatérési értéke.
Minden objektumban az osztály és az ősosztályok tulajdonságait és metódusait érhetjük el, láthatóságától függően. A láthatóság az OOP egyik erőssége. Tulajdonságokat és metódusokat egy objektumban láthatóvá (public), részben láthatóvá (protected), és láthatatlanná tehetünk (private). Ezeket az elrejtéseket szintén később tárgyaljuk, most koncentráljunk az objektum fogalmára. Az objektumok az osztály jegyeit hordozzák magukban. Ezek közül egyesek változtathatóak, másak állandóak, mint ahogy azt az előbbiekben láthattuk. A változtathatatlanok az osztály konstansaiban testesülnek meg. A változtathatóakat az osztály tulajdonságaiban, a számításokból eredményt visszaadó, vagy állapot lekérdező metódusaiban láthatjuk meg.
Nézzük az állatok osztályának leszármazottait. Ugorjunk egy nagyot a törzsfejlődésben és nézzük most az emlősöket, ezen belül a sertéseket. Definiáljuk a korábban említett absztrakt és virtuális szín metódust, és vegyük az értékét egy tulajdonságból. Észrevettük-e, hogy a sertés színe születésétől fogva állandó értéket vesz fel, de mégsem tehetjük osztály tulajdonságba az értéket? Ezért ennek a tulajdonságnak egységbe zártnak, rejtettnek kell lennie, és egy kívülről látható, csak olvasásra használt metódust kell definiálnunk rá, továbbá az objektum konstruktorában kell megadnunk a szín értékét. Ez a paraméter határozza meg későbbiekben a sertés színét, vagy ha részletesebben leírjuk, a mintáját.
Ha áttérünk a hüllők osztályán belül a kaméleonra, akkor értelme lesz a színt idő során módosító metódusnak is. Ezt alkalmazhatjuk a növényeknél is, tehát valahol meg kell jelennie az absztrakt és virtuális író metódusnak. De vajon hol? Nos, ez a két osztály (növény, kaméleon) metódusaként jelenik meg, az absztrakt metódus felülírásával (override). Mint az a sertéseknél érintőlegesen említést nyert, részletesebb leírást is adhatunk a szín mellett. Ha mintát is hozzáteszünk a színt meghatározó konstruktor vagy metódus paraméteréhez, akkor azt túlterhelt (overload) metódusnak nevezzük. Ugyan az a neve, csak mások a paraméterei. Ezt OO Pascal-ban külön jelölni is kell (mi mással, mint az overload kulcsszóval).
A polimorfizmus (többalakúság) már az absztrakt és virtuális metódusok felülírásával függ össze. A bevezetőben az élőlények osztályánál deklaráltunk egy szín olvasó absztrakt és virtuális metódust. Ha a leszármaztatott osztályokból létrehozunk objektumokat (pl.: gomba, sertés, növény, kaméleon), akkor az ős osztályban deklarált metóduson keresztül is lekérdezhetjük a szín vagy a fogyaszthatóság értéket. Pontosan ez úgy történik, hogy a leszármaztatott osztályból a konstruktorral létrehozott és attól visszakapott objektumot egy ős osztályú (típusú) változóba tároljuk (élőlények), majd a változó által elérhető, szín lekérdező metódust hívjuk meg. Mivel a leszármaztatott objektumok már mind egyedileg megvalósítják (implementálják) ezt a metódust, az ős osztályú változóból meghívható az ott absztraktként és virtuálisként megjelölt metódus. Minden visszaadott érték az objektum osztályának megfelelően megvalósított válasz lesz. Nézzünk egy példát rá. Egy élőlények osztályú változóba (legyen neve: "a"), egy kaméleon osztályból készült objektumot tárolunk. Mikor az "a" változó színt lekérő metódusát (pl.: getColor()) hívjuk meg, a kaméleon színét lekérő metódus fogja vissza adni az éppen aktuális színt, ami a kaméleon hangulatától, környezetétől függ. Ez percről percre változhat, és akár mikor lekérjük, többnyire az előző állapottó eltérő eredményt kapunk. Ha egy sertés osztályból készítünk objektumot az "a" változóba, a szín lekérdezés a sertés objektum konstruktorában meghatározott, születésétől fogva állandó értéket fogja visszaadni. Tehát az "a" változó az összes leszármaztatott osztály színét vissza tudja adni, de a választ a benne tárolt objektum osztálya határozza meg. Ez a polimorfizmus lényege.
Szakmai haszna ennek óriási. A programkód újrahasznosítására, átláthatóságára rendkívül pozitív hatása van. Éppen ezért az 1970-es évektől egyre népszerűbb az objektum orientált (OO) szemlélet. (Történeti visszatekintés a wikipedia oldalán olvasható.)
Nézzünk még egy speciális, de gyakran előforduló esetet. Ha olyan metódust, tulajdonságot szeretnénk elérni az "a" változón keresztül, amit csak a leszármaztatottban implementáltunk, akkor azt közvetlenül nem tudjuk elérni. Itt jelenik meg a típus kényszerítés. Ennek lényege, hogy az "a" objektumot egy leszármaztatott (mondjuk kaméleon) osztályúvá kényszerítjük, ami innentől mint leszármaztatott osztályú (típusú) változó működik, és elérhetővé válnak annak a metódusai, tulajdonságai. Ehhez tudni kell, hogy az "a" változóban valóban a kaméleony osztálynak megfelelő objektum van (pl.: Java-ban az instanceof kulcsszó), máskülönben tipuskényszerítési kivétel generálódik, roszabb esetben régebbi nyelveknél a gép memóriájának olyan területéről olvasunk, vagy még roszabb esetben olyan területére íruk (értsd: felülírunk!), ami az objektumtól teljesen független (lásd még: VMT). Ez katasztrófákhoz vezetett előbb utóbb, így az újabb generációs nyelvi megvalósítások különös figyelmet szenteltek ennek kizárására.
Röviden, tömören ennyi volt. A gyakorlati pédákat mindenkép nézzük meg, könnyebb megérteni ezt a velős megfogalmazást.
- A hozzászóláshoz be kell jelentkezni