DEFINITION PriorityQueue; VAR n-: INTEGER; PROCEDURE Insert ( x: INTEGER ); PROCEDURE Remove ( VAR x: INTEGER ); PROCEDURE Clear; END PriorityQueue.
MODULE PriorityQueue; CONST length = 128; VAR n-: LONGINT; a: ARRAY length OF INTEGER; PROCEDURE Clear*; BEGIN n:=0; a[0]:= MIN(INTEGER): END Clear; PROCEDURE Insert* (x: INTEGER); VAR i:INTEGER; BEGIN IF n < length-1 THEN n:= n+!;i:=n; WHILE x < a [ i DIV 2 ] DO a[i] := a[i DIV 2]; i:=i DIV 2; END; a[i]:=x; END END Insert; PROCEDURE Remove* (VAR x:INTEGER); VAR y,i,j:INTEGER; ready:BOOLEAN; BEGIN IF n>0 THEN x:=a[1];y:=a[n]; n:=n-1;i:=1;ready:=FALSE; WHILE ( i <= n DIV 2) & ~ ready DO j:=j+i; IF ( j<n ) & (a[j] <a[j+1]) THEN j:=j+1 END; IF y>a[j] THEN a[i]:=a[j];i:=j ELSE ready:=TRUE END END; a[i]:=y END END Remove; BEGIN Clear END PriorityQueue;
DEFINITION PriorityQueues; TYPE Queue = RECORD n-: INTEGER END; PROCEDURE Insert (VAR q: Queue; x: INTEGER ); PROCEDURE Remove (VAR q: Queue; VAR x: INTEGER ); PROCEDURE Clear(VAR q: Queue); END PriorityQueues;
DEFINITION PriorityQueues1; TYPE Queue = POINTER TO QueueDesc; QueueDesc = RECORD n-: INTEGER END; PROCEDURE Insert (VAR q: Queue; x: INTEGER ); PROCEDURE Remove (VAR q: Queue; VAR x: INTEGER ); PROCEDURE Clear(VAR q: Queue); END PriorityQueues1;
DEFINITION PriorityQueuesClass; TYPE Queue = RECORD n-: LONGINT; PROCEDURE (VAR q: Queue) Insert ( x: INTEGER ); PROCEDURE (VAR q: Queue) Remove ( VAR x: INTEGER ); PROCEDURE (VAR q: Queue) Clear; END; END PriorityQueuesClass;
TYPE Figure = POINTER TO FigureDesc; FigureDesc = RECORD selected: BOOLEAN; PROCEDURE (f:Figure) Draw; PROCEDURE (f:Figure) Move ( x,y:INTEGER); PROCEDURE (f:Figure) Store ( VAR rider: OS.Rider); END; Rectangle = POINTER TO RectangleDesc; RectangleDesc = RECORD (FigureDesc) x,y,w,h: INTEGER; PROCEDURE (r:Rectangle) Fill (pat: Pattern); END; TextBox = POINTER TO TextBoxDesc; TextBoxDesc = RECORD (RectangleDesc) text: ARRAY 32 OF CHAR; END; VAR rectangle : Rectangle; figure: Figure; figureDesc: FigureDesc; rectangleDesc: RectangleDesc;
A rectangle változó mezõire és eljárásaira , mint rekord mezõkre lehet hivatkozni: a rectangle.selected, rectangle.Draw stb. A kiterjesztett Rectangle, Textbox típusok specializációi a Figure bázis típusnak. A kiterjesztett típusokban nemcsak új eljárásokat lehet bevezetni, de lehetõség van már meglévõek felüldefiniálására is. Az öröklõdés egyik nagy elõnye a bázis típusok és a kiterjesztett típusok kompatibilitása. Ez lényegében azt jelenti, hogy a kiterjesztett típus rendelkezik a bázis típusának minden tulajdonságával. Vagyis a fenti példa alapján: minden Rectangle objektum egyben Figure is, viszont fordítva már nem igaz. Ez azt is jelentheti, hogy egy algoritmus, ami Figure típusú objektummal dolgozik biztos, hogy mûködni fog Rectangle típusúval is, viszont nem fogja kihasználni a Rectangle minden tulajdonságát. A kompatibilitás, inkompabilitás kérdése értékadások esetében biztosan felmerül. Az alábbi értékadás biztos, hogy helyes:
A fenti eljárást meghívhatjuk akár RectangleDesc típusú paraméterrel is, de könnyen elõfordulhat, hogy olyan értékadás szerepel az eljárás törzsében, hogy hibát kapunk.
Rekord ill. mutató típusú változók esetében megkülönböztetünk statikus és dinamikus típust. A változó statikus típusa az a típus, amit a változóhoz rendeltünk a deklarációja során. A dinamikus típus a változó futás idõbeni tényleges típusa, amely különbözhet a statikus típusától.
Rekord vagy mutató típusú változók dinamikus típusa futás idoben ellenõrizhetõ. Erre szolgál a típus teszt:
Visszatérési értéke igaz, ha a figure változó dinamikus típusa Rectangle (vagy legalábbis annak kiterjesztése). Ha a figure változó dinamikus típusa Rectangle, akkor értékül adható a rectangle változónak. Ez akkor lehetséges, ha típusõrt is meghatározunk a figure változóhoz:
A típusõr futásidõben ellenõrzi, hogy a figure dinamikus típusa Rectangle-e. Ha igen akkor ideiglenesen a figure statikus típusa Rectangle lesz, egyébként futás idejû hiba lép fel. A futás idejû hibát el lehet kerülni a típus teszt használatával:
Nyelvi konstrukció nem létezik absztrakt osztályok létrehozására, de létezik megoldás ilyen osztályok kialakítására. Az absztrakt osztályok legfontosabb feladata, hogy kiemelje a leszármazottak közös tulajdonságait és egyfajta közös interfészt adjon, amely az összes leszármazottra érvényes. Az absztakt osztályok egyes metódusait, amelyeket csak a konkrét leszármazottak valósítanak meg absztrakt metódusoknak nevezzük. Ezeket a metódusokat az örököltetés során felül kell definiálnunk. Az absztrakt osztályok számos tervezési mintában is feltünnek. Példa:
TYPE Stream = POINTER TO StreamDesc; StreamDesc = RECORD END; PROCEDURE (s: Stream) Write (ch: Char); BEGIN HALT(99); END Write; PROCEDURE (s: Stream) WriteString (a: ARRAY OF CHAR); VAR i:INTEGER; BEGIN i:=0; WHILE a[i] # 0x DO s.Write(a[i]); i:=i+1 END END WriteString; Ennek egy leszármazottja: TYPE DiskFile = POINTER TO DiskFileDesc; DiskFileDesc = RECORD (StreamDesc) ... END; PROCEDURE (f: DiskFile) Write (ch: CHAR); BEGIN ........ END Write;
Egy komponens generikus, ha különbözo típusokkal képes mûködni. Sok nyelv pl. Ada, Eiffel nyelvi szinten támogatja a generikusságot. Öröklõdés segítségével az Oberonban is tudjuk használni. Nézzünk meg erre egy példát: Egy bináris fa megvalósításához létrehozunk egy Tree osztályt. Az osztályhoz tartozó metódusok a fa elemeivel dolgoznak. A fa elemeit egy általános Node osztály valósítja meg. Késõbb ennek az osztálynak a kiterjesztésével specializálhatjuk az elem típusát.
TYPE Tree = RECORD PROCEDURE ( VAR t: Tree ) Init; PROCEDURE ( VAR t: Tree ) Insert ( x: Node ); PROCEDURE ( VAR t: Tree ) Delete ( x: Node ); PROCEDURE ( VAR t: Tree ) Search ( x: Node ): Node; Node = POINTER TO NodeDesc; NodeDesc = RECORD left,right: Node; PROCEDURE ( x: Node ) EqualTo ( y: Node ): BOOLEAN; PROCEDURE ( x: Node ) LessThan ( y: Node ): BOOLEAN; ....(* A Tree megvalósítása *)
TYPE StringNode = POINTER TO StringNodeDesc; StringNodeDesc = RECORD ( Nodedesc ) s: POINTER TO ARRAY OF CHAR; END; PROCEDURE ( x: StringNode ) EqualTo ( y: Node ): BOOLEAN; BEGIN RETURN x.s^ = y(StringNode).s^ END EqualTo; PROCEDURE ( x: StringNode ) LessThan ( y: Node ): BOOLEAN; BEGIN RETURN x.s^ < y(StringNode).s^ END LessThan;
Az egyik legfontosabb alkalmazása az objektum orientált programozásnak heterogén adatszerkezetek kezelése.
Ezt szituációt a következõk jellemzik:
(1) Az objektumok különbözõ változatban fordulnak elõ.
(2) Az objektumokat használó program nem akarja megkülönböztetni a változatokat.
(3) A jövõbeli változatok számát nem ismerjük, egy újabb változat késõbb is hozzáadható.
Változatok | Mûveletek |
---|---|
Objektimok a szerkesztõben (vonalak,háromszögek, körök) |
Draw, Move, Click |
Objektumok a képernyõn ablakok, ikonok, menük |
Draw, Move Click |
Objektumok a dialógusablakban (gombok,szövegek,görgetõsáv) |
Draw, Move, Click |
Objektumok egy játékban (fogó,préda,falak) |
Draw, Move, Collide |
Objektumok egy szimulációban (autók,személyek,közl. lámpák) |
Activate, Delay |
Ezt a rekordtípus használva olyan lista készíthetõ, amely többféle alakzatot tartalmaz:
Azonban a variáns rekordok hazsnálata veszélyes, mert a legtöbb fordító nem generál kódot annak érdekében, hogy leellenõrizze, vajon a program a helyes változatát használja-e az objektumnak futás közben. Ráadásul bármikor, amikor egy mûvelet hivatkozik egy alakzatra, a lehetséges változatokat meg kell különböztetni a programban. Azért, hogy kirajzoljuk egy lista összes alakzatát, a következõket kéne írnunk:
figure:=firsFigure; WHILE figure # NIL DO (*az osszes alakzat kirajzolasa *) CASE fugure^.kind OF line: ... (* vonal rajzolasa *) | rect: ... (* haromszog rajzolasa *) | circle: ... (* kor rajzolasa *) END; figure:=figure^.next; END;
A CASE esetszétválasztások általában szét vannak szóródva az egész programszöveg területén. Ami a legrosszabb, hogy egy újabb alakzat (pl. spline-ok) bevezetése a Figure adattípus módosítását igényli, ami a kliens nodulok újrafordítását teszi szükségessé. Ráadásul minden case kifejezést hozzá kell igazítani a spline objektumokhoz is. Ez a munka fárasztó és növeli hibázás lehetõségét. Az ilyen fajta szoftver kód külalakja rendetlen és nehéz a kibõvítése.
TYPE Figure = POINTER TO FigureDesc; FigureDesc = RECORD (* absztrakt *) next: Figure; selected: BOOLEAN; PROCEDURE (f: Figure) Draw; PROCEDURE (f: Figure) Move(dx,dy: BOOLEAN); PROCEDURE (f: Figure) HandleMouse(x,y: INTEGER;buttons: SET); PROCEDURE (f: Figure) Load(VAR r: OS.Rider); PROCEDURE (f: Figure) Store(VAR r: OS.Rider); ... END;
TYPE Line = POINTER TO LineDesc; LineDesc = RECORD(FigureDesc) (* szarmaztatas *) x0,y0,x1,y1: INTEGER; PROCEDURE (ln: Line) Draw; PROCEDURE (ln: Line) Move(dx,dy: BOOLEAN); ... END; TYPE Rectangle = POINTER TO RectangleDesc; RectangleDesc = RECORD(FigureDesc) (* szarmaztatas *) x,y,w,h: INTEGER; PROCEDURE (r: Rectangle) Draw; PROCEDURE (r: Rectangle) Move(dx,dy: BOOLEAN); ... END; TYPE Circle = POINTER TO CircleDesc; CircleDesc = RECORD(FigureDesc) (* szarmaztatas *) mx,my,radius: INTEGER; PROCEDURE (c: Circle) Draw; PROCEDURE (c: Circle) Move(dx,dy: BOOLEAN); ... END;
Az ilyen fajta objektumok újra lehetõvé teszik heterogén adatszerkezetek konstrukcióját.
A szerkesztõben minden listabeli objektum statikus típusa Figure. A szerkesztõ csak a Figure osztály mezõit és metódusait látja, bár ténylegesen minden objektum mögött egy vonal, háromszög vagy kör rejtõzik. Az összes alakzat kirajzolásához a szerkesztõnek csak a következõket kell tennie:
figure:=firstFigure; WHILE figure # NIL DO figure.Draw; figure:=figure.next; END;
Amíg a szerkesztõ fut, a felhasználó úgy dönthet, hogy betölti a Lines vagy a Circles modult. Ezek a a modulok hozzá vannak linkelve a már betöltött modulokhoz, ami képessé teszi a szerkesztõt, hogy vonalakat vagy köröket rajzoljon ki. Nem mindig szükséges a szerkesztõ teljes egészét használni. Minden felhasználó betölthet saját alakzatokat anélkül, hogy problémát okozna más felhasználóknak.Lást még Futásidejû kibõvítés
Egy rensdzer igazán csak akkor bõvíthetõ, ha bárki (nem csak az író) bármikor (még futás közben is) ki tudja bõvíteni. Ez a helyzet az Oberonnál. Új modulokat, így új osztályokat lehet implementálni és betölteni, amelyek megléte ismeretlen a program számára, mégis használni tudja õket. A grafikus szerkesztõ semmit sem tud a Splines modulról amíg nincs hozzáadva. A modul hozzáadható a szerkesztõhöz anélkül, hogy módosítanánk vagy újralinkelnék azt. Az interpretált objektum-orientált rendszerekben, mint a Smalltalk, ez szintén lehetséges, de a legtöbb kompilatív renszerben nem. Az Oberon egy kivétel: Kompilatív rendszer, amely olyan fokú kibõvíthetõséget nyújt, mint a Smalltalk.