II. OREXX alapok

Bevezetô

Az elôzô cikkemben nagy vonalakban bemutattam, hogy mi is az az OREXX. Ha valaki kedvet kapott hozzá, ez a cikk (és remélhetôleg az ôt követôek) segít elindulni azon az úton, aminek a végén az OREXX-mágusok összegyûlnek és egymásnak mutogatják programjaikat. (Mindezek ellenére be kell vallanom, hogy maga az OREXX egy meglehetôsen viharos gyermekkort mondhat magáénak. Elôször az OS/2 Developers' Connection CD-n találkoztam egy kezdeti bétájával, késôbb hallgattuk hogy belekerül az új OS/2 verzióba; majd azt, hogy nem, végül azt, hogy mégis. Ilyenkor az ember legyint egyet: "Hiszem, ha látom." Addig is használja a béta verziót, mely apróbb hibáktól eltekintve jól használható.

Ezen cikksorozat célja az OREXX megismertetése azért, mert immáron szinte biztos, hogy az OS/2 következô verziójában beépítve megtaláljuk.) A példaprogramokkal nehéz dolgom van: az objektum-orientált nyelvek rövid programjai többnyire hosszabbak, mint a hagyományos nyelveké, egyszerûen azért mert itt van egy minimum, amit mindenképp le kell írni (objektum definíciók, az objektumok ki- és bemenetei, stb). Igyekeztem rövid, mégis használható példákat gyártani. A példáimnál jóval hosszabb, ám igen hasznos programok találhatóak magában az OREXX-ben, a példaprogramok könyvtárában. Érdemes átfutni ôket, remélhetôleg idôvel nem csak kusza összevisszaságnak fog tûnni a programkód. Emellett igen sok, nagyon apró ám érdekes tesztprogramot gyártottam a cikk kapcsán is (hiszen nem titok, hogy az írás során magam is felfedeztem sok érdekes újdonságot, amik csak a kipróbálás során derültek ki), akik rendelkeznek modemes eléréssel azok ezeket megtalálhatják a LifeForce BBS ObjectREXX területén; akik pedig tudnak interneten FTP-t használni azok az ftp://brain.hajdu.hungary.net/pub/os2/orexx/aprosagok nézhetnek körül.

Alapozás

Az OREXX programoknak öt fô alkotóeleme van:

Kulcsszavak: ezek az OREXX lefoglalt szavai, mint például az IF vagy a WHILE. Segítségükkel többnyire vezérlési szerkezeteket építhetünk fel.

Függvények: ide sorolhatóak az OREXX saját függvényei, az általunk létrehozott föggvények, valamint a külsô modulok függvényei (mint például a RexxUtil, vagy hasonló kiegészítôk). Az OREXX-ben a függvényeknek nem kötelezô értéket visszaadniuk, illetve lehetôség van arra is, hogy a visszaadott értéket figyelmen kívül hagyjuk.

Változók: az OREXX-ben csak egy fajta változó létezik: az objektum. Ettôl nem kell megijedni: ha nincs szükségünk erre a tudásra, az OREXX nem fog vele zavarni, vagyis a változók a REXX-ben szokásos stringnek fognak látszani addig, amíg ki nem akarjuk használni objektum mivoltukat. (A REXX-et ismerôk számára érdekes lehet, hogy a STEM-ek [amikkel a REXX-ben a tömbökhöz hasonló struktúrákat lehett létrehozni] is objektumok lettek.)

Objektum osztályok (class): az objektum osztályok adják meg, hogy a különbözô objektum-típusok miként vislekedjenek, hogyan tartsák a kapcsolatot a környezetükkel. Egy osztályt úgy képzelhetünk el, mint egy öntôformát, amivel a formához hasonló objektum-változókat lehet elôállítani. Léteznek különleges osztályok is, melyekkel az osztályok elkészítését könnyíthetjük meg, ezek az úgynevezett MetaClass-ok, melyek nem objektumokat, hanem egész objektum osztályokat készítenek. (A MetaClassokra sokáig nem lesz szükségünk, és az is elôfordulhat hogy a gyakorlatban egyáltalán nem.)

Külsô utasítások: az OREXXegyik fô jellegzetessége továbbra is az maradt, amit a REXX nyelvben megszokhattunk: minden ismeretlen utasítást a nyelv továbbít a "gazdarendszerhez", vagyis ahhoz a rendszerhez ami az OREXX értelmezôt futtatta. Az OS/2 alatt ez az esetek nagy részében a CMD.EXE becenévre hallgató parancsértelmezô, aminek olyan izgalmas dolgokat lehet mondani mint COPY vagy DEL, de természetesen ilyen módon elindítható bármely futtatható program ugyanúgy, mintha azt parancssorból indítottuk volna. A programnak adhatunk paramétereket, és az esetlegesen visszadott érték (hibakód) az OREXX számára elérhetôvé válik az RC nevû változóban. (ResultCode).

Ezekbôl az elemekbôl építjük fel programjainkat. A programok szintaktikája az elôzô cikkben említett, vagyis a file végzôdése .CMD, az elsô sor kötelezôen egy megjegyzés, utána következik a fôprogram, és végül az objektumok definíciói.

A program futásakor az alábbiak történnek: Az OREXX elôször is átalakítja a programot egy tömörebb (tokenizált) formára, amit a file Extended Attribute-jában tárol. (Ha a program már tokenizált és nem változott meg, akkor természetesen ezt nem végzi el még egyszer.) Ennek során azonnal kiderülhetnek a legszembeszökôbb hibák (általában hibás objektum-definíciók), így a program hibával leáll, még azelôtt, hogy elindult volna. Ilyenkor történik meg az objektum-definíciók feljegyzése, létrehozása is. Ezután következik magának a programnak a végrehajtása. Itt további hibajelzésre már csak akkor számíthatunk, ha a program eljut a hibás részre. (Emiatt fontos, hogy a program tesztelése során lehetôleg minden sorra legalább egyszer jusson el a vezérlés.)

Kezdôknek is, haladóknak is melegen ajánlom az OREXX kiváló hibakeresô utasítását, aki a TRACE névre hallgat. Ha kíváncsiak vagyunk hogy egy programban mik is történnek, hogy a változók milyen típusúak, mi van bennük, vagy hogy a "hogy került ide ez az A betû amikor itt egy 666-os számnak kellene lennie?!" típusú kérdésekre választ kapjunk, akkor érdemes a kérdéses rész elé (vagy a program kezdetére) betenni egy TRACE ?I sort, melybôl az "I" betû jelentése, hogy minden vicikvacakot szeretnénk látni, ami csak történik a változóinkkal, és a "?" azt adja meg, hogy mindezt interaktívan tegye, vagyis minden sor után álljon meg és mutassa az eredményt. (Ha az objektumok belsejére vagyunk kíváncsiak, akkor oda [is] kell elhelyezni a TRAP-et.)

Az objektumok felépítése

Az objektum-orientált programok alapvetô elemei tehát az objektum definíciók (amelyek megmondják, hogy egy adott objektum-osztály mit is tud csinálni), maguk az objektumok (amelyek az osztályok definíciója alapján jönnek létre) valamint a szokásos programkód, ami az objektumokat kezeli.

Az objektumokkal üzeneteken (message) keresztül kommunikálunk. Ha az objektum állapotán (vagyis belsô változóin, melyek a külvilág számára rejtettek) változtatni akarunk, akkor egy üzenetet küldünk az objektumnak. Ezek az üzenetek tartalmazzák magát az üzenetet (az üzenet szövegét) valamint az esetlegesen az üzenettel járó paramétereket. Ugyanez a módszer ha az objektum belsejérôl, állapotáról akarunk megtudni valamit: üzenetet küldönk az objektumnak, hogy meséljen magáról. Az üzenetek az "objektumba érkezve" megkeresik, hogy az objektum ismeri-e ennek az üzenetnek a kezelését. Ha az objektumnak van ilyen belsô üzenet-kezelô eljárása, amit metódusnak (method) hívnak, akkor az objektum meghívja ezt az eljárást valamint átadja neki az üzenetben kapott paramétereket, ha voltak. A metódus elvégzi dolgát (esetleg akár más metódusokat is hívogathat), majd ha van kedve, visszaad egy értéket, ami eljut ahhoz, aki eredetileg elküldte az üzenetet az objektumnak. (Érdekesség, hogy az OREXX lehetôséget ad arra, hogy a küldônek ne kelljen az objektum ajtaja elôtt toporogni a válaszra várva, hanem megmondja az objektumnak hogy dolga végeztével kit értesítsen. Ezekrôl a lehetôségekrôl majd a többszálú végrehajtásnál [multithreading] ejtünk szót.)

Az OREXX-ben az üzeneteket az alábbi szintaxissal írhatjuk (ha nincs paraméter, a zárójeles rész elmaradhat, és ha nincs eredmény, vagy nem érdekes, akkor az értékadás is):

eredmeny = objektum~uzenet(parameterek)

Az objektum tehát megkapja az uzenet üzenetet, és megnézi, hogy van-e ilyen nevû metódusa. Ha van, akkor meghívja az esetlegesen megadott paraméterekkel. Amennyiben az uzenet metódus egy objektumot ad vissza tevékenysége végén, akkor lehetséges ezen eredmény-objektumnak újabb üzenetet küldeni:

eredmeny = objektum~uzenet(parameterek)~uzenet2

Ilyenkor az uzenet metódus eredményeképp elôállt objektum megkapja az uzenet2 üzenetet. Megoldható az is, hogy ha ugyanazon objektumnak akarunk újabb és újabb üzeneteket küldeni egymás után, akkor ne kelljen mindig megírni azt, hogy az objektum visszaadja saját magát értékként (erre egyébként lehetôséget ad a self nevû változó). Ha ugyanazon objektumnak akarunk több üzenetet küldeni, az alábbi formában tehetjük:

eredmeny = objektum~~uzenet1~~uzenet2~uzenet3

A kettôs tilde ("~~") jelentése: üzenet elküldése az objektumnak úgy, hogy a metódus visszaadott értékét eldobja a bitkukába és helyette az eredeti objektumot adja vissza értéknek. Hogy miért szerepel az uzenet3 elôtt csak egy tilde? Azért, mert nincs szükség ott is az objektum-ot visszaadni értéknek, lehet, hogy az uzenet3 hatására kapunk valami használható eredményt.

Mint arról már esett szó, az objektumok egyes példányai (instances) az objektum-osztályokból keletkeznek oly módon, hogy az objektum osztályt megkérjük, hogy gyártson egy újabb objektumot:

ujObjektum = .ujclass~new
ezisObjektum = .ujclass~new

Ezzel sikeresen létrehoztunk két új objektum példányt, amelyek úgy fognak viselkedni, ahogy azt az ujclass megadta nekik. Ilyen példányt akármennyit készíthetünk, a mûködésük (programkódjuk) ugyanaz, azonban mindegyiknek saját belsô állapota (változói) lesznek, és életüket egymástól függetlenül élik.

Öröklés és a metódusok

Az objektumok által ismert metódusokat tehát az az osztály határozza meg, amelybôl készültek. Ezek az osztályok azonban - jólnevelt OOP nyelvhez méltóan - képesek metódusokat örökölni más osztályoktól. Ez az öröklés alapvetô az OOP kényelmes bôvíthetôségéhez. De mit is jelent pontosan ez az öröklés?

Vegyük példának az elôzô cikk (meglehetôsen haszontalan) példáját, a SípolóKapcsolót. Ez úgy keletkezett, hogy az eredetileg megírt Kapcsoló objektum-osztályból (amit ki/be lehetett kapcsolni, illetve lekérdezni az állapotát) öröklôdött a SípolóKapcsoló osztály, ami minden bekapcsoláskor sípolt egyet. Ez azt jelentette, hogy minden tulajdonságot egy-az-egyben örökölt a Kapcsolótól (kikapcsolás és kapcsoló állapot lekérdezése), kivéve a bekapcsoló metódust, ami elôször sípolt egyet, majd meghívta a Kapcsoló bekapcsoló metódusát.

Nézzük meg, mi történik amikor adunk egy bekapcsolás üzenetet a SípolóKapcsolónak! Elôször is megnézi, hogy van-e ilyen metódusa. Van. Sípol egyet, majd meghívja a szülô (superclass, vagy röviden super) megfelelô metódusát. Ehhez az alábbi formát használjuk:

::class SipoloKapcsolo	/* az obj.osztaly neve */
::method be	/* a metodusanak neve */
self~be:super	/* a szulo metodusanak meghivasa */

Annyit tettünk, hogy az objektum saját magának küldött egy üzenetet, de megszabtuk, hogy a metódust nem az objektumban kell elkezdeni keresni, hanem a szülôjében (superclass). Ezzel a módszerrel meg tudjuk csinálni, hogy egy objektum osztály például a kezdeti indításnál ne csak a saját adatstruktúráit tudja létrehozni, de meg tudja hívni az ôseinek a new (létrehozás) vagy init (alapbeállítás) metódusait, így ezeket nem kell még egyszer megírnunk. (Ôszintén szólva ezt leírva jutott eszembe, hogy megnézzem, mit is csinál valójában a FORWARD utasítás, és örömmel láttam, hogy az OREXX ismét segíteni próbál. A FORWARD nagyon jól használható ilyen esetekben, amikor az inicializálásnak meg kell hívnia a szülôi init rutinokat is, ugyanis annyit csinál [alapesetben] hogy a kapott üzenetet továbbítja annak a szülônek, akinek akarjuk. Nagyon kényelmes megoldás: nem kell újraírni az üzenetet, illetve könnyebb általánosított rutinoknál felhasználni [ahol esetleg nem is tudjuk az üzenetet, csak azt, hogy ugyanazt kell továbbítani a szülônek, amit kaptunk].)

Az öröklést fontos ismerni, ugyanis az OREXX minden egyes objektum-osztálya örököl tulajdonságokat (metódusok) más osztályoktól. A Nagy Ôs neve Object Class, minden más osztály az ô leszármazottja, vagyis minden osztály örökli az Object metódusait. A másik Nagy Öreg a Class Class (vagyis "az az Osztály aki Osztályokat állít elô"), ô az OREXX egyetlen elôre elkészített MetaClass-a, vagyis olyan osztály, ami más osztályok elkészítéséhez nyújt alapokat. Az ô metódusait az osztályok öröklik (és nem az osztályok által elôállított objektumok... ez lehet, hogy bonyolultan hangzik - és kicsit az is). Többnyire nem lesz egyikükkel sem közös üzletünk, de hasznos ismerni metódusaikat, hiszen ezek minden objektumban megtalálhatóak.

Ebbôl a két alap osztályból származtatjuk az összes többi osztályt, és belôlük az objektumokat. Természetesen leszármazottaiknak is vannak leszármazottaik, és így egy többé-kevésbé könnyen áttekinthetô hierarchikus rendszert alkotnak. Amikor egy leszármazott kap egy üzenetet, melyet nem ismer, akkor azt továbbítja a közvetlen ôsének, aki vagy ismeri, vagy tovább adja az ô ôsének, egészen addig, míg vagy nem talál egy objektumot aki tudja, mit kell ezzel az üzenettel tenni - vagy el nem érjük az Object class-t aki tanácstalanul nézi hogy mit tegyen vele (és ha nincs ötlete, szép kis hibaüzenettel vidámít bennünket).

Ha ilyen helyzetbe jutunk (egy ismeretlen üzenet söpör végig az osztályok nemzedékein) még van egy esélyünk, hogy jóvá tegyük hibánkat, és ennek neve UNKNOWN metódus. Ha egy ilyen nevû metódust elhelyezünk objektumunkban (vagy valamely ôsében) akkor az ismeretlen üzenet esetén ide kerül a vezérlés. A metódus paraméterként megkapja a hibás üzenetet és annak paramétereit, és próbálkozhat jóvá tenni a dolgot. (Ez a lehetôség valami egészen másra is kiváló, [erre szép példa lesz majd a Directory class,] ugyanis így lehet olyan objektumot írni, ami képes "ismeretlen" metódusokra is értelmesen reagálni. Például egy Névnap Class, ami ha kap egy ismeretlen üzenetet, akkor megnézi, hogy az vajon egy ismert név-e, és ha igen, visszaadja a névnapját. Innentôl a dolog csak a találékonyságon múlik!) Ha azonban nincs ilyen UNKNOWN metódus, és befut egy ismertlen üzenet, akkor jön a hibajelzés.

Az öröklôdést próbálgatva az egyik tesztprogramocskámban tudatosult bennem, hogy pontosan mit is jelent a gyakorlatban az az OREXX leírásban szereplô mondat, hogy a változók csak a saját érvényességi körükön belül (scope) látszanak: sok más OO nyelvvel ellentétben az OREXX-ben a változók nem öröklôdnek, vagyis az osztály leszármazottai nem tudják a szülô változóit kezelni, csakis annak metódusain keresztül. A példaprogramomban ezért kellett metódusokat gyártani a szülô változóinak lekérdezésére, ugyanis észre kellett vennem, hogy a gyerek osztály nem láthatja a szülôben használt változót, noha itt nem külön objektumról van szó, csupán egy öröklési mechanizmusról. (És "tréfás" eredményeket kapunk ha elfelejtjük a szülô objektumok INIT metódusait meghívni egy-egy ilyen esetben.) Összefoglalva tehát azt lehet mondani: az OREXX-ben a metódusok öröklôdnek, az osztály változói nem. (Ennek vannak elônyei is, hátrányai is; szerintem pont ugyanannyi van mindkettôbôl.)

Az objektumok viselkedése

Mint tudjuk, a hagyományos REXX nyelvben minden string volt (errôl az elôzô cikkben már meséltem), és a REXX készítôire nem jellemzô az, hogy egy ilyen apró-cseprô dolog miatt, mint a nyelv kiváló OOP-sítése tönkretegyék a már jól megírt programokat. Emiatt a legtöbbet használt, sôt, alapértelmezett objektum osztály a String. (Ez azt is jelenti, hogy minden olyan változó, amit még nem használtunk, string objektum típusú. Errôl meg is gyôzôdhetünk a say ujvaltozo~defaultname módszerrel.) Egy szokásos értékadásnak (legyen az szám vagy szövegkonstans) egy String objektum az eredménye.

Ezen objektumoknak természetesen üzeneteket kellene küldeni ahhoz, hogy dolgozni lehessen velül, látszólag mégsem ez történik, amikor például leírjuk, hogy a = b + c! Azonban az OREXX csalóka, ugyanis az elôbbi értékadás megegyezik ezzel: a = (b~"+"(c~string)), vagyis a b objektumnak elküldtünk egy "plusz" üzenetet a c objektum tartalmával, mint paraméterrel, és erre a b objektum létrehozott egy újabb, névtelen objektumot (ami szintén String típusú, és az értéke a b+c értékével egyenlô), amit ezután hozzárendeltünk az a objektumhoz. (Azonban az ilyen módon megírt programjainkat inkább olyan helyen teszteljük, ahol más nem láthatja meg...)

Amint láthattuk, amikor egy objektum olyan helyzetbe kerül, hogy adatokat kell szolgáltatnia saját értékérôl, de nem kap üzenetet erre vonatkozóan, akkor az OREXX alapértelmezés szerint elküld egy string üzenetet az objektumnak, azt kérve tôle hogy adjon egy magára jellemzô stringet (amivel az értéket kérô környezet tud majd dolgozni). Az, hogy az objektumok mit tartanak az értekükre jellemzô stringnek... egyéni ízlés kérdése. Igényesebb objektumok olyan formát adnak, melybôl az objektum állapota teljesen visszaállítható a késôbbiek folyamán, de ahogy bonyolultabbak lesznek objektumaink egyre ritkább az ilyen "egyszerû string forma" tökéletes visszaadása.

Meg kell jegyeznem, hogy a tesztprogramjaimat írosgatva érdesek hibával talákoztam, melyet remélhetôleg a végleges változatban már ki fognak javítani. A programom arra számítva, hogy az alapértelmezett metódus a string, használta az alábbi formát:

/* the string crash, orexx hiba (beta?) */
xyzzy = .foo~new
xyzzy
say kesz

::class foo
::method string
say 'en egy objektum vagyok'

A harmadik sorban szereplô objektumnak elméletileg ki kellett volna írnia a szöveget és továbblépve kiírni hogy "kész". Azonban vmi hiba folytán az OREXX zokon veszi, hogy az objektum - mint parancs - nem ad vissza egy egzakt értéket: vagy null stringet ("") vagy valami értelmezhetô parancsot ("DIR"), és a REXX végrehajtó rész egyszerûen kiakad. Ez a hiba csak akkor jelentkezett, ha az automatikus string metódusra bazíroztam, amúgy nincs gond; és ez is kivédhetô úgy, hogy a metódus végére egy " return '' " (üres string visszaadása) utasítást biggyesztünk.

Ha egy objektum nem tud (vagy nem akar) ilyen string formát visszaadni, akkor többnyire a szülôk gondoskodnak errôl, legkésôbb maga Object Class papa, aki ilyenkor ékes angol nyelven közli, hogy az objektum értéke "egy XXX típusú objektum", ami mindenképp jobb, mint a semmi. Mindenképp hasznos, amikor egy változó értékét kiírjuk (pl. SAY utasítással), hogy ilyenkor vagy az objektum értékét látjuk (ha visszaadja), vagy legalább azt, hogy milyen típusú objektumot tartalmaz a változó.

Egy példaprogram

/* PolyMorf.Cmd: A polimorfizmus bemutatasa */

/* csinalunk egy tombot a statisztikai objektumokbol */
foo = .array~of(.count~new, .sum~new, .average~new, .minmax~new)

/* generalunk veletlenszamokat, es mindegyik statisztikai objektumnak atadjuk oket */
do i=1 to 99
  k = random(1000)
  do bar over foo
    bar~add(k)
  end
  call charout ,'.'		/* latszodjon hogy dolgozunk */
end

say ''

/* megmondjuk az objektumoknak hogy kozoljek az eredmenyt */
do bar over foo
  bar~result
end


/*** Egy egyszeru szamlalo **********************************/
::class count
::method init			/* alapbeallitas */
expose counter			/* az objektum EXPOSE nevu valtozojat hasznaljuk */
counter = 0
return self			/* eredmenykent visszaadja sajat magat: a felinicializalt objektumot */

::method result			/* az eredmeny kiirasa */
say self~mytype":" self~myprocess

::method mytype			/* A statisztika neve */
return "Counter"

::method myprocess		/* A statisztika eredmenye */
return self~getcounter

::method cntadd			/* Kulon nevet kapott mert az unokajabol kell hivni */
expose counter
counter = counter+1

::method add			/* Ez az "egyseges" neve a feldolgozo resznek */
use arg n
self~cntadd(n)


::method getcounter		/* Kulon nev azert hogy a gyerekek tudjak hivni */
expose counter
return counter

/*** Egy osszegzo **********************************/
::class sum subclass count	/* A COUNT class-bol szarmaztatjuk */
::method init 
expose sum
self~init:super			/* A szulo INIT meghivasa - itt szuksegtelen */
sum = 0;

::method mytype
return "Sum"

::method myprocess
return self~getsum

::method add
expose sum
use arg n
sum = sum + n

::method getsum
expose sum
return sum

/*** Ez atlagot szamol **********************************/
::class average subclass sum	/* A SUM class-bol szarmaztatjuk */
::method init
self~init:super
return self

::method mytype
return "Average"

::method myprocess
return self~getavg

::method add
use arg n
self~cntadd(n)			/* Ez a nagymama metodusa (ezert kellett neki kulon nev) */
self~add:super(n)		/* Ez a mamajae... */

::method getavg
return (self~getsum)/(self~getcounter)

/*** Ez pedig a vegleteket figyeli **********************************/
::class minmax subclass count	/* Ez is a COUNT-bol szarmazik */
::method init
expose min max
min = 1e666			/* nagyon nagy szam */
max = -1e666			/* ez meg ellenkezoleg */
return self

::method mytype
return "MinMax"

::method myprocess
return self~getminmax

::method add
expose min max
use arg n
if n > max then max=n
if n < min then min=n

::method getminmax
expose min max
return min',' max
/*************************************/

Változók, különleges metódusok

Az objektumok belsô állapotát változóik jellemzik. Ezek a változók "be vannnak 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 lennie, é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ármazottai nem tudják használni. A privát metódusok nagyon egyszerûen hozhatóak 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 file-ok 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 automatikusan indul, méghozzá akkor, aemikor 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 file-ok 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 metodus 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 indulni, 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", vagyis 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:.tavoliOs

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.

* specialis objektumok
nil, I/o, environm

* collection classok
array, bag, directory, list, queue, relation, set, table

* egyeb classok
Alarm, class, message, method, monitor, object, stem, stream, string, supplier

* multithreading
early reply, start, guard mech,

* wps integracio, som
mindenfele orgazmus

* built in objects, runtime obj def
.methods, .rs, .true, .local

error traps
meg hogy miert ,mikor nem, stb

rexxutil
syssleep, systree ilyesmik

Gervai Péter
1996. 09. 27.
[ Elôzô lecke | Következô lecke | Tartalom ]