Szükséges-e osztályok definiálásával foglalkozni?

Aki a Python alapjait már ismeri, annak nyelvi elemeivel már találkozott, tapasztalhatja, hogy nagyon sok beépített függvény áll rendelkezésre, valamint a saját igényeinkhez illeszkedő függvények is definiálhatók. E függvénydefiníciók változatos formában tehetők meg. Gondoljunk csak például a beágyazott függvényekre, a lambda függvényekre, a parciális függvényekre, dekorátorokra, valamint a generátor- és closure függvényekre. Ez utóbbiak ráadásul lehetővé teszik, hogy a függvények „emlékezettel” rendelkezzenek, vagyis a hívások között az előző híváshoz kapcsolódó állapotot is tároljunk.

Ha a függvények ilyen tárházához hozzávesszük a konténereket (pl. list, tuple, dict, bytes, bytearray, str, set, frozenset), a konténer- és generátorépítő kifejezéseket (comprehensions), az iterátorokat, a vezérlőszerkezeteket (feltételes elágazások, ciklusok) és a különféle operátorokat, akkor akár úgy tűnhet, hogy a felmerülő feladatok ezekkel megoldhatók. Többek között ezért is tárgyalja a Python tudásépítés lépésről lépésre című e-könyv ezeket nagyon részletesen először, és ezért kerülnek az osztályok és azok definiálása csak jóval később ismertetésre.

Ez viszont felvetheti a kérdést, hogy szükséges-e egyáltalán osztályokkal foglalkozni, osztályokat definiálni egy feladat megoldásához, vagy érdemes-e, illetve lehet-e keresni és választani az előbb említett eszköztárból megfelelő nyelvi szerkezetek és elemek kombinációját a cél eléréséhez?

A válaszhoz talán a legcélravezetőbb, ha átgondoljuk, hogy miért szoktunk osztályokat definiálni, azaz miért van osztályokra szükség?

Az objektum-orientált paradigmában járatosak rögtön két érvet is fel tudnának hozni:

1) azért szükséges az osztály, mert ebben rendeljük össze a feladatunkhoz kapcsolódó, összetartozó adatokat és a rajtuk végzendő műveleteket. Másképp fogalmazva ezzel valósítjuk meg az egységbezárást (encapsulation).

2) az osztály egy minta, tervrajz (blueprint), ami alapján az egyedi példányok létre tudnak jönni.

Elsőre mindkét indok helytállónak tűnik. De elemezzük egy kicsit a két állítást.

Ha csak az a szempont, hogy bizonyos adatokat és a rajtuk végezhető műveleteket zárjunk össze, vagy állapotot őrizzünk meg, akkor ezt closure definiálásával is meg tudjuk tenni.

Ami a példányok, vagyis egy minta alapján létrehozott objektumok, előállítását illeti, ha akarjuk, ezt is megvalósíthatjuk closure-rel.

Az előbb említett mindkét célkitűzés teljesíthető az alább látható closure függvénydefiníció segítségével, amely e tekintetben egy osztályt utánoz.

E módszernek azonban több gyengéje van. Az egyik, hogy minden példány előállításakor a beágyazott függvényobjektumok is újra létrejönnek, ami több száz vagy ezer példánynál már nem biztos, hogy kívánatos. Ez a gond osztályból történő példányosításnál nem áll fenn, mert az osztályban definiált függvényobjektumok csak egyszer jönnek létre, és a példányok a metódushívásokkor ezeket közösen használják.

A másik hátránya a closure-rel történő példányosításnak, hogy a példány típusa mindig function lesz, ahogy azt a teszteredmények is mutatják.

Más szóval, e módszerrel nem tudunk egy számunkra szükséges nevesített típust meghatározni, és így például típusellenőrzést sem tudunk végezni.

Látjuk tehát, hogy a hatékonysági szempontok mellett az osztályok egyik fő alkalmazása a típusdefiniálás.

Egy másik fontos jellemzője az osztályoknak az öröklés képessége, ami nem csak a kód újrafelhasználását segíti, hanem altípusok definiálását is lehetővé teszi. Talán még ezek nélkül is meg lehet oldani egy adott feladatot, de van, hogy az öröklés nem megkerülhető. Ilyen eset például az, ha egyéni kivételtípusokat akarunk létrehozni, mert ekkor örökölni kell az Exception beépített osztályból.

Arra a kérdésre tehát, hogy ismerve a Python fent sorolt bőséges nyelvi eszköztárát, szükséges-e egyáltalán osztályokkal foglalkozni, osztályokat definiálni egy feladat megoldásához, a válasz az, hogy attól függ. Mármint a konkrét feladattól. Ha például matematikai számításokra van szükségünk, vagy karakterláncműveleteket akarunk végeztetni, akkor kicsi az esélye, hogy osztályok definiálására lenne szükség. Olyan esetekben azonban, ahol egy összetett rendszert kell létrehozni, illetve a valóságot kell modellezni, és azt programkódba leképezni, akkor nagy a valószínűsége, hogy a modellezés eredményeképpen egyedi típusok lesznek szükségesek, amelyeket osztályokkal valósítunk meg.

E bejegyzés tartalmához a Python tudásépítés lépésről lépésre című e-könyv „Osztály vigyázz! – típuslétrehozás osztályokkal”, „Öröklődés”, „Egyéni kivételtípusok megvalósítása”, valamint a „Típuskezelés, típusosság és típusbeazonosítás” fejezetek kapcsolódnak.

Érdekel a Python tudásépítés lépésről lépésre az alapoktól az első asztali alkalmazásig című e-könyv.