A Smalltalk programozási nyelv

A metaclass-rendszer

Bevezetés

A metaosztály-koncepció minden bizonnyal a Smalltalk nyelv egyik legnehezebben befogadható része. Ennek az egyik oka a sok, könnyen összekeverhető név (mint például a Class class, vagy a Metaclass class), illetve hogy mindez a fejlesztő számára rejtett. Általában a programozók nincsenek is tisztában azzal, mi történik a háttérben, ami nem is probléma, elvégre nélküle is tökéletesen lehet fejleszteni.

A hiedelemmel ellentétben azonban meg lehet érteni relatíve könnyen az elvet, ha az ember rákap az ízére. Azért is jó, ha van rálátásunk a belső működésre, mert a Smalltalk máig az egyik legtisztább és legkövetkezetesebb objektum-orientált nyelv, és - a bonyolultság ellenére - igazán szép megoldásokat dolgoztak ki a tervezők.

A korábbiakban már betekintést engedtünk a Smalltalk osztály- és metaosztály-rendszerébe, viszont ebben a fejezetben bővebben szeretnénk írni erről.

Osztályok

Minden osztály az Object osztály közvetett, vagy közvetlen leszármazottja, kivéve maga az Object osztály. Az osztályok tulajdonságokat örökölnek az ősüktől, akik szintén örökölnek az ősüktől, és ez így gyűrűzik fel az Object osztályig.
Alapelv a Smalltalkban, hogy minden (ténylegesen minden!) objektum, és minden objektum pontosan egy osztály előfordulása. A Goldberg és Robson által írt Purple Bookban található a következő mondat:

"Az objektumoknak két fajtája van, az egyik létre tudja hozni saját előfordulásait (osztályok), a másik pedig nem."

Az állítás azt fogalmazza meg, hogy az osztályok is objektumok, tehát küldhetők neki üzenetek, ugyanakkor csak az osztályok képesek arra, hogy saját példányukat létrehozzák ellentétben az osztály-előfordulásokkal.
Az osztályoknak a következő szerepeik vannak a Smalltalkban:

Metaosztályok

Az előbb jeleztük, hogy minden osztály is egy osztály előfordulása, viszont ezekkel kapcsolatban elvárásunk, hogy minden osztály (mint annak egy példánya) esetén máshogy viselkedjenek. Tehát minden osztály egy-egy más osztály példánya, mint objektum. A Smalltalk-80-ban vezették be a metaosztály fogalmát, ami egy speciális osztály, melynek az előfordulásai is osztályok. Általában a metaosztálynak csak egy előfordulása létezik, illetve egy osztály mindig pontosan egy metaosztály előfordulása.
Még egyszer: az osztály egy metaosztály előfordulása, és a metaosztály határozza meg az osztály viselkedését, ami azt jelenti, hogy a metaosztályban vannak definiálva az osztályszintű metódusok. Tehát ha az osztály üzenetet kap, akkor a saját metaosztályában keresi meg az üzenetet implementáló metódust.
A metaosztályok neveit úgy kapjuk, ha a hozzájuk tartozó osztály (azaz az egyetlen példányuk) neve után írjuk a " class" szót. A metaosztályokat nem érhetjük el közvetlenül, hanem a megfelelő osztálynak küldött "class" üzenettel hivatkozhatunk rájuk.

A metaosztály-hierarchia

Ahogy az osztályok egy Object gyökerű fába szerveződnek az öröklődést tekintve, úgy ezzel párhuzamosan a metaosztályoknak is megvan ez a hierarchiája. Ha egy objektum kap egy üzenetet, a neki megfelelő osztályból kiindulva halad felfelé az Object gyökerű fában, ameddig meg nem találja a megfelelő metódust. Ez az osztályszintű metódusoknál hasonlóan történik, csak a metaosztály hierarchiában történik a keresés.

A ClassDescription és a Behavior

Az Object osztály a fa gyökere a rendszerben, így nem örököl egy másik osztálytól sem, de ez nem probléma, ugyanis az Object tartalmaz minden attribútumot és metódust, mely egy objektumra jellemző lehet. Ezzel párhuzamosan mi történik a metosztályok szintjén? Egy lehetséges megoldás lenne, hogy az Object Class (az Object osztály metaosztálya) tartalmazna mindent, ami szükséges. Ezzel szemben a Smalltalk-80 egy másik megközelítést alkalmaz, melynek előnyeit a metaosztályok rekurzív struktúrájának tárgyalásánál fogjuk látni.
Az Object class leszármazottja a Class osztálynak, ez lehetőséget ad arra az Object classnak, hogy megadja az Object osztály viselkedését, miközben az osztályokra jellemző tulajdonságok a Class osztályban vannak meghatározva.
A Class viszont a ClassDescription osztályból származik, mely lehetőséget biztosít az osztályok elnevezésére, kommentezésére és példányváltozók elnevezésére.
A ClassDescription azonban a Behavior osztályból származik, amely az osztályok viselkedését írja le, mint például a példányok létrehozását. Lényegében a minimális szükséges tulajdonságokat definiálja, mellyel azoknak az objektumoknak kell rendelkezni, amelyek létre tudnak hozni példányokat.
Ez a három osztály együtt definiálja az osztály fogalmát, viselkedését. A neveikből megfigyelhető (nincs a "class" a végén), hogy ezek nem metaosztályok, viszont logikusan az ők metaosztályai a metaosztály-hierarchia csúcsán helyezkednek el.

A metaosztály-rendszer rekurzivitása

Felvetődik a kérdés, hogy ha a metaosztályok is osztályok, akkor ők is példányai-e valamely osztálynak (mint objektumok). A Smalltalk következetes nyelv, így a válasz igen, ami adja a következő kérdést, miszerint melyik az ő osztályuk?
Itt kicsit egyszerűbb a helyzet, ugyanis minden metaosztály a Metaclass osztály egy példánya. Ezt az indokolja, hogy minden metaosztálynak hasonló az viselkedése: egy osztály struktúráját és viselkedését definiálják.

Ez eddig rendben, viszont adódik a következő kérdés, hogy a MetaClass osztály is példánya-e egy osztálynak. A válasz itt is igen, a korábbiak alapján sejthető, hogy ez az osztály a Metaclass class nevet viseli. Viszont a metaosztályok mindegyike a Metaclass egy példánya, így a Metaclass class is. Itt van lezárva a történet, ugyanis nem kell definiálnunk külön osztályt azért, hogy annak előfordulása legyen a Metaclass class. Ez az elgondolás egy teljesen logikus, következetes és nem utolsó sorban elegáns lépés a tervezők részéről.
A biztonság kedvéért vegyük át röviden a metaosztályokról elhangzottakat!

A könnyebb érthetőség kedvéért a következő ábrán bemutatjuk az osztályok között fennálló "példánya"-kapcsolatokat. Ha A osztályból B osztályba mutat nyíl, az azt jelenti, hogy A (mint objektum) a B egy példánya.

Példány-kapcsolatok

Az alábbi ábrán az osztályok közötti öröklési kapcsolatokat tekinthetjük meg, könnyen észrevehető, hogy a legfontosabb objektum-orientált alapelv - miszerint minden objektum - itt teljes egészében igaz.

Öröklődési kapcsolatok