Objektumorientált programozás

A Wikipédiából, a szabad lexikonból.

Az objektumorientált programozás (angolul Object Oriented Programming, röviden OOP) egy programozási módszer. Olyan programnyelv, amely lehetővé teszi különböző bonyolult változók (objektumok) létrehozását és kezelését. Amikor a programozók már kinőtték a klasszikus programozás kereteit, ki kellett gondolni egy új módszert a megnövekedett információtömeg kezelésére. Ezek lettek az objektumok.

Analízis szintű gondolkodás

Egy szoftver fejlesztésének korai fázisaiban a megvalósítandó rendszer feladatait szeretnénk feltérképezni, azok funkcionális és egyéb jellegű követelményeit. Más szóval, a kérdés ilyenkor az, hogy a rendszernek mit kellene tennie. Ilyenkor határozzuk meg a szoftver (formális és informális) specifikációját, majd abból kiindulva kezdjük magasabb szint absztrakciók segítségével előállítani a rendszer modelljét, amely a konkrét megvalósítás alapját fogja képezni.

Tervezés szintű gondolkodás

A meglévő modell alapján a szoftver konkrét implementációjához vezető utat tervezzük meg. Ilyenkor arra keressük a választ, hogy a meghatározott specifikációt hogyan valósítsa meg a rendszer. Ezen a szinten már képbe kerülnek különböző alacsony szintű technikák is, mint például a kommunikációs protokollok, programozási nyelvek és technológiák.

Tartalomjegyzék

[szerkesztés] Az OOP előnyei a szekvenciális nyelvekkel szemben

A hagyományos, ún. szekvenciális programozási nyelvekben az utasítások, sorról sorra, egymást követően hajtódnak végre. Ez annyit jelent, hogy a program egyes sorai egymásra épülnek: Ha az egyik sorban definiálunk egy változót, a következő sorban használhatjuk azt. Ezt az egyszerűséget megtöri az előre definiált rutinok és függvények használata, de még a ciklusokkal, illetve más vezérlési szerkezetekkel együtt is könnyen átlátható a forráskód struktúrája. Az ilyen nyelveknél ez felfogható előnyként is, azonban nagy projektekben kényelmetlenséget okozhat, hogy egy-egy alapvető algoritmust többször, többféleképpen is le kell írnunk, holott lényegi változtatás nincs benne. Ezt a problémát részben lehet szubrutinokkal és függvényekkel orvosolni, de az igazi megoldást az objektumok használata jelenti.

Az objektumközpontú probléma-megközelítés alapjaiban megváltoztatja a programozással kapcsolatos gondolkodásmódunkat. Az objektumorientált programozás alapja, hogy az egyes feladatok elvégzésére szolgáló kódrészleteket egy csokorba gyűjtjük, és lehetőséget biztosítunk nekik az egymással történő együttműködésre. Az objektumokat leíró osztályokat egymásból is származtathatjuk. Ezekből a tulajdonságokból is látszik, mennyire különbözik az objektum orientált nyelvek által megkövetelt gondolkodásmód, a hagyományos programnyelveknél használttól.

[szerkesztés] Mik is az objektumok valójában?

Ha egy programkódban objektumokról beszélünk, először az osztályok felépítését kell megvizsgálni, ugyanis minden objektum egy előre definiált osztály példánya. Egy osztályban általában egy feladat elvégzéséhez szükséges kódrészleteket gyűjtik össze funkciójuk szerint, tagfüggvényekbe rendezve. Egy osztályt létrehozása után példányosítani kell, hogy használhatóvá váljanak a benne összegyűjtött rutinok. Az osztály példányát nevezzük objektumnak.

class a { } //egy üres osztály

[szerkesztés] Az osztályok felépítése

A használt programozási nyelvtől függően, de legtöbbször a class előtétszóval definiálunk osztályokat. Egy osztály csak függvényeket és változókat tartalmazhat. Mivel az osztályokban található függvényeket csak az objektumon keresztül lehet elérni, ezért ezeket tagfüggvényeknek, vagy metódusoknak nevezi a szakirodalom. Bizonyos nyelvek megengedik, az osztály függvényeinek használatát annak példányosítása nélkül is, az osztályt mintegy interfészként használva. Ilyen esetekben azonban a függvény meghívásához meg kell adni annak az osztálynak a nevét is, amelynek a metódus tagja.

Az osztályok többnyire tartalmaznak egy ún. konstrukor típusú függvényt, melyből osztályonként csak egy van. Jelentősége, hogy az osztály példányosításakor minden esetben lefut, így használható a leendő objektum változóinak értékadására, de adhatunk neki komolyabb feladatokat is, például naplózhatja az objektum használatát, vagy végezhet bármilyen az objektum későbbi használatától független műveletet is.

Nyelvektől függően léteznek különböző osztályváltozók, melyek többnyire – a tagfüggvényekhez hasonlóan, csak az objektumon belül használhatóak. Például a PHP nyelvben ilyen a $this változó, mely a példányosított osztály nevétől függetlenül mindig saját objektumára fog hivatkozni.

class a {
        function a() //konstruktor
        {
                $this->szoveg = ’Hello világ! ’;
        }
        function kiir($kiirando)
        {
                echo $this->szoveg;
                echo $kiirando;
        }
}

a::kiir(’Bevitt szöveg’);    //kiirja, hogy ’Bevitt szöveg’

$obj = new a; //példányosítás
$obj->kiir(’Bevitt szöveg’); //kiírja, hogy ’Hello világ! Bevitt szöveg’

[szerkesztés] Öröklődés

Egy osztály definiálása után előfordulhat, hogy az osztályban szereplő kódokat más osztályokban is használni szeretnénk. Például egy adatbázis, vagy mondjuk egy fájlkezelő függvényeket tartalmazó osztályt, valószínűleg más objektumokból is szeretnénk használni. Ehhez csak arra van szükség, hogy egy adott osztályt amiből a másik osztály tagfüggvényeit el akarjuk érni, abból származtassunk. Ezt nevezzük öröklődésnek.

class a {
        function kiir_a()
        {
                echo ’A-osztály’;
        }
}

class b extends a {
        function kiir_b()
        {
                echo ’B-osztály’;
        }
}

$obj = new b;   //B-osztály példányosítása
$obj->kiir_a(); //Kiírja, hogy ’A-osztály’

Belátható, hogy az öröklődés használata rendkívül leegyszerűsíti a gyakran kellő függvények integrálását az egyes osztályokba, anélkül, hogy meg kellene változtatni az osztályok struktúráját. Azokban a nyelvekben ahol esetleg nem valósították meg az öröklődést, van egy alternatív megoldás a problémára: Abban az osztályban, ahol használni szeretnénk egy másik osztály tagfüggvényét, egyszerűen példányosítanunk kell azt. Ennek a módszernek hátránya lehet, hogy egyes objektumokból esetleg több példány is lesz, ezáltal felesleges memóriát használ a programunk. Ezt kiküszöbölendő megtehetjük, hogy a használt osztályon kívül példányosítjuk a másik osztályt, és paraméterként adjuk át.

[szerkesztés] Nyilvánosság (PPP)

A PPP rövidítés az objektum orientált nyelvekben többnyire alkalmazott adattulajdonságok rövidítése. Angolul, sorrendben: public, protected, private – azaz: nyilvános, védett és saját. Ezen tulajdonságokat az objektum tagfüggvényei, illetve változói egyaránt felvehetik. Az egyes tulajdonságok az elemek nyilvánosságát, azaz hozzáférhetőségét határozzák meg a következő módon:

  • public: Az objektumot használó bármely kód számára közvetlenül hozzáférhető.
  • protected: Közvetlenül nem, de egy öröklés által, alosztályon keresztül elérhető.
  • private: Csak azon objektum számára elérhetők, melyben meghatározták őket.

[szerkesztés] Felületek

A felületek, vagy más néven interfészek egyfajta biztonsági segítséget jelentenek a nagyobb projektekben. Képzeljük csak el, mi történne, ha A objektum példányosításakor paraméterként átadunk neki egy másik, B objektumot, de B objektum nem valósítja meg hozzá fűzött reményeket, azaz nem tartalmaz egy változót, vagy függvényt, amire A osztálynak szüksége van! Az ilyen hibák felkutatása az OOP összetettsége miatt nem egyszerű, de az ilyen hibák felületek használatával elkerülhetőek.

A felület valójában előre meghatározza egy osztály felületét: Megadja a benne található tagfüggvényeket, azok nyilvánossági tulajdonságait, bemenő paramétereit, egyszóval a függvénytörzs kivételével mindent.

interface pelda_interface {
        public function __construct(); //konstruktor
        protected function egyik_fgv($param1, $param2);
        private function masik_fgv();
}

Egy ilyen felület úgy véd meg a fent említett hibáktól, hogy az osztály definiálásakor megadjuk, milyen felületet kell megvalósítania. Ha a definiált osztály nem illeszkedik a felületre, fordításkori hibát kapunk, de elkerüljük a sokkal kellemetlenebb futásidőben bekövetkező hibát. A fenti interfész az alábbi osztály "csontváza":

class pelda_class implements pelda_interface {
        public function __construct()
        {
                //konstruktor függvénytörzse
        }
        protected function egyik_fgv($param1, $param2)
{
                //függvénytörzs
        }
        private function masik_fgv()
        {
                //függvénytörzs
        }
}

[szerkesztés] Elvont osztályok

Az elvont, vagy absztrakt osztályok bizonyos értelemben egyfajta átmenetet képeznek a hagyományos osztályok és az osztályokat meghatározó felületek között. Ez azt jelenti, hogy egy absztrakt osztály egyaránt tartalmaz kidolgozott, törzzsel rendelkező tagfüggvényeket, és előre nem meghatározott tagfüggvényeket, melyeket majd az osztályból származtatott alosztály fog részletesen definiálni. Az absztrakt osztály ílymódon hagyományos értelemben vett osztályként és a tőle öröklő alosztályok interfészeként egyszerre tölt be funkciót.

abstract class A {
        public function __construct()
        {
                //A osztály konstruktorának függvénytörzse
        }
        abstract protected function fgv();
}

class B extends A {
        public function __construct()
        {
                //B osztály konstruktorának függvénytörzse
        }
        protected function fgv()
        {
                //függvénytörzs
        }
}

[szerkesztés] Kivételkezelés

Egy program elkészítése során gyakran kerülhetünk abba a helyzetbe, hogy le kell kezelnünk egy esetlegesen fellépő hibát. Mondjuk fájlkezelő algoritmust készítünk, ami megfelelő feltételek mellett működik is, de mi történik, ha letöröljük a fájlt, miközben a programunk éppen műveleteket végezne rajta? Ilyenkor többnyire lefagy a scriptünk, de mennyivel elegánsabb lenne, ha egy hibaüzenet küldenénk a program használójának. Többek között az ilyen váratlan hibák kezelésére szolgálnak a kivételek.

A kivétel gyakorlatilag egy objektum, mely a fellépő hibát jelképezi. Mivel objektum, kell lennie egy osztálynak, amely meghatározza. Ez nyelvtől függően, de általában az Exception nevet viseli. A hibák "elkapása" többnyire annyiból áll, hogy figyeljük az egyes programrészek sikeres lefutását, és ha valahol hiba történik, máris "dobjuk" a kivételt, ami annyit jelent, hogy a hibás programrész futása azonnal megszakad, és végrehajtódik a kivételkezelő kódblokk.

$hiba = true; //egy változóval hibát szimulálunk

try {
        //itt fut a program
        if($hiba) {
                throw new Exception; //hibára kiváltjuk a kivételt
        }
}
catch(Exception $e) {
        //itt kezeljük az elkapott kivételt
}

A példában a try blokkon belül történt hiba, amire kivételt váltunk ki. Ekkor a program megáll és a végrehajtás automatikusan a catch blokkal folytatódik, melyben kezelhetjük az $e objektumban megjelenő kivételt. Például hibaüzenetet küldhetünk a felhasználónak, írhatunk a hibanaplóba, vagy akár megkísérelhetjük újra a hibás rész lefuttatását. Utóbbi esetben nem árt felvenni egy változót, amivel számolni fogjuk a próbálkozásokat, ha ugyanis másodjára, harmadjára n-edjére is hiba történik, a kivétel kezelése egy végtelen ciklushoz hasonlóan soha nem ér véget. Egy számláló minden próbálkozáskori ellenőrzésével ez elkerülhető.

[szerkesztés] Tervezési minták

A tervezési minták lényegében azokra a problémákra nyújtanak egyfajta általános megoldást, melyekkel a szoftverfejlesztők gyakran találkoznak. Ilyen probléma például, amikor egy adott környezetben már bevált kódot alkalmassá kell tennünk egy másik interfészen keresztül történő használatra is. Bár a probléma minden feladatnál egyedinek tűnik, egy idő után felismerjük, hogy a megoldási módszerek lényegében azonos sémát követnek. Így tehát, ha felfedjük egy probléma lényegét és összevetjük hasonló, már megoldott problémákkal, ugyanazokkal a lényegi lépésekkel találkozunk.