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.
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 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.
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.
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.
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.
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.