A Simula programozási nyelv

Objektum-orientált programozás

Szintaxis

<osztályazonosító> ::= <azonosító>
<prefix ::= <üres> | <osztályazonosító>
<virtuális sepcifikáció> ::= <üres> | virtual :<típusspecifikáció>
<osztálytörsz> ::= <utasítás> | <hasított törzs>
<kezdeti műveletek> ::= BEGIN | <blokkfej>; | <kezdeti műveletek> <utasítás>;
<záró műveletek> ::= END | ; <összetett utasítás vége>
<hasított törzs> ::= <kezdeti műveletek> <belső rész> <záró műveleletek>
<osztálydeklaráció> ::= <prefix> <főrész>
<főrész> ::= class <osztályazonosító> <formális paraméterlista>
<érték szerinti átadások specifikációja> <típusspecifikáció> <védelemspecifikáció> <virtuális specifikáció> <osztálytörzs>
<belső rész> ::= inner | <címke:belső rész>
<védelemspecifikáció> ::= <üres> | <védelemspecifikáció> <védelem>
<védelem> ::= hidden <azonosítólista>; | protected <azonosítólista>; | hidden protected <azonosítólista>; | protected hidden <azonosítólista>;

Osztályok

Simula osztályok négy részből állnak:


A SIMULA 67 volt az a nyelv, ahol a class fogalom, class hierarchia, valamint az öröklődés először megjelent. Ezért a SIMULA 67 az objektum-orientált nyelvek osztályába tartozik. A nyelvet egy 'szemétgyűjtéses memória kezelő'-vel (garbage collecting heap manager) együtt fejlesztették ki és ezáltal a felhasználónak nem kell semmilyen tárterületet explicit módon felszabadítania.
Az osztálydeklaráció az osztályazonosítóval jelölt osztályt definiálja. Az osztály maga olyan objektumokból áll, amelyek mindegyike az osztálytörzshöz, mint mintához illeszthető, futás közben keletkezett blokkpéldány. Az objektum az objektumgenerátor kiértékelésének eredményeként születik. Egy adott objektum attribútumai az objektum formális paraméterei, a virtuális attribútumok és az osztálytörzsben deklarált dolgok. Osztálydeklarációkban a paraméterek név szerinti átadása tilos.

Csak a következő típusspecifikációk lehetségesek: <típus> , ARRAY , és <típus> ARRAY.

A virtuális specifikációban definiált attribútumok a virtuális attribútumok. Ezek a formális paraméterlistában nem fordulhatnak elő. A tömb attribútum deklarációja az indexhatárt alkotó kifejezésben hivatkozhat az osztálydeklaráció formális paramétereire, de másra nem.
A SIMULA 67 nyelvben a következő módon lehet egy egyszerű osztályt deklarálni:

class Pont( x, y ); real x, y; begin ref( Pont ) procedure összegzés( P ); ref( Pont ) P; összegzés :- new Pont( x+P.x, y+P.y ); end Pont;

Ennek az osztálynak két real típusú adattagja és egy metódusa van. Egy ilyen pontot így példányosíthatunk:

ref( Pont ) p; p :- new Pont( 1.0, 2.0 );

Itt a p egy Pont típusú objektum referenciája.

Részosztályok, öröklődés

A SIMULA 67 nyelvben lehetőség van öröklődésre. Ha egy osztály definiálásakor örökölni szeretnénk egy másik osztály attribútumait, műveleteit akkor annak az osztálynak az azonosítóját a class kulcsszó elé kell írni. Így ha az előbbi Pont osztályból származtatni akarunk egy újat, az előzőnek részosztályát, akkor ezt prefixként használva deklaráljuk az új osztályt:

Pont class Poláris; begin real r, v; ref( Poláris ) procedure összegzés( P ); ref( Pont ); összegzés :- new Poláris( x+P.x, y+P.y ); r := sqrt( x**2 + y**2 ); v := arctg( x, y ); end Poláris;

Ez azt jelenti, hogy ennek az osztálynak az objektumai a Pont attribútumaihoz képest kiegészülnek egy r és v adatattribútummal, valamint az összeadási művelete is más. Az r és v adatokra vonatkozó értékadások az objektum generálásakor hajtódnak végre.
Ha az A osztálynak része a B osztály, akkor az egyszerűen kiegészül az új attribútumokkal.

A prefixek használatára a következő korlátozások érvényesek:


Az ugyanolyan nevű attribútumok eltakarják a korábbi definíciót. Minősítéssel azonban ezek az attribútumok is elérhetők:

class A; begin real P, R; end; A class B; begin boolean P, Q; end; ref( A ) x; ref( B ) y; x :- y :- new B;

Ez egy helyes hivatkozásadás, mivel y egy hivatkozás B típusú objektumokra, x pedig A típusúakra, és egy B-beli objektum lényegében egy speciális A-beli objektum. (Ezt szokták dinamikus hozzárendelésnek hívni.) Ilyenkor az x és y által hivatkozott objektum (természetesen ugyanaz, x == y) rendelkezik mind a négy attribútummal. Közvetlenül elérhetők a hivatkozás minősítésének megfelelő attribútumok, valamint amelyek nincsenek eltakarva, azaz:

x.P valós
x.Ry.Rvalós
 y.Plogikai
 y.Qlogikai

Azok, amelyek magasabb prefix-szinten (alosztályban, a specializáltak) újra lettek definiálva (a korábbit eltakarták), illetve ahol egy alacsonyabb prefix-szinten (az ősnél) még nem is volt definiálva egy attribútum, ezekre csak minősítéssel lehet hivatkozni az <objektumkifejezés> qua <osztályazonosító> segítségével:

x.Py qua A.Pvalós
x.Ry.Rvalós
x qua B.Py.Plogikai
x qua B.Qy.Qlogikai

Az objektumok és osztályok közötti relációk megállapítására szolgálnak az is és in kulcsszavak:

x is A <==> x egy A-beli objektumra hivatkozik x in A <==> x egy A-beli objektumra, vagy A részosztályának objektumára hivatkozik

Az előbbi példában szereplő x-re és A-ra az első logikai kifejezés hamis, mivel x egy B-beli objektum referenciája, de mivel B részosztálya A-nak, ezért a második kifejezés értéke igaz.

Az X objektum valamely k prefix szinthez tartozó utasítása az X objektum valamennyi olyan attribútumához hozzáfér, mely a k-val azonos vagy alacsonyabb prefix-szinten lett definiálva. A többihez nem, kivéve a virtuális attribútumokat. Egy több szinten is előforduló attribútum-azonosítónál az alacsonyabb szinten levőt eltakarja a magasabb szinten (leszármazottnál) levő, így az rejtetté válik. Az így rejtetté váltakat is el lehet érni a THIS használatával. A this C használatával az éppen aktív objektumra hivatkozhatunk, ha ez értelmes, azaz ha a C osztályazonosító hatáskörén belül történt a hivatkozás, és a kifejezés vagy a C-nek (vagy C egy részosztályának) a törzsében van, vagy a C-vel (vagy C egy részosztályával) minősített csatolt blokkban (lásd inspect utasítás).

Ha deklaráltunk egy A osztályt, melyből származtattunk egy B osztályt, akkor az A műveleteit és adatait (közösen: attribútumait) a B törzsében újra megírhatjuk. Szükség lehet azonban arra is, hogy alacsonyabb prefix-szinten (az ősnél) is hivatkozhassunk egy magasabb prefix-szinten (a leszármazottnál) deklarált attribútumokra, illetve hogy a leszármazottnál definiált műveletek legyenek érvényben az ősnél is. Erre szolgálnak a virtuális attribútumok. Ez akkor hasznos például, ha meg akarunk írni egy általános osztályt, de ezen a szinten még nem (vagy nem elég hatékonyan) tudjuk megírni az egyik függvényt. Ha ezt virtuálisnak specifikáljuk, akkor (újra) megírhatjuk a függvény törzsét, ha ebből az osztályból származtatunk újat. Viszont itt a már definiált attribútumok továbbra is érvényben maradnak, csak a virtuális attribútumokat cseréltük ki. (Azokat sem szükségszerű, meghagyhatjuk az eredetit, az általánosabbat, ha egyáltalán meg volt írva a törzse.) Erre példa lehet mondjuk egy olyan általános rendezőfa, aminek az elemtípusát még nem adom meg, mert azt majd csak az örökösökben szeretném specifikálni. A műveleteket (pl. beszúrás, törlés) előre megírhatom. Elég, ha az elemeket összehasonlító műveletet és az elembeolvasó/kiíró eljárást specifikálom virtuálisnak, így ráérek az örökösökben megírnom őket, mégis már az ősosztály törzsében hivatkozhatok rá. (Azaz az összehasonlítás eredményétől függ a beszúrás helye.)

Láthatósági szabályok

Az osztálydeklaráció fő részében megadhatunk ún. védelemspecifikációt, amely lehetővé teszi az osztályattribútumok hozzáférhetőségének korlátozását.
Két lehetőség van a védettség definiálására:

Objektum példányosítása:

Ref(Rectangle) R; (Osztályhivatkozás változó) ... R:- New Rectangle(50, 40); LR :- New LocRectangle(20, 30, 100, 200); (Vegyük észre, hogy a szülőosztály paraméterei is adottak)

A fenti objektumpéldányosítás műveletei:
Memóriafoglalás, hivatkozás eltárolása LR-be.
Értékek bemásolása a paraméterekbe (szülő osztályra először).
A törzs elindítása, először a szülőét, majd a részosztályét.

Virtuális specifikáció, illeszkedés, virtuális attribútumok

A virtuális specifikációk kettős célt szolgálnak: Az alábbi virtuális specifikációk megengedettek: LABEL, SWITCH, PROCEDURE, és <típus> PROCEDURE. A virtuális deklaráció akkor illeszkedik a virtuális specifikációhoz, ha azonosítójuk megegyezik és a fajtájuk is ( LABEL, SWITCH... ), valamint az illeszkedő deklaráció típusa azonos vagy alárendelt típus a specifikációhoz és valamennyi alacsonyabb szinten lévő illeszkedő deklarációhoz képest. Természetesen az illeszkedő deklaráció nem lehet alacsonyabb prefix szinten, mint a specifikáció. Egy hivatkozási típus egy másik hivatkozási típusnak az alárendeltje, ha az előbbi minősítése annak az osztálynak a részosztálya, amely az utóbbit minősíti. Egy közönséges eljárás úgy szemlélhető, mint egy általános típusú függvényeljárás. Bármely típus ehhez képest alárendelt típus. Egy objektumban csak egy érvényes illeszkedő virtuális attribútum lehet. Vagyis ha több szinten is előfordul ilyen akkor ezek közül mindig a legmagasabb prefix szintű lesz az érvényes.

Alosztályok deklarációja

Rectangle Class LocRectangle (X, Y); Integer X, Y; Begin ! More parameters; Boolean Tall; ! More attributes; Procedure Move (Dx, Dy); Integer Dx, Dy; ! More methods; Begin X := X + Dx; Y := Y + Dy End of Move; ! Additional life rules; Tall := Height > Width; OutText("Located at: "); OutFix(X,2,6); OutFix(Y,2,6); OutImage End of LocRectangle;

Törzs tördelése

Az inner utasítás segítségével határozhatjuk meg az osztálydefinícióban azt a pozíciót, ahová későbbi öröklés során a plusz elemek kerülhetnek. Az inner alapszó a k-adik prefix szint hasított törzsében az X objektum k-hoz képest magasabb prefix-szintjeihez tartozó utasításainak végrehajtási helyét jelöli ki. Ha k=n akkor üres utasítást jelent. Ha az inner szó nincs a törzsben (nem hasított) akkor a végrehajtás prefix-szint szerint emelkedő sorrendben történik.
Tegyük fel, hogy a programunk a következő szerkezetű:

Begin Class A(P); Integer P; Begin A1törzs Inner A2törzs End; Part1 A(3) Begin Blokk törzse End; Part2 End;

A fenti objektum példányosítás elvégzett műveletei:
1,Létrehozza az A osztály hivatkozását.
2,Értékek bemásolása a paraméter(ek)be.
3,Az osztálytörzs elindítása, ami magában foglalja a blokk kód futtatását.

Tehát a fenti programnál a műveletek szekvenciája a következő: Part1 A1törzs Blokk törzse A2törzs Part2

Néhány összetettebb példa:

CLASS épület(alapterületX,alapterületY); comment létrehozáshoz szüks. par.-ek.; INTEGER alapterületX,alapterületY; comment par-ek típusa; VIRTUAL : comment virt mezők megadása; PROCEDURE special_effects; ! Most jön az osztály törzse; BEGIN PROCEDURE összedől; BEGIN outtext("Nyiiii-kreccs-bumm"); special_effects; outtext("DRBRBFPAABAAAAAAAAAAAAAANG!"); END összedől; PROCEDURE special_effects; BEGIN outtext("Menekülési es halálozási hangok hallatszanak."); END special_effects; ... ide az jön amit létrehozáskor csinálni kell. INNER; ! itt fognak végrehajtodni a reszosztalyokhoz tartozo specialis dolgok.Ha nem lenne akkor az itteni utasitasok utan hajtodnak vegre.; ... ide az jon amit meg ezutan csinalni kell. END epulet; !Az END es a pontosvesszo kozotti resz kommentnek szamit;

Az (öröklődés):

épület CLASS tyúkól( tyúkszám,kakasszám); INTEGER tyúkszám,kakasszám; ! az épületet prefixként megadva ősosztály lesz a tyúkolra nézve; BEGIN PROCEDURE special_effects; ! Ez az eljárás felülírja az ősosztálybeli hasonló nevű eljárást.; BEGIN outtext("Kotkothajajajjjj-kukurikuuuuu-aaaaaaaaajajjjjjj"); END special_effects; ... ide az jön ami az épület osztálybeli 'inner' helyén fog végrehajtódni tyúkól generálásakor END tyúkól;

Másik példa:

Class Rectangle (Width, Height); Real Width, Height; ! Osztály két paraméterrel; Begin Real Area, Perimeter; ! Attribútumok; Procedure Update; ! Metódusok (lehetnek virtuálisak is); Begin Area := Width * Height; Perimeter := 2*(Width + Height) End of Update; Boolean Procedure IsSquare; IsSquare := Width=Height; Update; ! A négyzet élete létrehozásakor kezdődik; OutText("Rectangle created: "); OutFix(Width,2,6); OutFix(Height,2,6); OutImage End of Rectangle; ! Class with two parameters; Begin Real Area, Perimeter; ! Attributes; Procedure Update; ! Methods (Can be Virtual); Begin Area := Width * Height; Perimeter := 2*(Width + Height) End of Update; Boolean Procedure IsSquare; IsSquare := Width=Height; Update; ! Life of rectangle started at creation; OutText("Rectangle created: "); OutFix(Width,2,6); OutFix(Height,2,6); OutImage End of Rectangle;

Beágyazott osztályok

A Simula osztályok beágyazhatóak bármilyen mélységig! Azokat az osztályokat, melyek lokális osztálydeklarációkat is magukban foglalnak, főosztályoknak nevezzük.
A következő példa programrész definiál egy főosztályt Geometry néven:

! A program a "Geometry" főosztály definíciója. ; ! Egy fogalomrendszer megvalósítása "pont, téglalap, kör és vonal" fogalmakkal. ; Class Geometry; Begin Class Point(X,Y); Real X,Y; Begin Procedure Print; Begin ... End; Procedure Shift(Dx, Dy); Real Dx, Dy; Begin ... End; Print; ! A point élete; End of Point; Class Rectangle(RecName, Width, Height); Text RecName; Real Width, Height; Begin Real Area, Perimeter; Procedure Update; Begin ... End; Procedure Show; Begin ... End; Update; Show; ! A téglalap élete; End of Rectangle; Class Circle(Radius, Center); Real Radius; Ref(Point) Center; Begin Procedure Shift(Dx, Dy); Real Dx, Dy; Begin ... End; OutText("Circle created at "); ! A kör élete; Center.Print; End of Circle; Class Line(M,N); Ref(Point) M,N; ! A szakaszt két pont definiálja; Begin Real Slope; Slope := ... ; ! A vonal élete; End of Line; !A geometry-ben deklarált változók: ; Ref(Point) Origin, A, B; Ref(Line) X,Y; !A geometry törzse: ; Origin :- New Point(0,0); ! Az origó elkészítése; A :- New Point(1,0); B :- New Point(0,1); X :- New Line(Origin, A); ! Az axis elkészítése; Y :- New Line(Origin, B); OutText("*** Geometry initialized ***"); OutImage; End of Geometry;

A következő program a Geometry főosztályt használja. Figyeljük meg, hogyan használjuk a Geometry-ben deklarált osztályokat a megoldandó feladatra specifikus alosztályok deklarációjára ! Később lehetséges lesz deklarálni és használni hivatkozás típusú változókat, importált és lokálisan deklarált osztályokra is. Ez az alapötlete a Simula alapcsomagjainak vagy a felhasználó által definiált nyelveknek.

External Class Geometry; Geometry Begin ! Alosztályok deklarációja a prefixált blokkban: ; Point Class Color_Point(C); Character C; Begin ... End; Rectangle Class Square; Begin ... End; ! Változók deklarálása a prefixált blokkban: ; Ref(Color_Point) A1; Ref(Point) C, D; Ref(Circle) K; Ref(Line) E, F; Ref(Rectangle) R1; Ref(Square) S1; ! Blokk törzse: ; C :- New Point(5,6); D :- New Point(20,30); A1 :- New Color_Point(3, 4, 'G'); K :- New Circle(10, C); E :- New Line(C, D); F :- X; K.Shift (1,1); R1 :- New Rectangle("Rec_R1", 5, 4); S1 :- New Square("Square_S1", 4, 6); S1.Show; End of prefixed block;