Objektum orientált programozás hiba volt
Egy szakmának a tanulás és tanulmányozása általában elég sok időbe és energiába telik, mivel általánosan elmondható róluk, hogy nagyon nagy és változatos tudás bázisra épülnek, ami folyamatosan fejlődik és bővül. Pontosan emiatt nehéz róluk általánosan beszélni, de ebben a bejegyzésben mégis meg próbálom ezt megtenni.
Imperatív volt a legelső paradigma, amit megkérdőjeleztem, mivel van egy bizonyos tulajdonsága, ami egy olyan kétélű fegyver, ami jobban szeret minket megszúrni, mint magát a problémát. Itt side effect(vagyis mellék hatás)-ként ismert tulajdonságról beszélek. Mellék hatások abból a szempontból fontosak, hogy minden input/output esemény is annak számít.
Számomra a side effect mindig 2 dolgot jelenthetett:
A második eset pedig abból a szempontból problémás, hogy a függvény függ egy változótól, aminek az értéke bármikor módosulhat, ami azt jelenti, hogy a függvény függ a külső változón végrehajtott változásoktól és ami még rosszabb, hogy a függvény is módosítani tudja ezt a változót. Ebben az esetben még rosszabb a tesztelhetőség, mivel teszteléskor a külső változóra és annak értékeire is figyelni kell.
Ennek a két esetnek a kombinációja pedig tragikusan komplex mellék hatásokat képes előidézni, ami a függvénynek a tesztelhetőségét is tragikusan megkárosítja.
Ez a két esett elégé különbözőnek hangzik de mégis általában ugyanarra az eseményre lehet visszavezetni, ami nem más mint az értékmódosítás. Az értékmódosítás azért olyan veszélyes, mert minden mellékhatás erre az utasításra vezethető vissza. Például I/O műveletek is csak annyiból állnak, hogy a gép bizonyos regisztereinek megváltoztatjuk az értékeit.
Értékmódosításnak még meg van az a kellemetlen hatása, hogy programunk futásába bevezeti az idő fogalmát, ami azt jelenti, hogy fontossá válik, hogy az adott változónk értéke adott pillanatban éppen menyi. Mivel ha adott pillanatban éppen rossz értéke van, akkor valószínűleg helytelen futást vagy hibát fog eredményezni. Másképpen úgy is lehet fogalmazni, hogy értékmódosítás miatt a változó egy állapot változóvá válik, ami azt jelenti, hogy ügyelnünk kell, hogy futás során mindig jó állapotba legyen ez a változónk, különben baj lesz.
Térjünk vissza az előző két esetünkhöz és képzeljük el, hogy nem engedjük meg az érték módosítást:
Ebből is látszik az imperatív és funkcionális programozás közötti különbség, míg funkcionális nyelvek a pure függvények használatát hangsúlyozzák, addig az imperatív nyelvek azok ellentétét az impure függvények használatát hangsúlyozzák.
Másik kellemes hatása az értékmódosítás betiltásának, hogy nem alakulnak ki állapot változók, mivel minden érték változás pure függvények segítségével megy végbe, ami azért jó mert így a változások explicit módon mennek végbe függvények segítségével, vagyis egy bizonyos új érték elkészítését csak függvény hívásokkal tudjuk elérni úgy, hogy nem változtatjuk meg a változó eredeti értékét és ezáltal nem vezetünk be állapot változókat.
Imperatív paradigma hibái ellenére mégis fontos, mivel vannak olyan algoritmusok és adatszerkezetek, amiket könnyebben lehet imperatívan megvalósítani. Nem is beszélve olyan helyzetekről, amikor nagyon fontos egy olyan gépközeli nyelv használata, amivel pontosan tudjuk irányítani a program futását és erőforrás használatát.
Viszont az esetek többségében az ember jobban jár a funkcionális programozással vagy legalábbis funkcionális stílus használatával, mivel ebben az esetben nem csak jól tesztelhető, hanem könnyebben követhető kódot is tud készíteni az ember.
Az összes népszerű objektum orientált nyelv imperatív alapokon nyugszik, ami azt jelenti, hogy az objektum orientált nyelvek örökölték az imperatív nyelvek erősségeit és annak gyengeségeit is egyúttal. Viszont maga az ötlet is bevezettet néhány új gyengeséget:
Ez a fogalom a modell ellenőrzés világából származik és a lényege annyi, hogy minél több állapot változót használsz annál exponenciálisan nehezebben lesz érthető az egész programod működése.
Minden imperatív nyelvben gondot okoznak az állapot változóknak a száma, de egy objektum orientáltál nyelvben még súlyosabb a helyzet mivel az objektumok definíciójában benne van, hogy minden objektumnak van állapota és hogy ezek az objektumok egymással társalogva képesek egymás állapotait megváltoztatni.
Egy objektum tehát tekinthető egy állapotnak, ami sok kis másik állapotból és olyan függvényekből áll, amik módosítgatják ezeket a kis állapotokat. Ez már egymagában komplexnek hangzik, de amikor hozzávesszük, hogy egy objektum orientált program pedig sok kis objektumból áll, amik egymással kommunikálva állandóan módosítgatják egymás állapotait, akkor rájövünk, hogy minden objektum orientált program egy hatalmas nagy és komplex állapot gépnek felel meg.
Objektum orientáltság egyik legnagyobb hátránya, hogy ahelyett, hogy meggátolná az állapot változok keletkezését inkább ösztönzi azt.
Üzenet küldés hiánya: Alan Kay fejéből pattant ki az objektum orientáltság ötlete, viszont elmondása szerint sokan félreértelmezték az ötletét, mivel az ötletének a lényege az üzenet küldés volt objektumok között. Üzenet küldést azért tartotta fontosnak, mivel ezáltal természetesen el tudtak szigetelődni az objektumok egymástól. Ehhez képest a legnépszerűbb OO nyelvek megengedték, hogy objektumok egymás publikus metódusait hívhassák meg, ami erőforrás hatékony módszer, viszont ezáltal az objektumok alig szigetelődtek el egymástól.
Ez sajnos ahhoz vezet, hogy a program komponensek össze fognak fonódni, ami pedig ahhoz vezet, hogy egy komponens nagyon nehezen lesz módosítható anélkül, hogy a tőle függő komponenseket ne kéne megváltoztatni.
Összetett adat szerkezet: Objektum az alapépítőköve minden objektum orientált nyelvnek, ami azért szerencsétlen mivel az objektumoknak nagyon komplexek. Gondoljunk bele, hogy egy objektum összeolvasztja az összetartozó adatokat, funkciókat és ennek tetejébe még örökölhetnek egymástól, tartalmazhatják egymást és meg is valósíthatnak valamilyen interfészt. Egy objektum gyakorlatilag egy imperatív programnak is megfeleltethető, mivel abban is ugyanúgy megtalálhatóak az összetartozó adatok és függvények. Csak objektumok esetében még tovább tudjuk bonyolítani a helyzetet objektum példányok készítésével és örökléssel.
A helyzetet csak rontja, hogy új típusokat általában csak objektumok segítségével alkothatunk meg. Így még a legegyszerűbb típusaink is örökölni fogják az objektumoktól származó komplexitásokat.
Én személy szerint azért nem szeretem az objektum orientált programozást, mivel nem látom, hogy lenne jövője a paradigmának és már programozás nyelvi kutatások sem foglalkoznak vele, mivel egyszerűen túl bonyolult, ahhoz hogy lehessen ráépíteni bármit is. Nem is beszélve, hogy formális verifikálásuk/model ellenőrzésüket is szinte lehetetlené teszi a paradigmánál érzékelhető súlyos állapot robbanás.
Objektum orientáltság mindig az eszembe juttatja, hogy a választott szakmám még nagyon fiatal és még mindig abban a stádiumban van, hogy kitapogatja, hogy merre kéne haladnia. Ezért nem is meglepő ha enged a csábításnak és kipróbál egy ígéretesnek látszó ötletet, viszont fontos, hogy a hibákat felismerjük és kijavítsuk őket hiszen csak így tudjuk megtartani a helyes irányt és a profizmusunkat.
Sokszor felteszem magamnak a kérdést, hogy mi lett volna ha annak idején nagyobb hangsúlyt kapott volna a funkcionális programozás: Szakmánk színvonala jobb vagy rosszabb lenne? Menyire lenne más az egyetemi oktatás? Milyen nyelvek lennének a legnépszerűbbek? Programok minősége javult volna? Formális verifikáció népszerűbb lenne? Esetleg minden ugyanolyan lenne csak más paradigmát használnánk? Megaynyi kérdés, amire sosem lesz konkrét válasz csak spekuláció.
Alapok megrepedése
Majdnem minden mostani népszerű programozási nyelvnek az alapját az imperatív paradigma adja, ami nem meglepő hiszen annak idején nagyon fontos volt a gép közeli nyelvek használata annak érdekében, hogy minél jobb erőforrás kihasználást lehessen elérni. Viszont változnak az idők és ami régen jó megoldás volt az mára veszélyesé változott.Imperatív volt a legelső paradigma, amit megkérdőjeleztem, mivel van egy bizonyos tulajdonsága, ami egy olyan kétélű fegyver, ami jobban szeret minket megszúrni, mint magát a problémát. Itt side effect(vagyis mellék hatás)-ként ismert tulajdonságról beszélek. Mellék hatások abból a szempontból fontosak, hogy minden input/output esemény is annak számít.
Számomra a side effect mindig 2 dolgot jelenthetett:
- Egy függvény, aminek nincs visszatérési értéke(void)
- Egy olyan függvény, ami egy külső nem lokális és nem is konstans változó értékét használja
A második eset pedig abból a szempontból problémás, hogy a függvény függ egy változótól, aminek az értéke bármikor módosulhat, ami azt jelenti, hogy a függvény függ a külső változón végrehajtott változásoktól és ami még rosszabb, hogy a függvény is módosítani tudja ezt a változót. Ebben az esetben még rosszabb a tesztelhetőség, mivel teszteléskor a külső változóra és annak értékeire is figyelni kell.
Ennek a két esetnek a kombinációja pedig tragikusan komplex mellék hatásokat képes előidézni, ami a függvénynek a tesztelhetőségét is tragikusan megkárosítja.
Ez a két esett elégé különbözőnek hangzik de mégis általában ugyanarra az eseményre lehet visszavezetni, ami nem más mint az értékmódosítás. Az értékmódosítás azért olyan veszélyes, mert minden mellékhatás erre az utasításra vezethető vissza. Például I/O műveletek is csak annyiból állnak, hogy a gép bizonyos regisztereinek megváltoztatjuk az értékeit.
Értékmódosításnak még meg van az a kellemetlen hatása, hogy programunk futásába bevezeti az idő fogalmát, ami azt jelenti, hogy fontossá válik, hogy az adott változónk értéke adott pillanatban éppen menyi. Mivel ha adott pillanatban éppen rossz értéke van, akkor valószínűleg helytelen futást vagy hibát fog eredményezni. Másképpen úgy is lehet fogalmazni, hogy értékmódosítás miatt a változó egy állapot változóvá válik, ami azt jelenti, hogy ügyelnünk kell, hogy futás során mindig jó állapotba legyen ez a változónk, különben baj lesz.
Térjünk vissza az előző két esetünkhöz és képzeljük el, hogy nem engedjük meg az érték módosítást:
- Az első esetet úgy tudjuk megoldani, hogy a változtatásokat visszatérési értékként visszaadjuk. Ezáltal a magát a függvényt és annak visszatérési értékét tudjuk tesztelni a bemeneti paraméterek helyett.
- A második esetben megmarad a külső változóra való hivatkozás viszont értékmódosítás betiltása miatt ez a változó konstanssá változik. Emiatt pedig ismét könnyebben tesztelhető lesz a függvényünk, mivel nem kell a külső változó értékét változtatva tesztelgetnünk.
Viszont ha mégis megszeretnék változtatni a külső változó értékét, akkor érdemes felvenni egy új függvényt, ami a változónak az új módosított értéket adja vissza és csak annyi a dolgunk, hogy ezt a új módosított értéket átadjuk bementi paraméterként annak a függvénynek, ami kéri a változó új értékét. Ebben az esetben két könnyen tesztelhető függvényünk lesz egy helyett.
Ebből is látszik az imperatív és funkcionális programozás közötti különbség, míg funkcionális nyelvek a pure függvények használatát hangsúlyozzák, addig az imperatív nyelvek azok ellentétét az impure függvények használatát hangsúlyozzák.
Másik kellemes hatása az értékmódosítás betiltásának, hogy nem alakulnak ki állapot változók, mivel minden érték változás pure függvények segítségével megy végbe, ami azért jó mert így a változások explicit módon mennek végbe függvények segítségével, vagyis egy bizonyos új érték elkészítését csak függvény hívásokkal tudjuk elérni úgy, hogy nem változtatjuk meg a változó eredeti értékét és ezáltal nem vezetünk be állapot változókat.
Imperatív paradigma hibái ellenére mégis fontos, mivel vannak olyan algoritmusok és adatszerkezetek, amiket könnyebben lehet imperatívan megvalósítani. Nem is beszélve olyan helyzetekről, amikor nagyon fontos egy olyan gépközeli nyelv használata, amivel pontosan tudjuk irányítani a program futását és erőforrás használatát.
Viszont az esetek többségében az ember jobban jár a funkcionális programozással vagy legalábbis funkcionális stílus használatával, mivel ebben az esetben nem csak jól tesztelhető, hanem könnyebben követhető kódot is tud készíteni az ember.
Egy ötletet imperatív alapokon
Objektum orientáltság elég egyszerű ötletre épül: problémákat modellezzük le a való világból vett objektumok segítségével. Ezek az objektumok rendelkezzenek változókkal(állapottal) és függvényekkel(viselkedéssel). Az adott problémát ezek az objektumok oldják meg kommunikálva egymással. Objektum orientált paradigma egy nagyon könnyen vizualizálható és intuitív ötlet.Az összes népszerű objektum orientált nyelv imperatív alapokon nyugszik, ami azt jelenti, hogy az objektum orientált nyelvek örökölték az imperatív nyelvek erősségeit és annak gyengeségeit is egyúttal. Viszont maga az ötlet is bevezettet néhány új gyengeséget:
- Állapot robbanás
- Üzenet küldés hiánya
- Összetett adat szerkezet
Ez a fogalom a modell ellenőrzés világából származik és a lényege annyi, hogy minél több állapot változót használsz annál exponenciálisan nehezebben lesz érthető az egész programod működése.
Minden imperatív nyelvben gondot okoznak az állapot változóknak a száma, de egy objektum orientáltál nyelvben még súlyosabb a helyzet mivel az objektumok definíciójában benne van, hogy minden objektumnak van állapota és hogy ezek az objektumok egymással társalogva képesek egymás állapotait megváltoztatni.
Egy objektum tehát tekinthető egy állapotnak, ami sok kis másik állapotból és olyan függvényekből áll, amik módosítgatják ezeket a kis állapotokat. Ez már egymagában komplexnek hangzik, de amikor hozzávesszük, hogy egy objektum orientált program pedig sok kis objektumból áll, amik egymással kommunikálva állandóan módosítgatják egymás állapotait, akkor rájövünk, hogy minden objektum orientált program egy hatalmas nagy és komplex állapot gépnek felel meg.
Objektum orientáltság egyik legnagyobb hátránya, hogy ahelyett, hogy meggátolná az állapot változok keletkezését inkább ösztönzi azt.
Üzenet küldés hiánya: Alan Kay fejéből pattant ki az objektum orientáltság ötlete, viszont elmondása szerint sokan félreértelmezték az ötletét, mivel az ötletének a lényege az üzenet küldés volt objektumok között. Üzenet küldést azért tartotta fontosnak, mivel ezáltal természetesen el tudtak szigetelődni az objektumok egymástól. Ehhez képest a legnépszerűbb OO nyelvek megengedték, hogy objektumok egymás publikus metódusait hívhassák meg, ami erőforrás hatékony módszer, viszont ezáltal az objektumok alig szigetelődtek el egymástól.
Ez sajnos ahhoz vezet, hogy a program komponensek össze fognak fonódni, ami pedig ahhoz vezet, hogy egy komponens nagyon nehezen lesz módosítható anélkül, hogy a tőle függő komponenseket ne kéne megváltoztatni.
Összetett adat szerkezet: Objektum az alapépítőköve minden objektum orientált nyelvnek, ami azért szerencsétlen mivel az objektumoknak nagyon komplexek. Gondoljunk bele, hogy egy objektum összeolvasztja az összetartozó adatokat, funkciókat és ennek tetejébe még örökölhetnek egymástól, tartalmazhatják egymást és meg is valósíthatnak valamilyen interfészt. Egy objektum gyakorlatilag egy imperatív programnak is megfeleltethető, mivel abban is ugyanúgy megtalálhatóak az összetartozó adatok és függvények. Csak objektumok esetében még tovább tudjuk bonyolítani a helyzetet objektum példányok készítésével és örökléssel.
A helyzetet csak rontja, hogy új típusokat általában csak objektumok segítségével alkothatunk meg. Így még a legegyszerűbb típusaink is örökölni fogják az objektumoktól származó komplexitásokat.
Egyet előre kettőt hátra
Sajnos az objektum orientált paradigmáról nem tudom elmondani ugyanazt mint az imperatív paradigmáról vagyis hogy fontos lenne. Azért nem tartom fontos, mivel a paradigma előnyeit elhomályosítja annak hátrányai és nem is ad túl sok pluszt az imperatív programozáshoz. Az egyetlen nagy különbség a kettő között, hogy az objektum orientált programozást "magasabb" szintű paradigmának tartják, mert egy nagyon komplex adat szerkezetet használ építő elemként. Objektumok alapként való használata nagyon jól hangzik, viszont gyakorlatban inkább ellenünk dolgozik, mint velünk.Én személy szerint azért nem szeretem az objektum orientált programozást, mivel nem látom, hogy lenne jövője a paradigmának és már programozás nyelvi kutatások sem foglalkoznak vele, mivel egyszerűen túl bonyolult, ahhoz hogy lehessen ráépíteni bármit is. Nem is beszélve, hogy formális verifikálásuk/model ellenőrzésüket is szinte lehetetlené teszi a paradigmánál érzékelhető súlyos állapot robbanás.
Objektum orientáltság mindig az eszembe juttatja, hogy a választott szakmám még nagyon fiatal és még mindig abban a stádiumban van, hogy kitapogatja, hogy merre kéne haladnia. Ezért nem is meglepő ha enged a csábításnak és kipróbál egy ígéretesnek látszó ötletet, viszont fontos, hogy a hibákat felismerjük és kijavítsuk őket hiszen csak így tudjuk megtartani a helyes irányt és a profizmusunkat.
Sokszor felteszem magamnak a kérdést, hogy mi lett volna ha annak idején nagyobb hangsúlyt kapott volna a funkcionális programozás: Szakmánk színvonala jobb vagy rosszabb lenne? Menyire lenne más az egyetemi oktatás? Milyen nyelvek lennének a legnépszerűbbek? Programok minősége javult volna? Formális verifikáció népszerűbb lenne? Esetleg minden ugyanolyan lenne csak más paradigmát használnánk? Megaynyi kérdés, amire sosem lesz konkrét válasz csak spekuláció.