OOP röviden
Az OOP-ben "...nem a műveletek megalkotása áll a középpontban, hanem az egymással kapcsolatban álló programegységek hierarchiájának megtervezése." http://hu.wikipedia.org/wiki/Objektumorient%C3%A1lt_programoz%C3%A1s
Mielőtt megvizsgáljuk hogyan működik az OOP a Processingben, beszéljünk az "objektumról". Képzeld el hogy egy napod programját írod le, kb így nézne ki: pszeudokóddal leírva:
- Felkelek
- Kávét iszok
- Reggelizek: műzlit, tejet.
- Földalattival utazok
Ez mit jelentenek? Milyen speciális szerepeket töltenek be ezek? Megtévesztő lehet, mivel nem írtunk utasításokat, de a legfőbb dolog te vagy, az ember. Különböző tulajdonságokkal rendelkezel. Egy bizonyos módon nézel ki, pl. barna a hajad, szemüveget hordassz, furcsán nézel ki. A képességed is megvan ahhoz hogy megtegyél dolgokat, pl. felkelj (vagy tovább aludj), egyél, utazz a földalattin. Az objektumok éppúgy mint te tulajdonságokkal rendelkezenek és feladatok elvégzésére képesek.
Ez hogyan vonatkozhat a programozásra? Egy objektum tulajdonságai a változók (adatok) és a dolgok amiket el tud végezni azok a funkciók. Ezekből épül fel az objektum-orientált programozás.
Nézzük meg azt az emberre átültetve:
Emberi Adatok:
- Magasság
- Súly
- Nem
- Szem szín
- Haj szín
Emberi funkciók:
- Alvás
- Felkelés
- Evés
- Utazás
Ebben az esetben az embert mind objektumot kezeljük. Az objektumokat osztályokba (class) soroljuk. Másik példa: a sütőforma sütiket csinál, de nincs benne alapvetően süti. A sütiforma az osztály és a süti az objektum.
Objektum használata
Mielőtt megnézzük hogyan írjuk le ténylegesen az osztályt megnézzük hogyan működik az objektum a fő programrészekben (setup, draw). Példának a kocsit használjuk, ami a kódban már egy téglalap lesz:
Adat (globális változó):
- Kocsi színe
- Kocsi x helye
- Kocsi y helye
- Kocsi sebessége
Setup:
- Megadjuk a kocsi színét
- Meghatározzuk a kocsi helyét
- Meghatározzuk a kocsi sebességét
Draw:
- Hátteret kitöltjük egy színnel
- A kocsi színét is megadjuk
- Megnöveljük a kocsi helyének értékét a sebességének értékével
A kódban a globális változókat a setup() előtt határozzuk meg, szokásos módon a kijező ablak létrehozása kerül a setup()-ba, a draw()-ben futtatjuk mejd le a mozgást, amit egy külön függvényeben move() írtunk most le, éppúgy mint a display() függvényt is ami kirajzolás után törli a képernyőt, így látszódni fog az animáció:
Az objektum orientált programozás lehetővé teszi, hogy a fő program helyett a kocsi-objektumban tároljuk a változókat és funkciókat. A kocsi-objektumot egyrészt az adatit ismerjük (szín, helyzet, sebesség) másrészt a metódusát (művelet az objektumon belül, pl. a kocsit vezetni lehet):
Adat (globális változó):
- Kocsi objektum
Setup függvény:
- Kocsi objektum inicializálása (kezdőérték megadása)
Draw függvény:
- Háttérszín kitöltés
- Kocsi objektum megjelenítése
- Kocsi objektum vezetése
Tehát az előző kódunkat e szerint átrendezve így néz ki:
Car myCar; void setup() { myCar = new Car(); } void draw() { background(255); myCar.drive(); myCar.display(); }
Mielőtt a kód többi részébe is belemennénk, nézzük meg hogyan működnek az osztályok.
A "sütőfoma" leprogramozása
Az autós példa szemlélteti hogyan használjuk objektumokat a Processingben egyszerű, átlátható kódok megírásához. Nehezebb lesz olyan objektumot írni ami egy osztályt is tartalmaz.
Minden osztálynak 4 elemet kell tartalmaznia: név, adat, konstruktor és metódus (Technikailag valójában csak a névre van szükség, de a többit is tartalmazza a program.)
Az alábbi táblázat megmutatja mi a különbség az OOP (létrehozunk egy osztályt) és a nem-OOP között:
Mi mit jelent?
Class Name / Osztály neve:
Az osztály nevének akármilyen szót választhatsz. Ezeket hagyományosan nagybetűvel kezdjük hogy megkülönböztessük a változóktól amik hagyományosan kisbetűsek. Kapcsos zárójelen belülre kerül a kód az osztály neve után.
Data / Adat: Változók gyűjteményét jelenti. Ezek gyakran hivatkozásokra vonatkoznak.
Constructor / Konstuktor: Speciális funkciótlát el az osztályon belül amiben egy hivatkozásszerepel. Itt az objektumra vonatkozó instrukciók szerepelnek, pl. ilyen a setup() aProcessingben, amit arra használunk hogy a sketch-en belül létrehozzunk egy egyéni objektumot valahányszor egy új osztályt hozunk létre. Aminek mindig ugyanaz kell hogy legyen a neve, mint a meghívott operátornak:"Car myCar = new Car();".
Functionality / Funkcionalitás: Műveletek írásával különböző funkciókat adhatunk az objektumunknak.
Tehát a főbb függvények:
void setup() {
}
void draw() {
}
class Car {
}
Néhány definíció:
"Objektum (példány)
Object (Instance)
Információt (adatokat) tárol és kérésre műveleteket végez. Van állapota,
viselkedése és futásidőben azonosítható.
Osztály
Class
Az osztály egy felhasználói típus, amelynek alapján példányok (objektumok)
hozhatók létre. Az osztály alapvetően adat és metódus (művelet) definíciókat
tartalmaz.
Konstruktor
Constructor
Az a művelet, amely inicializálja az objektumot. Automatikusan hívódik. Egy
osztályhoz annyiféle konstruktort készítünk, ahányféle képpen lehetővé tesszük a
példányok inicializálását ."
http://www.ms.sapientia.ro/~manyi/teaching/c++/definiciok.pdf
Objektum használata - a részletek
Korábban láthattuk hogyan lehet objektum használatával leegyszerűsíteni asketch-ünk fő részeit (void,loop).
// 1. objektum deklarálása Car myCar; void setup() { // 2 . objektum inicializálása myCar = new Car(); } void draw() { background(255); // 3. egy objektum műveletének meghívása myCar.drive(); myCar.display(); }
Most nézzük meg a fönti kód részleteit lépésneként:
1. objektum deklarálása
Objektumot úgy defíniálunk, hogy megadjuk a típusát és a nevét. Valamilyen egyszerű adattípussal mint pl. az intege, ez így néz ki:
int var; // type name
Az egyszerű adat típusok információkegyes darabjait tartalmazzák:az integer, a float, a karakterstring, stb. Objektumhoz kapcsolódó változó deklarálása is ilyen egyszerű. Class a típusa és utána megadjuk a nevét. Az objektumok nem primitív adat típusok, hanem összetett adat típusokat tartalmazó műveletek.
2. objektum inicializálása
Kezdőérték megadását jelenti, azaz megjelöljük a változónk értékét. Integer esetén így néz ki:
var = 10; // var equals 10
Ha nem adod meg az értéket, akkor alapból 0 lesz, de akkor is defíniálni kell! Egy objektum inicializálásaegy kicsit bonyolultabb. Ahelyett hogy simán hozzárendelünk egy értéket, mint egy integernél vagy floatnál, fel kell építenünk az objektumot. Amit először a new nevű operátorral létrehozunk:
myCar = new Car()
A fenti példában a "myCar" az objektum neve és az = jel azt mutatja hogy valamivel egyenlővé tesszük majd, ez lesz amire hivatkozik majd az objektumunk. Ami most történik,valójában a Car objektum inicializálása. Ha meghívjuk a Car osztályt láthatjuk hogy ez a kód-sor meghívja a konstuktort, vagyis azt a speciális műveletcsoportot amit Car()-nek neveztünk el. Válozókkal ellentétben mindig inicializálni kell!
3. egy objektum műveletének meghívása
Ha egyszer sikeresen deklaráltuk és inicializáltuk a változónkat, osztályunkat, már tudjuk használni. MEg tudjuk hívni az objektumben szereplő funkciókat/függvényeket.
A car esetében egyik elérhető funkciónak sincs paramétere (argumentuma):
myCar.drive(); myCar.display();
A funkciókat a pont szintaxissal hívjuk meg - objektumok tulajdonságaihoz vagy tagfüggvényeihez férhetünk vele hozzá. (A függvények argumentumát, vagy formális paramétereit is szokás változónak nevezni.)
Konstruktor argumentumok
"Konstruktornak hívjuk az objektumorientált programozási nyelvekben egy osztály azon metódusát, amely az objektumpéldányosításakor hívódik meg. A konstruktor általában azért felelős, hogy az objektum adattagjait az osztály szempontjából értelmes, érvényes állapotba hozza, bizonyos nyelvekben pedig az objektum által használt memóriaterületet lefoglalása is feladata. A konstruktor neve általában az osztály nevével egyezik meg. A konstruktorok általában rendelkezhetnek paraméterekkel, amik az objektum kezdeti állapotát határozzák meg. A programnyelvek rendszerint megengedik, hogy azonos néven több, eltérő paraméterlistájú változat is szereplejen. Ilyenkor az objektum létrehozásakor használt argumentumok (független változók) döntenek arról, melyik metódus fut le ezek közül." http://wiki.prog.hu/wiki/Konstruktor
Az előző példában a car objektumot úgy inicializáltuk hogy a new operátort egy az osztályra vonatkozó konstruktor követte:
Car myCar= new Car();
Ezek voltak az OOP alapjai. Ennek ellenére lehet egy kis problémánk a fenti kóddal. Mi van ha két car objektumot akarunk használni?
Car myCar1 = new Car(); Car myCar2 = new Car();
Így elértük célunkat; a kód két car objektumot tartalmaz, az egyiket a myCar1 változóban, a másikat a myCar2 változóban tároltuk el. Azonban, ha tanulmányozoda car osztálytészreveheted hogy ez a két car azonos:midegyik fehér, középen halad és a sebessége 1. Pszeudó kóddal elmondva:
Csinálj egy új autót!
Helyette ezt akarjuk mondani:
Csinálj egy új piros autót, aminek a kezdőpozíciója 0,10 és a sebessége 1.
De ezt is mondhatjuk:
Csinálj egy új kék autót, aminek a kezdőpozíciója 0,100 és a sebessége 2.
Ezt megtehetjük úgy hogy argumentumokat helyezünk a konstruktor metódusába:
Car myCar = new Car(color(255,0,0),0,100,2);
A konstruktor ezeket az argumentumokat tartalmazhatja:
Car(color tempC, float tempXpos, float tempYpos, float tempXspeed) { c = tempC; xpos = tempXpos; ypos = tempYpos; xspeed = tempXspeed; }
Nézzük meg hogyan működnek ezek a paraméterek ebben a kontaxtusban:
Pszeudo fordítása: Csinálj egy békát aminek a nyelve 100 hosszusságú.
Az argumentumok olyan helyi változók amiket a függvény testében használunk, értékekkel töltünk meg amikor meghívjuk a függvényt. A példánkban ezeknek csak egy céljuk volt, hogy inicializálják a változókat az objektumban. A konstruktorok argumentumai ideiglenesek és kizárólag azért vannak hogy átküldjenek egy értéket onnan ahol az objektumon belül egy másik objektum is szerepel. Ez lehetővé teszi hogy ugyanazzal a konstruktorral több objktumot hozzunk létre:
Car myCar1; Car myCar2; // Két objektum van! void setup() { size(200,200); // Paraméterek kerek zárójelbe kerülnek, amikor az objektumot felépítjük myCar1 = new Car(color(255,0,0),0,100,2); myCar2 = new Car(color(0,0,255),0,10,1); } void draw() { background(255); myCar1.drive(); myCar1.display(); myCar2.drive(); myCar2.display(); } // Annak ellenére hogy több objektumunk vannak, csak egy osztályra van szükségünk. // Nem számít mennyi sütitsütünk, elég egy sütiforma hozzá. class Car { color c; float xpos; float ypos; float xspeed; // Argumentumokkal defíniájuk a konstruktort: Car(color tempC, float tempXpos, float tempYpos, float tempXspeed) { c = tempC; xpos = tempXpos; ypos = tempYpos; xspeed = tempXspeed; } void display() { stroke(0); fill(c); rectMode(CENTER); rect(xpos,ypos,20,10); } void drive() { xpos = xpos + xspeed; if (xpos > width) { xpos = 0; } } }
forrás:
http://processing.org/learning/objects/