A múlt héten "megtanultam" objektumorientáltan programozni. Na nem mintha nem tanultam volna ezt korábban, sőt, ifjú éveimben írtam egy komplett Windowst Turbo Pascalban, ami alaposan ojjektumorientált volt, a pontból származott a vonal, a vonalból a téglalap, a téglalapból az ablak stb.
Ez a dotnetes ojjektumorientáltság viszont sehogy sem ment a fejembe a múlt hétig. Mert persze, a string egy objektum, naná, minden stringem rendelkezik frankó metódusokkal, mint pl. ToUpper(), meg vannak statikus metódusai is, pölö string.Format(), és ez oké, de ez rám, mint programolóra ugyan milyen hatással van, ha ugyan van? Továbbra is a { jelnél kell elkezdeni a programot, és a }-nél ér véget, nemde?
Kitérő: egyik oktató kolléga háborog, hogy állandóan az interfészek fogalmát kell elmagyaráznia, mint az óvodában, mert a népeknek fogggggalmuk sincs az objektumorientált programozásról, pedig tanulták. Lapítottam eddig csendesen, hogy ez akár én is lehetnék, mindent tudok az objektumorientált programozásról (enkapszuláció, öröklődés, metódus, minden-minden), mégsem férnek a fejembe az interfészek :)
Rájöttem, hogy ezek a mai fiatalok egyszerűen "visszaélnek" az objektumokkal, és nem csak arra használják, amire valók, hanem mindenre. Gyakorlatilag minden változójuk objektum, minden átadott paraméterük objektum, sőt, sok esetben komplett kollekció. Mi annak idején azt tanultuk, hogy nézz körül a világban, és ha valami nagyon úgy néz ki, mint egy egybefüggő entitás, csinálj belőle objektumot. Egyéb esetekben ott van neked a sok-sok lokális változó a sztekken, jó az, nomeg gyors is.
A mai fiatal meg ezt mondja: "nézd már, hű de sok paramétert kell átadni ennek a metódusnak, csináljunk belőle egy ojjektumot, és akkor csak egy paraméterünk lesz!" Esetleg még hozzáteszi: "a Unit-tesztelést is milyen jól megkönyíti, hogy csak egy paraméterünk van, és nem öt!".
Vagyis teljesen más megfontolások alapján ontják magukból az ojjektumokat, mint anno mi tettük. Ontják. Két értéket kell átpasszolni valahova? Hopp, egy anonim objektum, nesze!
new {egyikertek=5, masikertek=66}
Ügyes, ügyes, én tutira nem csináltam volna. Egészen a múlt hétig.
A másik társaság, aki orrba-szájba objektumozik, az a Microsoft. De naggggggyyyyyyyyyyon dúúúúúúúúúúúúúúúúrván! Az egyik legszebb példa a helper metódusok csodálatos világa. Van egy gyári ojjektumtípus, mondjuk a string. Van neki egy rahedli statikus metódusa, de neked ez nem elég, azt szeretnéd, ha a stringek tudnának még valamit pluszban, mondjuk - hogy teljesen gyakorlatias példát adjak - visszafelé beszélni, Indulagörögaludni, Rámnémetnemlel,elmeteménmár. A metódus neve legyen mondjuk Palindrom.
Oké, ez szuper. Mekkora eséllyel fogjuk tudni ezt az elképzelést átverni Redmondon? "Hahó, Bill, hahó Steve! Kellene a string osztálynak plusz egy metódus! Nagyon kellene!"
Ugye, hogy ez kivitelezhetetlen? Ugyanakkor helyben meg tudjuk csinálni magunknak. meg lehet patkolni bármelyik osztályt, és rá lehet aggatni plusz metódusokat. És aki ezt nem tudja, az dotnet-vak, vagy legalábbis dotnet-hülye. Így köll csinálni:
public String Palindrom(this String bemeno)
{
...blablabla, meg is kellene írni, de nem írom meg...
}
A szintaxis elképesztő: az első "bemenő" paraméter neve előtti this kulcsszó indítja be az átváltozást. A compiler felismeri, és azt mondja: "aha, ezek a Stringet akarják új metódussal megfejelni!" - és megcsinálja.
Ilyenkor az ember hátán hirtelen feláll az összes szőr, nomeg a molekulákok, és elordítja magát: "de hát miéééééééééééééééééééért?!"
Meglepő módon van rá válasz: ezzel a módszerrel úgy tudtuk bővíteni a stringet, hogy semmilyen kész alkalmazást nem kell átírkálni és újrafordítani a használatához. (Ellentétben a leszármaztatással és öröklődéssel.) Egy DLL-be bele lehet hányni a plusz metódust, belerakni a projectbe, és BÁRMELYIK stringen azonnal használni. Hát ezért.
És akkor az interfész. Ami egy olyan öröklődés, ami semmit nem örököltet. Ha valaki megnézi .net Reflectorral egy interfész definícióját, azt találja, az üres. MINDIG üres. Ha egy interfészből származtatunk, a SEMMIT fogjuk megörökölni. Értelmetlen!
Hacsaaaaaaaaaaaak...!
Ez egy hosszabb történet. Kezdődik azzal, hogy a C#-ban már nincs többszörös öröklődés. Minden objektumnak csak egyetlen őse lehet. Ezt a korlátozást egyrészt az áttekinthetőség, másrészt a statikus öröklődés visszaszorítása érdekében tették. A Design Patternek sokkal többet hoznak a konyhára, mint amennyit az öröklődés csúnya korlátozása elvisz.
Igen ám, de ezzel látszólag megszűnt a lehetősége annak, hogy egy objektum hol Alma legyen, hol Körte, a hívási körülményeknek megfelelően. Hol dugóhúzó legyen, hol körömreszelő, hol meg csavarhúzó. Erre találták ki az interfészeket, amelyek egy-egy szerszámot jelentenek a svájci bicskán. Jobban mondva egy interfész nem más, mint annak definíciója, hogy az adott objektumot LEHET dugóhúzóként használni. De hogy HOGYAN lehet, azt nem határozza meg, ezért üres. Azért üres, mert honnan a bánatból tudhatná dugóhúzó feltalálója, hogy egy sokkal később kifejlesztett objektumot hogyan lehet majd dugóhúzóként használni?
Interfészből pediglen korlátlan számúval fel lehet szerelni az objektumokat, de most már tudjuk, azon az áron, hogy okozunk magunknak egy csomó programoznivalót, mert a HOGYAN a mi feladatunk marad. (Ellentétben az öröklődéssel, ahol a HOGYANT készen kapjuk, az ősosztályé szépen működik alapból.)
Így lehet lértehozni a Dugóhúzó interfészt:
interface IDugohuzo
{
public void BalraKiteker();
}
Ennyi. Hogy hogyan kell balra kitekerni egy tetszőleges objektumot, az az objektum fejlesztőjének feladata.
Hogyan csinálunk a tányérból dugóhúzót?
class Tányér: IDugohuzo
{
}
Ennyi. Innentől kutya kötelességünk megvalósítani, implementálni a BalraKiteker() metódust, különben a kód le sem fordul. Homályos, ugye? Eddig még nekem is az lenne. Mi a túróra jó ez? Mi ez a kavarás? Miért nem egyszerűen csak beleírom a metódust, miért nem hagyom ki az interfészt a csudába? Most jön a lényeg!
Vannak olyan rendszerelemek, amelyek csodálatosan kezelgetik a mi saját objektumainkat, HA felismerik rajta az általunk keresett csatlakozót, interfészt. A legelterjedtebb, legismertebb ilyen rendszer például a LinQ, amelyik kvázi SQL-nyelven kezelhetővé teszi az objektumainkat, lehet szelektálni a tányérjainkat, lehet sorbarendezni őket (WHERE matyóhímzés) stb. Ehhez "csupán" arra van szükség, hogy a saját Tányér objektumunk implementálja a "központilag előírt" interfészt. Kész.
(A fenti példa nem igazán szerencsés, mert ráadásul a LINQ-eléshez bőven elegendő, ha a kollekciónk IEnumerable, nem kell, hogy maga a tányér is az legyen. Tehát a tányérokat belerakjuk egy List<>-be, és máris mehet a tányérzsonglőrködés)
Az MSDN-ben van egy versenyzős példa, az talán kifejezőbb. Van egy versenypálya a városban, ahol olyan izék tudnak versenyezni, amelyek implementálják az IVerseny interfészt, és azon belül van Sebesség propertijük. Szeretnénk használni a versenypályát, vagy úgy is fogalmazhatnánk, azt szeretnénk, ha beengednének minket a versenypályára. Ha van egy autó objektumunk, el tudjuk indítani a versenyen, ha implementáljuk az IVerseny interfészt, és kitöltjük a Sebesség propertit, már rajthoz is állhat. Van egy nyulunk is, azt is szeretnénk indítani a versenyen. Ehhez bizony a nyuszira is fel kell szerelni az IVerseny interfészt, és meg kell adni a sebességét, és már mehet is!
Vagy legyen ez: van egy állás, bárki betöltheti, aki rendelkezik MCSEFGHIJ-képesítéssel, vagyis implementálja a MCSEFGHIJ-interfészt. Ehhez a delikvenst fel kell szerelni a kívánatos interfésszel: tanul, levizsgázik, megszerzi az MCSEFGHIJ képesítést, és máris mehet a meló!
Momentán több ojjektumorientált trükk nem jut eszembe, a tanfolyam, amit elvégeztem: ASP.NET MVC :) Köszi Tocsi, egészen megvilágosodtam! :)