Az objektumok belsô állapotát változók jellemzik. Ezek a változók "be vannak zárva" az objektumba (encapsulation), azokhoz kívülrôl nem lehet hozzáférni, csakis az objektum interface-én keresztül, vagyis azokkal a metódusokkal amik ezt a célt szolgálják. Az objektum változói hasonlóan mûködnek az OREXX más változóihoz: nem kötelezô ôket létrehozni, alapértelmezésük a string objektum. Azonban ahhoz, hogy egy-egy metódus használni tudja ôket, "engedélyezni kell" használatukat. Erre a célra szolgál az. EXPOSE utasítás. Használatának módja a példaprogramban látható: a metódus legelsô sorának kell tennie, és felsorolnia azon objektum-változókat, melyeket a metódus használni és esetleg módosítani akar. Ha ezen változókat módosítjuk, akkor az objektum állapota változik, vagyis a többi metódus is a megváltoztatott értékkel fog dolgozni. Ezzel ellentétben lehetôségünk van helyi (local) változók létrehozására is (erre láthatunk példát a program legtöbb osztályának add metódusában, ahol az n egy ilyen lokális változó), ezek értéke elvész, amint a metódus befejezte mûködését, valamint ha van az objektumnak egyezô nevû változója, annak értéke sem fog a késôbbiekre nézve megváltozni. Minden metódusokban használt változó, ami nem szerepel az EXPOSE utasításban, lokális változó lesz.
Lokális változókról szólva ejtsünk szót a privát metódusokról, amelyek hasonló elvek alapján mûködnek. A privát metódus lényege az, hogy csakis azon objektum metódusai "látják", amelyben a privát metódus van. Vagyis sem kívülrôl nem hívható meg, sem az objektum-osztály leszármazottjai nem tudják használni. A privát metódusok nagyon egyszerûen hozhatók létre, mindössze egy private szót kell a metódus fejlécének végére biggyeszteni. Használatára elég ritkán van szükség, de amikor igen, akkor nagyon jól jön. (Például akkor, amikor azt akarjuk hogy még véletlenül se hívjon bele a program más része abba a metódusba.)
Különleges metódusokról szólva illik pár szót ejteni a példaprogramban található init nevû metódusról, illetve az uninit nevû kisöccsérôl. Ez a páros az automatikusan végrehajtódó metódusok büszke családjának tagja, vagyis anélkül is elindulnak, hogy bárki is meghívná ôket. (Ennek ellenére reménykedem, hogy egy erôszakosan magyarítgatónak sem fog eszébe jutni a váratlan vendég metódus" elnevezés...) Az init metódus automatikusan elindul mindig, amikor egy új objektumot hozunk létre, így könnyû és biztonságos módot nyújtva arra, hogy az objektum alapbeállításait elvégezzük, mint például a fontosabb objektum-állapot leíró változók nullázása, a használt fájlok megnyitása, használt erôforrások ellenôrzése, hasonlók. Így biztosak lehetünk abban, hogy nem tudunk olyan objektumot létrehozni, aminek a belsô állapota "bizonytalan", a változóinak talán nincs értéke. (Azt már említettem, hogy az OREXX változók alapértelmezésben string típusúak lesznek. Azt azonban még nem, hogy ilyenkor minden változó a saját nevét tartalmazza nagy betûkkel, tehát például a kutya változó tartalma ha még nem tettünk bele semmit "KUTYA". Ez fontos, ha például a változónak számot kellene tartalmaznia, mert az alapértelmezés nem "0", így a nem elég gondosan beállított változók az elsô számtani mûveletnél aranyos kis hibaüzenettel örvendeztetnek meg bennünket.)
Az uninit az inithez hasonlóan automatkiusan indul, méghozzá akkor, amikor az objektum megszûnik. Hogy ez mikor történik meg, az függ a programunk felépítésétôl. (Külön izgalmas dolog az UNINIT-be egy SAY utasítást betéve figyelni, hogy egy-egy objektum mikor szûnik meg!) Az uninit azonban helytôl függetlenül elindul, mielôtt az objektum kikötne a bitkukában, és eltakarítja az objektum által gyártott hulladékokat. Ide érdemes írni az objektum által nyitva tartott fájlok lezárását vagy egyéb, nem OREXX erôforrás lefoglalásának megszüntetését. Ha ezt nem tesszük meg, (például az objektummal lefoglaljuk a hangkártyát és a megszûnéskor nem szabadítjuk fel,) akkor jó eséllyel a lefoglalás csak a rendszer újraindításával oldható fel, ami meglehetôsen kényelmetlen módja a program befejezésének.
Virtuális metódusok
Az öröklésnél elég egyértelmûnek látszik, hogy a dolog hogyan mûködik: a gyermek osztály ugyanazokat a metódusokat ismeri, mint a szülô, plusz a sajátjait. Ez így van, azonban nézzük meg a bemutatkozó cikkben szereplô példát:
/* virtualis metodusok? */ a = .nagy~new b = .kicsi~new a~dolgozik b~dolgozik b~papa return ::class 'Nagy' ::method 'uzen' say self~string 'iranyitasaval:' "Nagy vagyok! " '0d0a'x return ::method 'dolgozik' say self~string 'iranyitasaval:' "A nevem:" self~uzen return ::class 'Kicsi' subclass nagy ::method 'uzen' say self~string 'iranyitasaval:' "Kicsi vagyok!" '0d0a'x return ::method 'papa' say self~string 'iranyitasaval:' "Az en papam mondja:" self~uzen:super return
A dolgozik metódus elôször bemutatkozik, majd meghívja az uzen metódust. Ez a Papánál nem okoz különlegességet: a papa dolgozik-ja meghívja a papa uzen-jét - minden egyszerû.
Azonban a Kicsi esetében kissé más a helyzet. Kicsi örökölte Papától a metódusait, többek között a dolgozik és az uzen metódusokat, majd ezekbôl az uzen-t lecserélte a sajátjára. Mi is történik akkor, amikor Kicsi kap egy üzenetet, hogy dolgozzon?
Az üzenet eljut Kicsi-hez, aki megnézi, hogy van-e neki dolgozik metódusa. Mivel ilyen nincs, eggyel feljebb lépünk, és az üzenetet megkapja az ôs: Papa. Neki van ilyen metódusa, ami eljut odáig, hogy meghívja a self-uzen részt. Ilyenkor azonban a self nem a Papát tartalmazza, hanem azt, aki az üzenetet eredetileg kapta, vagyis Kicsi-t. Hogy ez miért jó? Természetesen azért, mert az üzenetek feldolgozása mindig "legalulról" indul (a legkésôbbi leszármazottól, vagyis az éppen futó objektum osztályától), és így lehetôségünk van csupán egy-két metódust lecserélni ahelyett, hogy mindent újra kellene írni, ami ezt a metódust használja (meg esetleg azokat is, amiket még ezen kívül használ). A megoldás az OREXX részérôl egyszerû: a self értéke mindig az eredeti, hívó objektum.
Ezt a mechanizmust azonban néha szükséges kikerülni, mert néha nem az aktuális objektumtól kell indulnia, hanem az ôseinek a metódusát kell használni. Ha a közvetlen ôsrôl van szó (superclass), akkor ez nem jelent gondot, hiszen errôl már esett szó, egyszerûen a super módosítót használjuk a metódus meghívásakor:
::method beallit say 'itt beallitom sajat magam' self~beallit:super /* itt pedig az osomet */
Ha nem írtam volna oda a super-t (ennek a szerkezetnek a neve "Class override", felülbírált osztály), akkor a beállít a végtelenségig hívogatta volna saját magát. Ugyanezen módszer használható azonban akkor is, ha az objektumunknak sok-sok ôse van, és nem a közvetlen ôsétôl akarunk valamit kérni. Ilyenkor a Class override általános formáját használhatjuk:
self~metodus:osztaly például: self~beallit:.tavoli.Os
Arra kell figyelni, hogy az override osztály kell, hogy legyen, és nem objektum, vagyis a neve egy ponttal kell, hogy kezdôdjön. Ezzel a módszerrel megoldhatjuk, hogy az üzenet ne a leszármazási fa legaljáról induljon, hanem akár valahonnan a közepétôl, vagy akár magának az Object osztálynak is üzengethetünk, kikerülve a leszármazottait és az azok által módosított, öröklôdött metódusokat.
Speciális objektumok
Az OREXX minden program számára biztosítja azokat az osztályokat, melyekrôl fentebb említés esett, és késôbb még bôvebben is esik szó róluk (ilyen a String, Stack vagy akár az Object Class). Ezen osztályokon kívül azonban a nyelv biztosít számunkra elôre elkészített objektumokat is, melyeket felhasználhatunk programjainkban. Ezek - más nyelvek elôre definiált konstansaihoz vagy rendszerváltozóihoz hasonlóan - a program indításakor rendelkeznek egy konstans vagy egy elôre meghatározott értékkel; egy részüket meg is tudjuk változtatni.
Elôször nézzük meg a konstansokat, melyek értékadásánál és értékvizsgálatánál (például egy összehasonlítás egy IF utasításban) hasznosak.
Talán a legtöbbet használt konstans objektum a .NIL. Ez üres objektum, metódusait közvetlenül az Object Class-ból kapta. Amellett, hogy sok függvény .NIL objektumot ad vissza akkor, amikor "üres" a végeredmény, ezzel az objektummal tesztelhetjük például azt, ha egy tömb üres (ha üres karakterlánccal hasonlítanánk, az eredmény hamis lenne, hiszen az üres tömb eredménye egy "üres tömb objektum", és nem egy null string). Két másik objektum konstans a .TRUE és a .FALSE, melyek a logikai "igaz" és "hamis" értékeket adják meg. Alaposabban megnézve persze láthatjuk, hogy ez inkább a jól olvasható kód készítését segíti, mivel ezek értéke az "1" illetve a "0" szám (ami a hagyományos REXX-ben is az igaz és hamis értékeket jelentette).
Gyakorlatilag ennyi objektum-konstanssal rendelkezünk, és sokkal érdekesebbek is az elôre létrejött, de változtatható objektum-változók:
Az Environment Object (aminek a jelentése "Környezet Objektum") egy nagyon speciális gyermek: szinte soha nem hivatkozunk rá, de mindig használjuk. Ez úgy lehetséges, hogy minden ponttal kezdôdô név amit használunk (tehát a konstansaink, beépített változóink vagy az elôre elkészített vagy általunk létrehozott objektum osztályok) egy-egy bejegyzés az Environment nevû (Directory, vagyis "Katalógus" típusú) objektumban. (Ha teljesen egzakt akarok lenni, akkor el kell árulnom, hogy az .ENVIRONMENT Directory Objektum tulajdonképpen önmagát (az Environment Objektumot) tartalmazza... De ez tényleg csak érdekesség, a munkát nem befolyásolja.) Ahogy a neve is mutatja, ebben az objektumban tárolódik le mindaz, ami az éppen futó program számára "a környezetbôl elérhetô". Lehetôségünk van akár közvetlenül is változtatni, noha erre elég ritkán van szükség, azonban mivel ez az a környezet, mely közös minden rendszerünkön futó OREXX program számára, nagyon kényelmes így megoldani az egész rendszerre kiterjedô változtatásokat. (A változtatások azonnal érzékelhetôk ismét egy végtelenül egyszerû módszer több program szinkronizálására. Sôt, az .ENVIRONMENT nem felejt, azaz a változtatások a rendszer újraindításáig érvényben is maradnak! [Ezért ha valamire már nincs szükségünk, takarítsuk el a szemetet magunk után!])
Ehhez hasonló - pontosabban ennek mintegy kiegészítôje - a .LOCAL, aminek a neve helyi környezet objektum. A név itt is jellemzô, vagyis ez pont úgy mûködik mint az .ENVIRONMENT, csak ez nem a rendszer egészére, hanem az aktuális programunkra (és a hozzátartozó thread-ekre [szálakra] vonatkozik). Szintén Directory, tehát a Directory Class metódusaival módosítható, a globális környezethez hasonlóan.
Van még azonban az OREXX-ben hasznos elôregyártott objektum, méghozzá az összefoglaló néven I/0 (vagyis Input/Output, Bemenet/Kimenet) objektumok. Ezekbôl három van:
Ezek mindegyike a késôbbiekben még szóba kerülô Monitor Class jeles tagja, ami számunkra mindössze annyiban fontos, hogy a Monitor metódusaival lehet ôket vezérelni. Mindegyikük - természetesen - az environment-ben, méghozzá annak a helyi (.local) példányában található, így lehetôség van a "direkt" módosítgatásukra is. De a legegyszerûbb módja használatuknak az, hogy maguknak az objektumoknak meséljük el kívánságainkat, vagyis átirányítást kérhetünk egy fájlba a
.output~destination( .stream-new( 'celpont.dat' ) )
módon, és az átirányítást megszüntethetjük a
.output~destination
módszerrel. Egyszerû, nem?
Ugyanígy a bemenet is átirányítható, a PULL és társai ilyenkor nem a billentyûzetrôl várják az adatokat, hanem a megadott fájlból. Az .error átirányításának egy fájlba viszont igazából csak akkor van értelme, ha az átirányítás mellett lekezeljük a hibajelenségeket is (ON ERROR trap, ezekrôl késôbb még lesz szó), hiszen attól, hogy az üzenet még fájlba megy, a program ugyanúgy megáll.
| Gervai Péter 1996. 12. 15. | [ Elôzô lecke | Következô lecke | Tartalom ] |