58. Osztály, objektum fogalma, implementációs lehetőségek(Shagreen) 59. Egységbezárás (elvi háttere, gyakorlati megvalósítás) (Shagreen) 60. Öröklés (elvi háttere, gyakorlati megvalósítás) (Shagreen) 61. Polimorfizmus (elvi háttere, gyakorlati megvalósítás) ( Shagreen) 62. Interfészek (elvi háttere, gyakorlati megvalósítása) ( Shagreen) 63. Eseménykezelés (elvi háttere, gyakorlati megvalósítása) (még kidolgozás alatt ) 66. Generikus típusok (elvi háttere, gyakorlati megvalósítása) (még kidolgozás alatt )
Tartalom 58 Osztály, objektum ..........................................................................................................................3 59. Egységbe Zárás..............................................................................................................................8 60. Öröklés (elvi háttere, gyakorlati megvalósítás) ............................................................................ 13 61. Polimorfizmus (elvi háttere, gyakorlati megvalósítás) .................................................................. 17 62. Interfészek (elvi háttere, gyakorlati megvalósítása) ..................................................................... 24
58 Osztály, objektum Osztály, Objektum fogalma , implementációs lehetőségek Az objektum-orientált programozás (angolul object-oriented programming, röviden OOP) egy programozási módszer. Ellentétben a korábbi programozási nyelvekkel, nem a műveletek megalkotása áll a középpontban, hanem az egymással kapcsolatban álló programegységek hierarchiájának megtervezése. Az objektum-orientált gondolkodásmód lényegében a valós világ lemodellezésén alapul – például egy hétköznapi fogalom, a „kutya” felfogható egy osztály (a kutyák osztálya) tagjaként, annak egyik objektumaként. Minden kutya objektum rendelkezik a kutyákra jellemző tulajdonságokkal (például szőrszín, méret stb.) és cselekvési képességekkel (például futás, ugatás). Az osztályok alapvető célja az adatok, és az adatokat kezelő/módosító/lekérdező eljárások egységbe zárása, ezáltal a tényleges tárolás és a működés elrejtése, egyfajta "fekete doboz" működési elv kialakítása. Mindez egy olyan, a fejlesztési folyamat alatt kényelmesen bővíthető öröklési struktúrába helyezve, amelynek segítségével a procedurális programozáshoz képest magasabb absztrakciós szinten tudunk programozni. Objektum: Az objektum állapottal rendelkező entitás, amely a benne tárolt adatok felhasználásával feladatokat hajt végre és egyéb objektumokkal kommunikál. jellemzői: -
-
-
-
Minden objektum egy előre definiált osztály példánya. Adatokat(=mezők) és algoritmusokat(=metódusokat) tartalmaz. Metódusokkal vesz részt az üzenetváltásban, ezeken keresztül kommunikál más objektumokkal és a külvilággal.(elemei: célobjektum, metódus, paraméterek, eredmény) Minden objektum egyértelműen azonosítható. Egy objektum különböző állapotokkal rendelkezhet, az egyes állapotokat mezői aktuális értéke határozzák meg. A viselkedése mindent definiál, amit az objektum „csinálhat”. Megadja, hogyan reagálhat más objektumok kéréseire. Egy állapot az objektumon végzett műveletek hatására változhat (mindig “megjegyzi” aktuális állapotát), viselkedését az általa ismert (az objektumba épített) algoritmusok határozzák meg. Két objektum állapota akkor egyezik meg, ha minden megfelelő mezőértékük megegyezik. (Ha két objektum állapota megegyezik, maguk az objektumok akkor sem azonosak) Az identitás azt jelenti, hogy minden objektum egyedi, még akkor is, ha az állapotuk megegyeznek. Saját életciklussal rendelkeznek (önállóak). Minden objektum valamilyen létező osztályba tartozik
Osztály: Az osztály fogalma: Leírás objektumok egy csoportjához, amelyeknek közösek az attribútumaik, operációik, más objektumokkal való kapcsolataik és szemantikus viselkedésük. Minden objektum pontosan egy osztály példánya, és az osztály az adott objektum típusa.( sablont adnak az objektumokhoz) Az osztályok tehát azonos adatszerkezetű, viselkedésű objektumokat írnak le. Az osztály egy adott objektumtípust határoz meg annak adataival (mezők) és beépített algoritmusaival (metódusok). jellemzői: -
-
-
-
Egy osztályból több példány is létrehozható Egy osztály összes példánya ugyanazokat a mezőket és metódusokat tartalmazza. Az egyes példányok létrehozásuk pillanatában azonos állapotúak, ezt követően viszont önállóan működnek tovább.(különböző helyen tárolódnak a memóriába) Léteznek az osztályra (és nem az egyes objektumpéldányokra) jellemző mezők és metódusok is ezeket statikus mezőknek, statikus metódusoknak nevezzük. Az osztály tartalmazza a metódusok kódját. A metódusokat objektumokra hívjuk meg, és az objektumokban tárolt adatokon dolgozunk(kivéve a statikus metódusokat) (Osztály tartalma mezők,metódusok, tulajdonságok,események, operátorok,beágyazott típusuk) (beágyazott típusok: class, struct, interface, delegate) Az osztály tartalmazhat további osztály leírásokat. Ezeket beágyazott osztályoknak (nested class)hívjuk. Konsturktor:Minden osztálynak van legalább egy konstruktora. Ez egy különleges függvény, amely az osztály példányosításakor hívódik meg. o Feladata az osztály kezdőértékeinek beállítása, helyfoglalás a memóriában ,kezdeti műveletek végrehajtása,stb. Több konstruktort is megadhatunk, a fordító a szignatúrától függően választja ki a megfelelőt(Ha külön nem definiálunk konstruktort, akkor is létrejön.)Destruktor:Az elfoglalt memóriát szabadítja fel, eltakarít maga után. o Destruktorból pontosan egy darab van az osztályban. o Minden objektum külön-külön szüntethető meg (önállóan léteznek) o Az objektumok felszámolása lehet a programozó feladata vagy történhet automatikusan is. Osztály szintű (statikus) tagváltozó Ez az adattag csak egy példányban jön létre egy osztályon belül, független az objektumok számától, globális az adott osztály típusra nézve .(egyik objektumban megváltoztatjuk értékét akkor az összesben változik az értéke.) Többszörösen egymásba ágyazhatók az osztályok
1. ábra Osztály UML diagrammja
Metódusok általános típusai -
• Módosító metódusok – Megváltoztatják az objektum állapotát
-
• Kiválasztó metódusok – Hozzáférést biztosít az objektum adataihoz, de nem változtatják meg őket (így az objektum állapotát sem)
-
• Iterációs metódusok – Az objektum adatainak valamely részhalmazán „lépkednek végig”, és az adott részhalmazra vonatkozóan végeznek el műveleteket
Osztály kapcsolatok 1. Leszármazás (ún. „IS-A” kapcsolat) a. Egy osztály leszármazottai „öröklik” az osztály jellemzőit. b. A leszármazottak bővíthetik az ősosztály adatait és algoritmusait. c. A leszármazottak minden műveletre képesek, amelyre az ősosztály képes volt. 2. Asszociáció a. Az asszociáció osztályok közötti tetszőleges típusú viszony. b. Asszociációs kapcsolat áll fenn két osztály között, ha az egyiknek a saját helyes működéséhez ismernie kell a másikat. 3. Aggregáció és kompozíció (ún. „HAS-A” kapcsolat) a. Az aggregáció az asszociáció speciális esete: tartalmazási kapcsolat. (A tartalmazó és a tartalmazott osztály egymástól függetlenül létezhetnek) b. A kompozíció az aggregáció speciális esete: szigorú tartalmazási kapcsolat.( A tartalmazó és a tartalmazott életciklusa közös)
Implementációs lehetőségek //osztály deklarálása a "class" kulcsszó segítségével történik class Példaosztály { /* Itt kell deklarálnunk az osztály összes tagját (mezőket, metódusokat...) * A metódusok konkrét megvalósítását szintén itt kell megadnunk * Osztályok és objektumok tagjainak elérése: "." operátor * (példány szintű tagoknál a példány nevét, osztály szintű tagoknál az osztály nevét kell az operátor elé írnunk)*/ // A mező értéke helyben is megadható (inicializálás) string jegy = "jeles"; int j = -10; //csak olvasható mező readonly string SosemVáltozomMeg = "Nem hát!"; // konstans mezők const double pi = 3.1415; // A metódusok rendelkezhetnek megadott vagy változó darabszámú paraméterrel (params kulcsszó) void EgyParaméteresMetódus(bool l); void TöbbParaméteresMetódus(int a, int b, string név); void MindenbőlSokatElfogadóMetódus(params object[] paraméterTömb); //visszatérési értékkel void NincsVisszatérésiÉrtékem(); int EgészSzámotAdokVissza(int a); SajátTípus Átalakító(SajátTípus forrásObjektum, int mező); //konstruktor public Példaosztály(string jegy, int j) { //this paraméterrel hivatkozunk az éppen aktuális objektumra this.jegy = jegy; this.j = j; } // új objektum a new operátor segítségével hozható létre SajátTípus újObjektum = new SajátTípus(); //destruktor ~Példaosztály() }
Forrás:
http://people.inf.elte.hu/oriss/allamvizsga/at/gergo/M%FBszer/kell/Sztr_puska1.doc http://www.stud.u-szeged.hu/Horvath.Krisztian.4/Term02/progI_seged.pdf http://www.cs.elte.hu/linfo/Prog/Forditok/delphi/oop.html http://hu.wikipedia.org/wiki/Objektumorient%C3%A1lt_programoz%C3%A1s Kotsis Domonkos és Miklós Árpád OOP diák Sípos Mariann OOP diák Csink lászló OOP diák
59. Egységbe Zárás Az egységbezárás (encapsulation) azt jelenti, hogy a procedurális megközelítéssel ellentétben az adatok és a függvények a program nem különálló részét képezik, hanem azok egy egységes egészként jelennek meg, összetartoznak: együtt alkotnak egy objektumot. Az információ elrejtés (information hiding) azt jelenti, hogy az objektum felhasználójának nem kell tudnia arról, hogy az belsőleg hogyan működik, csak azt, hogy milyen funkcionalitást valósít meg, mi a felülete (interface).
Programozás szempontjából ennek több előnye is van az eljárás központú megközelítéssel szemben. Először is a különböző program részek közötti kötöttség lényegesen csökkenthető, és ennek biztosítására, kikényszerítésére nyelvi eszközöket is kapunk. Egy modul készítése esetén nem tudjuk elérni, hogy annak felhasználója esetleg nem megfelelő módon alkalmazza, ami inkonzisztens állapothoz vezethet. Egy objektum esetén szabályozni tudjuk a benne lévő változók és metódusok láthatóságát, biztosítani tudjuk, hogy a programozó az objektumot csak annak felületén keresztül tudja felhasználni.
A fentiből következik egy másik fontos előny is: az osztályunk belső működését bármikor tetszés szerint megváltoztathatjuk, amennyiben biztosított, hogy annak felülete változatlan marad. Ez azt eredményezi, hogy programunk sokkal rugalmasabban és nem utolsó sorban biztonságosabban átalakítható későbbi igények esetén. Például könnyen előfordulhat, hogy hatékonysági szempontok miatt szeretnénk a belső adattárolási formátumot megváltoztatni: OOP esetben csak arra kell figyelnünk, hogy az interfész ne változzon meg, míg procedurális megközelítésben ez jóval nagyobb körültekintést igényel, ráadásul a változtatások akár elsőre nem gondolt programrészekre is hatással lehetnek.
Névterek Az OO paradigma jellemzője az elnevezések óriási száma. Minden osztálynak, objektumnak, mezőnek, metódusnak egyedi nevet kell adni, hogy a későbbiekben hivatkozni lehessen rá. Nem könnyű jól megjegyezhető, a célt később felidéző neveket adni. A programok méretével egyenes arányban nő a névütközések valószínűsége. A programok általában nem csak saját osztályokat használnak fel. A névtér, mint az elnevezések érvényességének tartománya, hierarchikus logikai csoportokra bontja az elnevezéseket.
Minden elnevezésre csak a saját névterén belül lehet hivatkozni. Ennek megfelelően a saját névtéren belül minden elnevezés egyedi. A névterek általában tetszőleges mélységben egymásba ágyazhatók. Azonos elnevezések más-más névtereken belül szabadon használhatók, így erősen lecsökken a névütközés probléma jelentősége.
Láthatósági szintek (Mezők és metódusok védelme) A láthatósági szintek segítségével különítjük el az osztály belső, illetve kívülről és elérhető tagjait. Az egyes mezők és metódusok láthatósága külön-külön szabályozható. Alapvető láthatósági szintek:
Nyilvános (public): az adott tagot az osztály saját metódusai, az osztály leszármazottainak metódusai és az osztályt használó más osztályok metódusai használhatják. Ezzel a szinttel valósul meg az osztály külvilág számára látható „felülete”. Védett (protected): az adott tagot az osztály saját metódusai és az osztály leszármazottainak metódusai használhatják. Ez a szint a leszármazottak számára tesz elérhetővé bizonyos funkciókat. Privát (private): az adott tagot csak az osztály saját metódusai használhatják. Ezzel a szinttel valósul meg az osztály szigorúan belső jellegű funkcionalitása.
A láthatósági szintek segítségével hatékonyan, biztonságosan (adatrejtéssel együtt) valósítható meg az egységbezárás. Az osztályok a kívülről is látható elemeiket bocsátják más osztályok rendelkezésére. A nyilvános mezők adatokat, a nyilvános metódusok műveleteket tesznek elérhetővé. Az egyes osztályok megvalósítási részletei módosíthatók anélkül, hogy az osztályt használó más osztályoknak erről tudniuk kellene. A megvalósítást végző algoritmusok nincsenek kihatással az osztályt használó kódra. Konkrét OO nyelvi megvalósításokban általában további láthatósági szintek is léteznek.
Példány szintű tagok (Objektumokhoz tartozó mezők és metódusok) A példány szintű tagok a példányosított objektumok saját adatmezői, valamint saját adatain műveleteket végző metódusai vannak. A példány szintű mezők tárolják a példányok állapotát. Az egyes metódusok programkódját általában nem tartalmazza külön-külön az osztály minden példánya.
A metódusok minden példánynál azonosak és nem módosíthatók, ezért a metódusok programkódját az osztályon belül szokás tárolni. Szükség lehet azonban arra, hogy a példány szintű metódusok hivatkozni tudjanak arra az objektumra, amelyen a műveletet végzik. Példa: átadás paraméterként vagy eredményként; elnevezések egyértelműsítése. Ezt általában egy rejtett paraméterrel valósítják meg. Megnevezése nyelvi megvalósításonként változik („this”, „Self”, „Me” …). Mindig minden példánymetódusból elérhető, értéke a példány maga.
Osztály szintű tagok (Objektumokhoz tartozó mezők és metódusok) Az osztály szintű tagok az egyes osztályokhoz tartozó egyedi adatmezők, valamint az ezeken műveleteket végző metódusok. Osztály szintű adatmezők: minden osztály pontosan egyet tartalmaz belőlük, függetlenül az osztályból létrehozott objektumpéldányok számától.
Osztály szintű metódusok: akkor is elérhetők, ha az osztályból egyetlenegy példány sem létezik, csak osztály szintű adatmezőket használhatnak, konkrét példány nem igénylő feladatok végrehajtására is alkalmasak. Példa: főprogram megvalósítása. Speciális osztály szintű metódus az osztály szintű konstruktor, amelynek feladata az osztály szintű adatmezők kezdőértékének beállítása. Általában az osztályra történő első hivatkozás előtt fut le automatikusan.
2. ábra Osztály és példány szintű tagok
Tulajdonságok (intelligens mezők) A tulajdonság olyan nyelvi elem, amely felhasználás szempontjából metódusként viselkedik. Az adott osztály felhasználói mezőnek „látják” a tulajdonságot. A külvilág mezőként hivatkozhat a tulajdonságra. A tulajdonságot megvalósító osztály külön-külön metódust rendelhet a tulajdonság olvasási és írási műveletéhez. Olvasáskor a megfelelő metódus egy valódi (általában privát) mező értékét is visszaadhatja, de akár számítással is előállíthatja a visszaadott értéket. Íráskor a megfelelő metódus egy valódi (általában privát) mező értékét is módosíthatja, de végezhet egyéb műveleteket is vagy akár módosítás előtt ellenőrizheti az átadott értéket. A tulajdonság tehát felfogható intelligens mezőként. A tulajdonságok a mezőkhöz és a metódusokhoz hasonlóan különböző láthatósági szintekhez sorolhatók.
Gyakorlati megvalósítás /*Névtér: * A névterek az elnevezések tetszőleges logikai csoportosítását teszik lehetővé * Nincs közük a fizikai tároláshoz * Tetszőlegesen egymásba ágyazhatók*/ namespace A { namespace B { class Egyik { } } } namespace A.B { class Másik { } } /* Az "Egyik" osztály ugyanabba a névtérbe tartozik mint a "Másik" osztály * * Minden névre a saját névterével együtt kell hivatkozni * A névterek importálhatóak: */ /* Hatókörök * kijelöli a változók érvényességi tartományát * helyi változók a deklarációjukat tartalmazó blokk vagy metódus lezárásáig ("}") érhetők el Láthatósági szintek*/ public string x // Korlátlan protected string x Adott osztály és leszármazottai internal string x //Adott program, adott osztály protected internal string x //Adott program, adott osztály és leszármazottai private string x //Adott osztály /* A névterek láthatósága mindig public, A típusok (osztályok) láthatósága public vagy internal .Egy osztályon belül is létrehozhatunk több azonos nevű, de eltérő paraméterlistával és visszatérési értékkel rendelkező metódust. Az osztály szintű mezők az osztály saját adatmezői minden osztály csak egyet tárol ezekből a mezőkből, függetlenül a később létrehozott példányok számától*/
public static int Darabszám; /* Az osztály szintű metódusok magán az osztályon működnek (akkor is hívhatók, ha 1 példány sem létezik az osztályból) csak osztály szintű mezőket használhatnak nem létezik aktuális objektum így "this" paraméter sem konkrét példány nem igénylő feladatra is alkalmasak*/ public static void Main() {} /* this paraméter a példány szintű metódusokban szükség lehet rá, hogy hivatkozni tudjunk arra az objektumra, amelyik a metódust éppen végrehajtja a rejtett this paraméter minden példány szintű metódusban az aktuális objektumot jelöli akkor használatos amikor az aktuális objektumot paraméterként vagy eredményként szeretnénk átadni (pl. konstruktor)*/ public Osztály(string név, int kor) { this.név = név; this.kor = kor; } //Tulajdonságok Public string Name { get {return name;} set{name=value;} } //hívó kódban úgy viselkedik, mint egy tagváltozó, de értékátadáskor set-en lehíváskor get metódussal.férünk csak hozzá. Rejtett adattagok kezelésére alkalmas.
Forrás:
http://weblabor.hu/cikkek/oopkodujrahasznositas
http://www.eet.bme.hu/publications/e_books/progr/cpp/node81.html
http://aries.ektf.hu/~dream/e107/e107_files/downloads/OOP_folia_1.pdf Kotsis Domonkos és Miklós Árpád OOP diák Sípos Mariann OOP diák Csink lászló OOP diák
60. Öröklés (elvi háttere, gyakorlati megvalósítás) Az öröklés egy igen fontos fogalom az OOP nyelvekben. Sok hasonló, de csak kis mértékben különböző osztályok esetén használjuk, illetve ha valamilyen speciális tulajdonsággal szeretnénk az osztályunkat felruházni. Az öröklődés során származtatással hozunk létre egy már meglévő osztályból egy újat, ez egy specializációs művelet („származtatás). A „leszármazottak” „öröklik” az őstípus tulajdonságait. A leszármazottak bővíthetik, esetenként akár szűkíthetik az őstípus állapotterét, illetve műveleteit. Teljes leszármazási hierarchiákat is létrehozhatunk. Kiváló lehetőség a közös tulajdonságok, műveletek összevonására és újrahasznosítására. Az alapelv következetes alkalmazásával elérhető, hogy a már megvalósított funkcionalitás később a megvalósítás részleteinek ismerete nélkül is felhasználható legyen. Jól átgondolt előzetes tervezést igényel.
A leszármazottak tényleges felépítése
T ö b b s z ö r ö s
ö r ö k l é s (Öröklés egynél több ősosztállyal)
Többszörös öröklés esetén egy osztálynak több őse van. Ez egy sokat vitatott lehetőség néhány OOP megvalósításban.
Előnyök:
egyszerre több ősosztály képességei örökölhetők, több származtatási szempont érvényesíthető egyszerre, több ős tulajdonságait ötvöző „vegyes” osztálytípusok, nagyobb rendszereknél gyakran felmerül az igény rá.
Hátrányok:
igen nehéz megvalósítani, komoly megvalósítási problémák: adatmezők öröklése, közös közvetett ősök (leszármazás több útvonalon), nagyobb rendszereknél szinte lehetetlen jól, következetesen megvalósítani, bonyolítja a programozási nyelv eszközeit, a programok tervezését
Alternatív megoldás az interfészek rendszerével történő megvalósítás. Az interfészek megvalósítási előírásait ilyenkor mind meg kell valósítani az adott osztályban. Adatmezők öröklése, leszármazás több útvonalon
S p e c i á l i s
m e t ó d u s o k
ö r ö k l ő d é s e (Konstruktorok és destruktorok)
A konstruktorok és destruktorok nem feltétlenül öröklődnek. Az objektumok létrehozásakor végre kell hajtani minden egyes ős valamelyik konstruktorát is. Ez szintén része a konstruktorok alapvető feladatkörének.
Hívási sorrend
1) A konstruktorok leszármazás szerinti sorrendben hajtódnak végre. Először az ősök adatait kell inicializálni, hiszen a leszármazottak ezeket akár már a saját konstruktorukban is fel kívánhatják használni. 2) A destruktorok leszármazás szerinti fordított sorrendben hajtódnak végre. Először az utódok esetleges objektumait kell felszámolni, hiszen az utódok destruktora még hivatkozhat az ősök belső objektumaira. Gyakorlati megvalósítás /* az örökölt metódusok a leszármazottakban módosíthatók * a leszármazottak új tagokkal bővíthetik az ősosztálytól örökölt tagok halmazát * minden osztály közös őse a System.Object osztály * A leszármazott osztályok deklarációjánál " : " karakterrel elválasztva meg kell adnunk az ősosztály nevét is (kivéve ha az ősosztály a System.Object)*/ class Állat { int lábszám; public Állat() { } public void Fut() { } } class Emlős : Állat { public bool KicsinyétEteti() { } } class Kutya : Emlős { public void FarkátCsóválja() { } public void Ugat() { } }
/* A konstruktorok nem öröklődnek * Ha az ősosztályban van paraméter nélküli konstruktor, az a leszármazott osztály konstruktorában automatikus meghívódik * Ha az ősosztályban nincs paraméter nélküli konstruktor, az ősosztály konstruktorát meg kell hívni a leszármazott osztály konstruktorából (erre a célra a "base" kulcsszó áll rendelkezésre)*/ //1. helyes megoldás class A {} class B : A { public B(int x) { } } //2. helyes megoldás class A { public A() { } } class B : A { public B(int x) { } } //3. helyes megoldás class A { public A(int x) { } } class B : A { //konstruktor felülírás public B(int x) : base(x) { } } Forrás : http://hu.wikipedia.org/wiki/Objektumorient%C3%A1lt_programoz%C3%A1s http://www.gtbbp.hu/~szabol/tetelek/programming/B/objektum.html
61. Polimorfizmus (elvi háttere, gyakorlati megvalósítás) Többalakúság: A különböző, egymásból származó objektumtípusok hasonló műveletei a konkrét objektumtól függően más-más konkrét megvalósítással rendelkezhetnek.
Mező- vagy típus-polimorfizmus : Öröklés során az öröklött mezők a leszármazottakban is elérhetők, statikusan viselkednek, nem változtathatók meg öröklés során. Minden leszármazott tartalmazza minden korábbi ős összes mezőjét, emellett tetszőlegesen bővítheti is a mezők körét. A mezők ennél fogva (a mai OO programnyelvekben) nem képesek többalakú viselkedésre.
Metódus-polimorfizmus: A különböző, egymásból származó objektumtípusok hasonló műveletei a konkrét objektumtól függően más-más konkrét megvalósítással rendelkezhetnek. Ennek a gyakorlati megvalósítása a polimorfizmus. Nem virtuális metódusok esetén két probléma léphet fel.
Az utódban nem felüldefiniált metódus hívása esetén az ősosztályban definiált metódus hívódik meg. Amennyiben a meghívott metódus tartalmaz az utód osztályban felüldefiniált metódushívást, akkor az ős osztálybeli metódust hívja meg és nem az utódosztálybelit. Pl.: CA{ Add(int addx) //hozzáadunk az x-hez addx-et és meghívja a Show metódust Show() //Show metódus kiírja x értékét x=1 } CB:CA{ Show() //Show metódus kiírja a x és y értékét y=3 } A CB osztályban Add metódus hívás esetén a metódusban szereplő Show metódus az ősosztálybeli Show metódust hajtja végre.
Ősosztály referencia típusú hivatkozás során az utódot példányosítva az ősosztály metódusai lesznek csak elérhetők. Az előző példa osztályait használva a „CA b=new CB()” példányosítása után az Add metódusba épített Show metódus az ősosztályban definiált módon fogja kiíratni az értékeket.
Virtuális metódusok:
Az utódban nem felüldefiniált metódus hívása esetén az ősosztályban definiált metódus hívódik meg. Amennyiben a meghívott metódus tartalmaz az utód osztályban felüldefiniált metódushívást, akkor az utód osztálybeli metódust hívja meg. A nem virtuális metódusoknál használt példa osztályokat felhasználva A CB osztályban Add metódus hívás esetén a metódusban szereplő Show metódus az utódosztálybeli Show metódust hajtja végre.
Ősosztály referencia típusú hivatkozás során az utódot példányosítva az utódosztály metódusai lesznek. Az előző példa osztályait használva a „CA b=new CB()” példányosítása után az Add metódusba épített Show metódus az utódosztályban definiált módon fogja kiíratni az értékeket.
Virtuális és nem virtuális metódusok utódaiból a „base.metódusnév” alapján tudunk hivatkozni az ősosztálybeli metódusokra. Késői kötést valósíthatunk meg az abstract, virtual, override módosítókkal. • Az absztrakt metódus esetén nincs implementációja (megírt kódja) a metódusnak. Utód osztályban a metódust csak virtual kulcs szóval ellátva tudjuk felüldefiniálni. Override kulcsszót használjuk az implementált (felüldefiniált) metódus utódosztályban, ahol megszeretnénk hívni. • A virtual kulcsszóval késői kötésű függvényt hozunk létre, melyet az override módosítóval ellátott azonos nevű és paraméterlistájú függvény helyettesíthet (felüldefiniálhat) valamely utódosztályban. • Override metódus tovább overridolható. • Sem az abstract, sem a virtual módosítóval ellátott függvény nem lehet private láthatóságú, az override metódus pedig nem módosíthatja az eredeti függvény láthatóságát.
Virtuális metódusok címét az osztályok a virtuális metódus táblában tárolják (VMT). Ha virtuális metódust hívtunk, akkor a ténylegesen futtatott metódus címét az objektumpéldányhoz tartozó VMT-ből nézi ki a keretrendszer, a példányokhoz csatolt VMT a konstruktorral együtt inicializálódik (ekkor kerülnek bele a VMT-be a virtuális eljárások példány típusához kötött címei).
Virtuális, nem virtuális metódusok összehasonlítása: Attól függően, hogy virtuális vagy nem virtuális a metódus, azonos metódushívás mást jelenthet. Nem virtuális metódusnál a változó típusa, virtuális metódusnál a példány típusa szabja meg, hogy melyik eljárás hívódik meg.
Elérhetőségük Rendelkezésre állás
Metódushívás
Viselkedésük öröklés után
Tárolás (objektumokat tagváltozókban tároljuk.)
Előnyök
Hátrányok
Többalakúság
nem virtuális metódusok virtuális metódusok A metódusok elérhetők a leszármazottakban, elérésüket csak a láthatósági szintjük határozza meg. Ez az információ már példányosításkor dől el, hogy a fordításkor ismert, így a tagváltozó ténylegesen mely fordítóprogram előre osztály egy példányát elkészítheti a hívást tartalmazza. Dinamikus (futási megvalósító gépi kódot. idejű vagy „késői”) kötésnek Statikus kötésnek nevezzük. nevezzük. Az adott objektum deklarált Az adott objektum tényleges típusának megfelelő osztály típusának megfelelő osztály adott nevű metódusa hívódik adott nevű metódusa hívódik meg. meg Korlátozottan módosítható Szabadon módosítható (újra deklarálhatók; nincs (felülbírálhatók) kapcsolat ős és utód között) A tagváltozók deklarált típusa A tagváltozók tartalma választja ki a hívandó metódust nemcsak a deklarált osztály lehet, hanem annak összes leszármazottja is. - egyszerű megvalósítás - Többalakúság - nincs futási idejű - Rendkívüli rugalmasság teljesítményvesztés nem alkalmas többalakúságra - teljesítmény veszteség - nagyobb tárigény - bonyolultabb megvalósítás nem alkalmasak többalakúság megvalósításának többalakúságra legfőbb eszközei
Absztrakt osztály: Olyan osztály aminek legalább 1 absztrakt metódusa van. Absztrakt osztály esetén csak előírjuk a kötelezően megvalósítandó metódusokat és változókat. Kódot nem tartalmaz, ezért nem példányosít ható.(céljuk az öröklés kikényszerítése.) Öröklések funkcionális előírásainak betartására használjuk, segítségükkel általános funkcionalitás adható meg az öröklés hierarchia felsőbb szintjein.(leszármazottban biztosan meg kell valósítani!)
Lezárt osztályok: Az öröklés és ezzel a többalakúság megakadályozása. Védeni akarjuk osztályunkat a későbbi örökléssel való felhasználástól. Virtuális metódusaik átírhatók statikus metódusokká. Véglegesnek szánt osztályok, illetve metódusok esetén lehet célszerű alkalmazni ezt a megoldást
Virtualitás lehetősége különböző nyelvekben:
C++:
@@ VMT helyett, minden objektumban tároljuk a virtuális metódusok címét, ezért az objektumok mérete megnő.( Kevés objektum esetén nem jelentős a változás.) @@ A program sebessége minden virtuális függvény hívásakor lassabb lesz.
JAVA: @@ Nincs „virtual” kulcsszó mert minden virtuális. @@: Osztály metódusnál az adott osztályé hívódik meg mindig.(nincs virtuális metódus) @@ Alkalmazásuk egyszerűbb, de a virtuális fgvk idő igényesebbek.
C#: @@ Alapértelmezettként a metódusok nem virtuálisak. @@ „Overide”-olni csak azokat a metódusokat lehet amelyek az ősében szerepel „virtual” vagy „abstract” szó. @@ Abstract ill Virtuális tag függvénye nem lehet private láthatóságú. @@ „Override”-olás során nem módosíthatjuk a metódusok láthatóságát.
C# gyakorlati megvalósítása: /* A nemvirtuális metódusok változatlanul örökölhetők vagy a leszármazottakban elrejthetők * A leszármazott osztályban célszerű az újonnan bevezetett metódust a "new" kulcsszóval megjelölni */ class Állat { public void Fut() { Console.WriteLine("Az állat így fut."); } } class Kutya : Állat { new public void Fut() { Console.WriteLine("A kutya így fut."); } } class NemVirtuálisMetódusok { static void Main() { Állat egyikállat = new Állat(); egyikállat.Fut(); //Az Állat osztály Fut() metódusa hívódik meg Kutya másikállat = new Kutya(); másikállat.Fut(); //A Kutya osztály Fut() metódusa hívódik meg Állat házikedvenc = new Kutya(); házikedvenc.Fut(); //Ismét az Állat osztály Fut() metódusa hívódik meg } }
/* A virtuális metódusok a leszármazottakban módosíthatók vagy elrejthetők * A virtuális metódusokat az ősosztályban a "virtual" kulcsszóval kell megjelölnünk * A leszármazottakban felülbírált virtuális metódusokat az "override" kulcsszóval kell megjelölnünk */ class Állat { public virtual void Fut() { Console.WriteLine("Az állat így fut."); } } class Kutya : Állat { public override void Fut() { Console.WriteLine("A kutya így fut."); } } class VirtuálisMetódusok { static void Main() { Állat házikedvenc = new Kutya(); házikedvenc.Fut(); //A Kutya osztály Fut() metódusa hívódik meg } }
// Absztrakt osztály abstract class Alakzat { public abstract void Kirajzol(); } class Ellipszis : Alakzat { public override void Kirajzol() { // Kirajzol() metódus az Ellipszis osztály megvalósításában } } class Kör : Alakzat { public override void Kirajzol() { // Kirajzol() metódus a Kör osztály megvalósításában } } /* Lezárt osztályból nem származtatható másik osztály * Lezárt metódus leszármazottakban nem bírálható felül * A lezárt osztályok és lezárt metódusok célja az öröklés megakadályozása * A lezárt osztályok és metódusokat a "sealed" kulcsszóval kell megjelölnünk */ sealed class LezártOsztály { sealed public void LezártMetódus() { } }
62. Interfészek (elvi háttere, gyakorlati megvalósítása) Az interfészek célja, hogy kapcsolófelületet teremtsen a programkód készítője és a kód felhasználója között. Interfészek segítségével lehetőségünk nyílik időben egymástól elhatárolt fejlesztések végzésére. Az interfész önmagában nem példányosít ható, valójában egy tervezési eszköz. Ahhoz, hogy tudjuk használni, implementálni kell egy osztálydefinícióval, csak publikus és absztrakt metódusokat valamint publikus és statikus konstansokat tartalmazhat. A fent említett tulajdonság miatt gyakran „üres” osztályoknak is nevezzük. Az implementálás azt jelenti, hogy az interfész összes metódusának konkrét jelentést adunk (megvalósítjuk). Az így implementált osztály már használható, sőt ősosztályként is megjelenhet.
Interfész megvalósítása (támogatása): Egy osztály akkor „támogat” egy interfészt, ha kötelezően vállalja, hogy megvalósítja a benne foglalt funkcionalitást. Az interfész - osztály kapcsolata CAN-DO kapcsolatra hasonlít. Interfész tartalmazza az előírt megvalósításokat, az osztályok pedig tartalmazzák a konkrét megvalósítást. Interfészek önmagukban nem példányosít hatók, csak az őket megvalósító osztályokon keresztül érhetők el a műveleteik. Az interfész megvalósításának lépései
osztály definícióban a megvalósítandó interfészek felsorolása az interfész(ek)ben definiált metódusok implementálása
Egy osztály egyszerre tetszőleges számú interfészt valósíthat meg, ezzel a polimorfizmus szempontjából a többszörös örökléshez hasonló eredményt érhetünk el Egy osztálynak kötelező implementálnia az általa megvalósított interfészek által definiált metódusokat (ellenkező eset fordítási hibát eredményez) Kivéve az absztrakt osztályokat, ahol nem szükséges minden metódust megvalósítani
Implicit/explicit interfész megvalósítás: A többszörös öröklésnél felmerülő problémák közül néhány felmerül a több hasonló interfészt megvalósító osztályok esetén is.
Több interfész is tartalmazhat ugyanolyan szignatúrájú metódusokat, ennek kezelése érdekében kétféle interfész megvalósítást használhatunk:
Implicit megvalósítás o Az osztály metódusának a szignatúrája megegyezik az interfész(ek)ben megadott szignatúrával. o Bármelyik interfésszel hivatkozunk az osztályra, mindig ugyanaz a metódus fut le. Explicit megvalósítás: o az osztályban a metódus neve mellett megadjuk az általa megvalósított interfész nevét is attól függően, hogy melyik interfésszel hivatkozunk az osztályra, mindig a megfelelő metódus fut le. o Célszerű ezt a megvalósítást használni.
Interfész típusú referencia: Az interfészek tulajdonképpen típusok, ezért lehetséges ilyen típusú változók deklarációjára is Egy T típusú osztályra az alábbi típusú referenciákkal hivatkozhatunk (más típus fordítási hibát eredményez):
T típusú referenciával T valamelyik őstípusának referenciájával T osztály által megvalósított valamelyik interfész típusának referenciájával egyéb: konverziók, casting stb.
Az interfész típusú referenciák az osztály típusú referenciákhoz hasonló módon működnek Ez nem keverendő össze azzal, hogy az interfész típusból nem lehet példányt létrehozni!
Interfész hierarchia: Az osztályokhoz hasonlóan az interfészek között is fel lehet építeni egy öröklődési hierarchiát (öröklődés helyett itt gyakran a kiterjesztés szót használjuk) Az osztályok és az interfészek hierarchiája egymástól független, interfész őse nem lehet osztály és osztály őse sem lehet interfész (az osztályok közti öröklést és az interfész megvalósítását tekintsük különbözőnek) Az osztályokhoz hasonlóan az interfészek is általában mind egy legmagasabb szintű ősből származnak Az osztályokhoz hasonlóan a polimorfizmus előnyeit az interfészek között is alkalmazhatjuk (minden interfész használható bármelyik őse helyén)
Osztály hierarchia: Amennyiben egy osztály megvalósít egy interfészt, akkor a leszármazottjai is mind megvalósítják. Ennek direkt jelölésére általában nincs szükség A megvalósító metódusokat a leszármazottak öröklik, így értelemszerűen nincs szükség további követelmények teljesítésére Egy T típusú osztályra tehát az alábbi típusú referenciákkal hivatkozhatunk:
T típusú referenciával T valamelyik őstípusának referenciájával T osztály által megvalósított valamelyik interfész típusának referenciájával T osztály bármelyik őse által megvalósított interfész típusának referenciájával T osztály által megvalósított interfész bármelyik őstípusának referenciájával (egyéb: konverziók, casting stb.)
Néhány további gondolat:
Jelölő interfészek (marker interface) o nincsenek metódusaik o az osztályhoz rendelve futás közben lekérdezhetők, ezzel a futtató környezet (vagy reflexión keresztül az egyéb programok) számára nyújt információt o ha van helyette más nyelvi elem, célszerű elkerülni a használatát Segítő osztályok (helper class) o egy összetett interfész esetén gyakran csak néhány metódus megvalósítására lenne szükség, azonban mindig kötelező mindet implementálni o kényelmi szempontból az interfészekhez gyakran készítenek egyszerű, az interfészt üres (vagy alapértelmezett kóddal) megvalósító ún. segítő osztályokat o Nem célszerű interfészt új metódusokkal bővíteni, mivel így az ezt implementáló osztályok fordíthatatlanná válnak
Gyakorlati megvalósítások: Nevüket I betűvel kezdjük és az interface kulcsszóval látjuk el. A gyakorlatban a metódusok public és abstract kulcsszavai és a mezők final , public és static kulcs szavai is elhagyhatók, mert az interfész osztály csak ilyen metódusokat tartalmazhat. (Fordító eleve elé rakja.)
namespace BoltiAlkalmazas { interface ITulajdonsag { bool T(); } interface IKonyvelheto : ITulajdonsag { int Osszeg(); } class Alkalmazott : IKonyvelheto { string nev; int fizetes; public int Osszeg() { return -fizetes * 12; } #region Tulajdonsag Members public bool T() { return fizetes < 50000; } #endregion } }
class Aru : IKonyvelheto, ITulajdonsag
{ …//megvalósítási kódok }
//több interfész megvalüsítása