A TOM programozási nyelv

Objektum-orientált programozás

A TOM programnyelv egy objektum-orientált programozást támogató programnyelv. Olyannyira OOP nyelv, hogy az objektumok az elemi építőelemei. Az osztályok és a példányok különböznek a nyelvben. Az objektumok egy osztályát az osztályobjektum definíciója és a példány definíciója együtt definiálja. A példány az osztályobjektum allokálható változata.

Vannak allokálható példányok és megvalósítatlan osztályok és példányok. Az allokálható példányokban minden metódusnak implementálva kell lennie, míg a nem allokálható, absztrakt osztályokban és példányokban lehetnek implementálatlan metódusok. Ha egy példány allokálható, annak vagy közvetlenül, vagy indirekten a State osztályból kell öröklődnie, ebben az osztályban vannak definiálva az allokáláshoz szükséges adattagok és metódusok. A State osztály tartalmaz egy isa példányváltozót, mely minden példányra megmutatja, hogy mely osztály objektum írja le a tulajdonságait.

Egy osztály vagy példány definíciója deklarálhat megvalósítatlan metódusokat, ebben az esetben ők maguk is megvalósítatlan osztályok vagy példányok lesznek, nem lehet őket allokálni. Csak olyan példányokat allokálhatunk, melyeknek minden metódusa implementálva van. Létezhetnek olyan példányok is, melyek nem írhatóak le állapottal, csupán metódusok deklarációit és esetleg definíciót tartalmaznak, és persze adattagokat, de csupán azért lettek létrehozva, hogy származtathassunk belőle újabb osztályobjektumokat. Ha nem tartalmaz egyáltalán implementációt egyetlen metódusára sem, akkor olyan példányt kapunk, mint pl. Java programnyelvben az interface.

Minden osztály őse a meta osztály, aminek az őse a meta-meta osztály objektum. Egy osztálynév lehet egy azonosító, vagy egy unit nevével megjelölt azonosító unitnév.azonosító formájú. Egy példány típusának neve maga az osztálynév, az osztályobjektum típusának neve pedig a class (osztálynév).

Például a my_foo egy Foo példány típusa, vagy valamely alosztályának típusa, ha:

foo my_foo;

és a foo_class a Foo osztályobjektumának típusa, vagy annak valamely alosztálya, ha:

class (Foo) foo_class;

Öröklődés

Ha már létező osztályhoz hasonló osztályt szeretnénk létrehozni, mely rendelkezik egy másik osztály tulajdonságaival, akkor nagy segítség számunkra a TOM programozási nyelv által kínált öröklődés. Sajnos a TOM csak public öröklődést támogat, azaz minden az ősosztályban definiált metódus elérhető az alosztályokban is, ellentétben a C++ által nyújtott privat öröklődéssel. A gyermekosztály örökli a szülő, azaz a superosztály minden tulajdonságát, így adattagjait és metódusait is. A többszörös öröklődés megengedett a nyelvben, azaz egy gyermekosztály, másnéven a leszármazott osztály több superosztály tulajdonságait is örökli, de azok superosztályainak tulajdonságait csak egyszeresen. Azonban probléma adódik, ha több superosztályában is megtalálható egy metódus neve, vagy egy adattag, ami esetleg nem azok közös superosztályaiban van megvalósítva. Ekkor a leszármazott osztályban ezen metódusokat vagy adattagokat újra kell definiálni, vagy megmondani, hogy mely szülőtől örökölje azokat. Az utóbbi megoldásra egyelőre nincs lehetőség a nyelvben, tehát újra kell definiálni. Az öröklődés szintaxisa a nyelvben:

Interface instance|class <class_name> :superclass[,superclass2,…] [object variables] method* end; Implementation instance|class <class_name> :superclass[,superclass2,…] [object variables] method* end;

Az interface rész a deklarációkat tartalmazza, azt, ami látható az osztályból, az implementation rész pedig a megvalósításokat is. Az implementation részt a programozó írja, az interface részt pedig általában az interface generátor hozza létre automatikusan. A fordítás során a compiler csak az implementation részeket olvassa, és az esetleges függőségek kapcsán keresi az interface részekben, hogy mely deklarációk hol vannak implementálva. Az interface részben leírt metódusdeklarációk, változók, és az implementation részben leírtak között nem lehet különbség, ugyanabban a formában kell megjelenniük, különben a fordító hibát jelez. Az object variables rész az objektumváltozókat tartalmazza, tehát az objektumváltozók felsorolása. Egy objektumváltozó deklarálása a következőképpen néz ki:

[local] [static] [redeclare] [láthatóság] <típusnév> <azonosító> [,<azonosító2>,…];

A static szót csak osztályobjektum definiálásakor használhatjuk, példány esetén nem. Ha a változó elé odaírjuk a static szót, akkor abból a változóból egy közös változó lesz az összes ebbe az osztályba tartozó példány esetén, megváltoztatásával minden példányban megváltozik ez az érték. Tehát ez egy osztályobjektumra vonatkozó változó lesz, éppúgy, mint C++ -ban.

A local szót csak static típusu változó esetén használhatjuk. Jelentése több szálon futó alkalmazások esetén van, azt jelenti, hogy minden egyes futó szálnak legyen egy saját ilyen változója.

A redeclare szót akkor kell használnunk, ha egy superosztályból örökölt változót akarunk újradeklarálni a leszármazott osztályunkban. Használhatjuk akkor, ha egy ősosztálybeli változó típusát szeretnénk megváltoztatni, de csak az eredeti, az ősosztályban megadott típus egy altípusa lehet az új típus. Például char típusról long típusra nem lehet újradeklarálni.

A metódusoknak a deklarációit csak az interface részben lehet elhelyezni, az implementációit pedig csak az implementation részben. Ezekről még az Alprogramok részben bővebben lesz szó.

Elemi osztályok

A TOM programnyelv definiál néhány elemi osztályt, melyek a compilerbe beépített unitban vannak definiálva, ennek a unitnak a neve: _builtin_. Megj.: Erre a unitra lehetetlen hivatkozni, mert a TOM nem engedi az azonosítókat aláhúzással kezdeni. Ezek az osztályok pedig a következők:

_builtin_.Top: Ez az implicit ősosztálya minden definiált osztálynak. Tartalmazza a builtin, a statikus kötés, és az operátorok definicióját.

_builtin_.Any: Ez az implicit alosztálya minden definiált osztálynak. Ha egy metódus bármivel visszatérhet, akkor a visszatérési típust Any-nek kell definiálni.

tom.State: Az ősosztálya minden allokálható példánnyal rendelkező osztálynak. Ez az osztály teszi lehetővé többek között a dinamikus kötést.

Metódusok felüldefiniálása

Egy örökölt osztály esetén a gyermek osztály felül tudja írni az ősosztályától örökölt metódusokat. Legyen pl. egy Counter osztályunk, ami a State osztályból származik, és ebból származtassuk a TwoCounter osztályt, aminek az a feladata, hogy a nextValue metódus meghívásakor 2-vel növeli az értékét, szemben a Counter osztály nextValue metódusával.

implementation class TwoCounter: Counter end; implementation instance TwoCounter redefine int nextValue { current_value += 2; = current_value; } end;

Ha a nextValue–t meghívjuk a TwoCounter–től, az 2-vel növeli az értéket, egyébként teljesen ugyanúgy viselkedik, mint a Counter-ben.

Polimorfizmus - dinamikus kötés

Egy alosztály rendelkezik ugyanazokkal a metódusokkal, mint amelyekkel szülőosztálya rendelkezett, és ezek meghívásakor ugyanazt csinálja is, feltéve, hogy nem írta felül azokat. Például a következő metódus lekérdezi a nextValue–t a Counter objektumtól, mint argumentumot.

int getNextValueFrom Counter counter { = [counter nextValue]; }

Amikor ez a metódus egy TwoCounter objektummal találkozik a várt Counter objektum helyett, a nextValue metódus még mindig érvényes. Tulajdonképpen bármilyen, a Counter által értelmezett metódus érvényes a TwoCounter objektumon is, mivel az alosztálya.

De mi történik a következő kódban?

Counter c2 = [TwoCounter alloc]; int i = [c2 nextValue];

A c2 változónak Counter–beli típusa van, de jelenleg egy TwoCounter objektumra mutat. Ilyenkor a nextValue metódus meghívásakor az aktuálisan hívott osztály szerinti metódus hívódik.

Láthatóság

Egy példány közvetlenül elérheti a saját példányváltozóit, és az osztályának összes elérhető osztályváltozóját. Egy osztály objektum közvetlenül elérheti a saját osztályváltozóit, amit deklarál, vagy örökölt valamely superosztályától. Más közvetlen változóelérés nem lehetséges, a többi változót metódusokon keresztül lehet csak elérni.

Fordítási időben nagyon fontos az adattagok és a metódusok védelmének szigorú fogalma, azaz a láthatóság megadhatósága. Minden metódus és változó számára a TOM három különböző láthatósági szintet definiál:

private: Az adott elem csak az aktuális osztályból érhető el.

protected: Az adott elem csak az aktuális osztályból, és annak leszármazottjaiból érhető el, ez az alapértelmezett láthatóság a példány- és osztályváltozók számára.

public: Az adott elem minden osztály számára elérhető. Ez az alapértelmezett láthatóság minden metódus számára.

Egy publicnak definiált változó azonban nem közvetlenül érhető el az adott osztályon kívülről (lásd feljebb), egy hozzáférő és egy módosító metódust kell hozzá definiálni. Ha a változót mutable-nek deklaráljuk, akkor ha a változót publicnak deklaráljuk, akkor egy módosító metódus definiálódik hozzá.

/* Hozzáférő */

int foo_bar { = foo_bar; }

/* Módosító */

void set_foo_bar int value { foo_bar=value; }