410 likes | 539 Vues
AZ OOP ALAPJAI. OOP vs struktúrálatlan programozás. Az objektum-orientált programozás a klasszikus struktúrált programozásnál jóval hatékonyabb megoldást képes nyújtani a legtöbb problémára.
E N D
OOP vs struktúrálatlan programozás • Az objektum-orientált programozás a klasszikus struktúrált programozásnál jóval hatékonyabb megoldást képes nyújtani a legtöbb problémára. • Az osztályokújrafelhasználásának támogatásával nagymértékben tudja csökkenteni a szoftverek fejlesztéséhez szükséges időt. • Az objektumorientált programozás abban különbözik leginkább a struktúrálatlan programozási módszerektől, hogy a programrészeket, hasonló feladatokat, de főleg hasonló feladatokon dolgozó adatokat és az azokat felhasználó metódusokat csokorba foglaljuk. • Az objektum-orientált programozás jobban igyekszik közelíteni a világban lejátszódó valódi folyamatokhoz.
Az OOP alapfogalmai • Osztály • Objektum, példány, egyed • Osztálydefiníció • példányváltozók • osztályváltozók • metódusok • konstruktor • destruktor • inicializáló blokk • Hozzáférési jogok • Egységbezárás • Adatelrejtés
Osztály létrehozása • Egy egyed egy osztály megvalósítása, más szóval példányosítása. Javaban ezt így érjük el: Object objektum = new Object() • Ilyenkor lefut az osztály konstruktora, ahol meghatározhatjuk a kezdőértékeket, inicializálhatjuk az objektumot. • Egy osztálynak lehetnek példányváltozói, illetve osztályváltozói. Előbbiből minden egyes példányosított egyedhez kerül egy-egy, míg osztályváltozóból az összes azonos osztályból példányosított egyedhez csak egy. Ugyanez érvényes az eljárásokra is. • Java esetén egy osztály definíciója és deklarációja, azaz az eljárások feje és megvalósítása nem szétválasztható, mint például C++-ban.
Példa - Autó • Valósítsunk meg egy Autó osztályt Javaban! package pelda002; // az osztályt tartalmazó csomag public class Auto { //az osztály kezdete // az osztály megvalósítása } //az osztály vége
Autó - benzinár: int - tipus: String - km_ora: double - benzin: double - ferohely: int +Auto(tipus: String, fogyasztas: double, ferohely: double) +Auto(tipus: String, fogyasztas: double, ferohely: double, km_ora: double) +költség(km: double): double +fuvar (km: double) +tankol (l: double) +toString(): String
Példa – Autó - változók • Ahogy C++-ban, úgy a Javában is felsorolhatunk változókat, de a Java megengedi, hogy iniciáljuk is azokat. • Változó deklarációja: [módosítók] típus változó [inicializáció] public class Auto { static private intbenzinar=250; //statikus típus private String tipus; privatedouble benzin; private double fogyasztas; private double km_ora=0; //példa kezdőértékre private int ferohely;
Példa – Autó - Konstruktor • Minden osztály példányosításakor lefut egy konstruktor. Ennek neve – ahogy C++-ban is – megegyezik az osztály nevével, és nincs visszatérési értéke. • Minden osztálynak létezik konstruktora, legfeljebb az üres, mégha nem is hozzuk létre, lefut. • Konstruktorból lehet több is, túlterhelésükre ugyanaz vonatkozik, mint a függvények túlterhelésére.
Példa – Autó - Konstruktor public Auto(String tipus, double fogyasztas, int ferohely){ this.tipus=tipus; this.fogyasztas=fogyasztas; this.km_ora=0; this.benzin=0; this.ferohely=ferohely; } public Auto(String tipus, double fogyasztas, int ferohely, double km_ora){ this.tipus=tipus; this.fogyasztas=fogyasztas; this.km_ora=km_ora; this.benzin=0; this.ferohely=ferohely; }
Példa – Autó - Metódusok • Érdekesség, hogy a metódus neve megegyezhet egy változóéval is. • Az osztály metódusait, ahogy láttuk a konstruktornál, szintén túl lehet terhelni. • Metódus definíciója: [módosítók] típus Név([paraméterek]) {törzs} • Példa metódusokra:
Példa – Autó - Metódusok • Az alábbi függvények dokumentációja és magyarázata a külön dokumentumban található public void tankol(double l){ benzin+=l; } private double benzinigeny(double km){ return ((double)km/100)*fogyasztas; } public double koltseg(double km){ return benzinigeny(km)*benzinar; }
Példa – Autó - Metódusok public double halad(double km) { if(benzinigeny(km)<=benzin){ benzin-=benzinigeny(km); km_ora+=km; System.out.println("Megtettunk " + km + "km utat!"); return km; } else { System.out.println("Nincs benzin " +km+ "km uthoz!"); return 0; } }
Példa – Autó – Getter & Setter • Objektumok leírásánál legtöbbször elrejtjük a belső változókat a külvilág elől, mert pl nem akarjuk, hogy változtassák, módosítottan akarjuk megjeleníteni vagy átadni, vagy egyszerűen nem akarjuk, hogy lássák. Ezért is lettek private változók. Ilyenkor használatosak a be- és kiviteli függvények, a getterek és setterek. Most az előbbire látunk példát: • public double benzin_ora(){ return benzin; } public double km_ora() { return km_ora; }
Példa – Autó – String toString() • A Javában minden egyednek van egy függvénye, ami szöveges információt hordoz a példányról, ez a public String toString() függvény, amit ha nem hozunk létre, az objektumunk örököl az ősosztálytól (erről bővebben később). public String toString(){ return tipus + ", benzin: " + benzin_ora() + ", km: " + km_ora(); }
További osztály-elemek (említés szintjén, a példába ne foglaljuk őket): • inicializációs blokk (vagy blokkok), ami még a konstruktor előtt lefut, és azokon a változókon dolgozhat, amiket már előtte definiáltunk (mindez a az osztály törzsében, metóduson kívül). String s0; { s0 = new String("es-null"); // inic. blokkon kívül ezt nem szabadna! } • Javában a Garbage-collector elvégzi helyettünk a memória tisztítását, de néha szükség lehet valamiféle destruktor jellegű metódusra. Erre szolgál a Finalize, illetve a classFinalize metódus: • protected void Finalize() throws Throwable {} • static void classFinalize() throws Throwable {}
Módosítók összessége Osztály: • public: Az osztály bárki számára látható • final: Végleges, nem lehet tovább bővíteni. Az öröklődésnél lesz róla szó bővebben. • abstract: Az öröklődésnél lesz róla szó, csak bővíteni lehet, példányosítani nem. • (Üres): Üres gyakorlatilag úgy használható, mint a public, a csomagra vonatkozóan
Módosítók összessége Változó, illetve objektum: • public: Az objektumot használó bármely kód számára közvetlenül hozzáférhető. • protected: Private, de az alosztályok látják • private: Csak azon objektum számára elérhetők, melyben meghatározták őket. • final: Végleges, azaz konstans érték • static: Osztályváltozó, egy osztályhoz csak egy tartozik
Módosítók összessége Metódus esetén: • public: Az objektumot használó bármely kód számára közvetlenül hozzáférhető. • protected: Közvetlenül nem, csak egy öröklés általi alosztályon keresztül érhető el. • private: Csak azon objektum számára elérhetők, melyben meghatározták őket. • static: Példányosítás nélkül használható, (pl println fv), csak statikus változókat használhat. • final: Végleges, a leszármazott nem írhatja felül. (Öröklődésnél bővebben) • abstract: Absztrakt osztálynál használható, kötelező felülírnia, azaz megvalósítania a leszármazott osztálynak.
Öröklődés • Tegyük fel, hogy létre akarunk hozni: • Autót (már megvan) • Taxit • Buszt • Ez így egy kicsit sok, főleg hogy az adatok és metódusok nagy része ismétlődne. • Megoldás: bővítsük ki az Autót a Taxi jellemzőivel! = specializáljunk!
Öröklődés a Javában • A leszármazott osztály örökli a szülő minden tulajdonságát. • A szülő private tagjaiból is van példánya, de nem férhet hozzájuk közvetlenül (erre van a protected). • Valójában Jávában minden objektum származtatott, az ősosztály java.lang.Object kiterjesztettje. • Jávában a kiterjesztést az extend szóval jelölhetjük. • A leszármazott a szülőobjektum egyes tagjaira a super kulcsszóval hivatkozhat. • Object metódusai: equals, getClass, toString, stb
Az Auto osztály bővítése • Bővítsük ki az Autó osztályát, és rögtön írjuk bele az új változóinkat! public class Taxi extends Auto{ private double penztarca; private double kmdij; // függvénytest }
Példa – Taxi - konstruktorok • A leszármazott nem örökli a szülő konstruktorát. • Van lehetőségünk a leszármazott konstruktorának első sorában meghívni a szülő valamelyik konstruktorát a super kulcsszóval. • Ha ezt nem tesszük meg, vagy ha nem is definiálunk konstruktort, akkor is végrehajtódik a szülő üres kostruktora (ha van ilyen), mégpedig minden gyermekbeli inicializációs blokk előtt.
Példa – Taxi - konstruktor public Taxi(String tipus, double fogyasztas, int ferohely, double kmdij) { super(tipus, fogyasztas, ferohely); this.kmdij=kmdij; this.penztarca = 0; }
Elfedés, felülírás • Nyilván néhány változónak és metódusnak más szerepet szánunk az új, származtatott osztályban. • Ilyenkor felülírhatjuk, elfedhetjük a szülő azonos szignatúrájú metódusait (egyébként egyszerű túlterhelés lenne). • Taxi esetén másképp számoljuk a költséget, hiszen hozzájön még a fuvarozó kilométerdíja. Egyszerűen írjunk új eljárásokat azonos névvel, de az új funkcióval! • Itt is hivatkozhatunk a szülő metódusára (ha az már egy új fv miatt el lenne fedve) a super kulcsszóval. • A toString() függvényünket is úgy módosítsuk, hogy az már kiírja a fuvarozó pénztárcájának tartalmát is!
Példa – Taxi • Írjuk meg a Taxi osztályt! • A költség számításánál vegyük figyelembe a kilométerdíjat! • A halad függvényt hagyjuk meg, mellé hozzunk létre egy fuvaroz függvényt, ahol adjuk hozzá a taxis pénztárcájához a kilométerdíjból beszedett összeget is. • A toString() metódus írja ki egyrészt az Autó adatait, de tegye hozzá a taxis pénztárcájának tartalmát is.
public double koltseg(double km){ return super.koltseg(km)+kmdij*km; } public void tankol(int l){ benzin+=l; penztarca-=l*benzinar; } public String toString(){ returnsuper.toString()+", a fuvarozo penze: "+penztarca; }
A halad(int km) függvényt ne írjuk felül, mert két fuvar közt egyszerű autóként halad a taxis: public double fuvar(double km){ if(halad(km)==km){ //a halad(int km)-t nem irtuk felul. penztarca+=koltseg(km); return km; } else return 0; }
Költség/fő (ha többen taxizunk, olcsóbb) public double koltseg_per_fo(double km, int fo){ if(fo>ferohely+1){ System.out.println("Tobb utas, mint ferohely, ha rendor jon, nagyon draga lesz!"); return 0; } else return koltseg(km)/fo; }
Kész a Taxi osztály • De még nem működhet teljesen: • Az Autó tagváltozóinak mindig private a módosítójuk, változtassuk meg protectedre, hogy tudjuk őket használni! • Hozzunk létre egy futtató osztályt main()-nel! public class runAuto { public static void main(String[] args){ Auto lada = new Auto("Lada", 10, 5); Taxi daewoo = new Taxi("Daewoo", 7, 5, 200); lada.tankol(40); lada.halad(15); // Megtettunk 15.0km utat! System.out.println(lada.toString());// Lada, benzin: 38.5, km: 15.0 daewoo.tankol(30); daewoo.halad(40); // Megtettunk 40.0km utat! System.out.println(daewoo.koltseg_per_fo(15, 4)); // 815.625 daewoo.fuvar(200); System.out.println(daewoo.toString()); // Daewoo, benzin: 13.2, km: 240.0, a fuvarozo penze: 36000.0
Ízelítő a következő óra anyagából • Polimorfizmus: Auto tata = new Taxi("Tata (indiai automarka)", 9, 4, 250); // létrehoztunk egy autót, ami egyébként egy taxi. tata.tankol(18); // Taxival vettunk 18.0l benzint! // tata.fuvar(100); // helytelen! tata.halad(100); // Megtettunk 100.0km utat! System.out.println(tata.toString()); // Tata (indiai automarka), benzin: 9.0, km: 100.0, a fuvarozo penze: -4500.0
Véglegesítés • Hozzuk létre a Busz osztályt a Taxi osztálybol! • A buszon egy vonaljeggyel lehet utazni, szintén van kilométerdíj, amit a sofőr kap. • A költség marad ugyanúgy, ahogy a taxinál volt, ellenben a fuvar esetében az üzemeltető cég pénztárcáját tekintsük, azaz a jegyárból vontjuk ki a buszvezető díját is! • Az osztály legyen végleges, azaz már ne lehessen szülő (final) • Az alábbi osztályokat valósítsuk meg: • public Busz(String tipus, double fogyasztas, int ferohely, double kmdij) • public double fuvar(double km) • public double fuvar(double km, int fo) • public double Haszon(double km, int fo) • public double koltseg_per_fo(double km, int fo)
Busz megvalósítás public final class Busz extends Taxi { private int jegyar = 230; /** * Busz konstruktora * @param tipus A busz márkája * @param fogyasztas //A busz fogyasztása * @param ferohely // Férőhelyek száma * @param kmdij //A buszvezető bére kilométerenként */ public Busz(String tipus, double fogyasztas, int ferohely, double kmdij){ super(tipus,fogyasztas, ferohely, kmdij); }
Busz megvalósítás /** * A fuvar ára egy vonaljegy. Most csak egy embert viszunk * csak hogy felul tudjuk irni a Taxi fuggvenyet */ public double fuvar(double km){ if(halad(km)==km){ //a halad(int km)-t nem irtuk felul. penztarca+=jegyar – km_dij*km; return km; } else return 0; }
Busz megvalósítás /** * Fuvar több főre. * @param km Megtett út * @param fo Utasok száma * @return Megtett kilométerek száma */ public double fuvar(double km, int fo){ if(halad(km)==km){ penztarca+=fo*jegyar - km*kmdij; return km; } else return 0; }
Busz megvalósítása /** * A fuvarozó cég összköltsége. Tegyük fel, hogy * az utasok végig utazták az utat. * @param km A megtett kilométerek száma * @param fo Utasok száma * @return Költség forintban */ public double Haszon(double km, int fo){ return fo*jegyar - koltseg(km); }
/** * Akárhányan is utazunk akármennyit, csak egy vonaljegyet veszünk. */ public double koltseg_per_fo(double km, int fo){ return jegyar; }
Általánosítás • A specializálással ellentétes fejlesztés az általánosítás. • Tegyük fel, hogy létre akarok hozni egy kerékpár típust. Szintén lenne néhány közös jellemzője az autóval. • Hozzunk létre egy jármű osztályt, de úgy, hogy azt ne lehessen példányosítani, azaz legyen abstract! • Ilyenkor amelyik függvényt abstract jelzővel illetjük, azt az utódnak muszáj felülírnia. Ez esetben a halad(), illetve a költség() metódus lehet ilyen. • Az autóból természetesen át kell tenni néhány változót, azokat, amik a kerékpárral, illetve általánosságban egy járművel egyeznek! • Valósítsuk meg a járművet!
public abstract class Jarmu { protected String tipus; protected double km_ora; protected int ferohely; // Haladást megvalósító metódus, absztrakt public abstract double halad(double km); // Költséget kiszámoló metódus, absztrakt public abstract double koltseg(double km); // Egy megvalósított metódus, kiírja a nevét és a kmórát. public String toString(){ return this.tipus+ ", " + km_ora + " km"; } }
Kerékpár megvalósítása • Írjuk meg a kerékpár osztályát! • A költség legyen 0! (ezért is szeretjük a kerékpárt) • A haladást csak a kilométeróra növekedése kövesse! • A toString() a típust és a kilométerórát írja ki!
public final class Kerekpar extends Jarmu { public Kerekpar( String tipus){ // Egy kerékpár this.tipus=tipus; this.km_ora=0; this.ferohely=1; } public double halad(double km) { // A halad fv. this.km_ora+=km; return 0; } public double koltseg(double km) { // Adott utra adott koltseg. return 0; } }
Vége! (majdnem) • A feladatok megoldásai megtekinthetők a digitus../stuat/progny/ mappában. • Házi feladat: • Hozzunk létre egy osztályhierarchiát, melyben ingatlanokat reprezentálunk. • Legyen egy absztrakt Ingatlan osztály, ahol az ingatlan területe, címe és bérleti díja van, továbbá egy metódust, ami az éves költségeket számolja ki. • Írjunk egy Lakás osztályt, ahol a lakók számát is tároljuk, ők a lakásért fizetnek adót, illetve rezsit. • Írjunk egy Kollégium osztályt, mely a Lakásból öröklődik, számoljunk az állami támogatással és számoljuk ki az egy főre eső költségeket is. • Végül valósítsunk meg egy Iroda osztályt is, ahol tároljuk a cég nevét, a költségek számításánál pedig figyelembe vesszük az adót is. • Összes osztálynál legyen aktuális String toString osztály!