Minden program tökéletes programként kezdi meg életét, és ez az állapot egészen addig tart, amíg a programot legelôször ki nem próbáljuk. Ezzel nem azt akarom mondani, hogy nem létezik olyan program, ami csinál is valamit (noha a mai világban néha el-elkap ez az érzés), hanem inkább azt, hogy lehetôség szerint fel kell készítenünk programjainkat a legrosszabbakra is: ne álljon tanácstalanul ha valahol a közepén elgépeltünk egy sort, amire ezer esetbôl csak egyszer kerül a vezérlés, ne nézzen üres tekintettel ha az a file, amit másfél milliszekundummal ezelôtt olvasott hirtelen eltûnt, vagy ne rogyjon egy halk sikoltással magába ha véletlenül olyan dolgot kellene kiszámolnia, amit épeszû számítógép jogainak felemlegetése mellett megtagad.
Nos, ezeknek a kényes eseményeknek megelôzésére nyújtja az OREXX a hibakezelésre alkalmas rutinjait, az angol nyelven csak "csapdáknak" titulált Error Trap-eket. Alapesetben az OREXX jó nevelô módjára minden egyes hibánkra nagyon részletes, jól értelmezhetô hibajelzéseket produkál, pontosan megadva a hiba helyét, mibenlétét és legtöbbször ha lehet, további információkat a hiba valószínû okairól. Azonban ezzel a programunk futása lezárult, ha adatokat tartottunk a memóriában arra várva hogy azt a késôbbiekben diskre írjuk, akkor ezen reményeink bizonyosan a füstbemenés klasszikus példáját valósították meg. Hasonlóan negatív hatású az is amikor valami vizuálisan kellemes képernyô közepére esik be ez a részletes, ám esztétikailag nem túl cizellált hibaüzenet, a szép képernyônk romjai közé behelyezkedô beviteli sorral egyetemben.
Az elgondolás egyszerû és mégis hatékony: a programmal elôre közöljük, hogy ha bizonyos hibaesemények elôfordulnak akkor mely az a rutin, aki ezzel érdemben tud foglalkozni, és esetleg csökkenteni a hiba által okozható adatállományi vagy vizuális sérüléseket; lehetôségek szerint elmentve az elmentendôket, illetve esztétikusan tudtunkra adva a hibát tisztességgel hagyja el a programot.
Ezen túl vannak olyan hibák melyek nem szükségszerûen szakítják meg a program futását, hanem esetleges menet közbeni korrigálásukkal a program akár tovább is folytatható. Ilyen hibák lehetnek a file-okkal kapcsolatos problémák ("NOTREADY"), vagy egyes külsô parancsok sikertelen végrehajtási eredményei. Vannak továbbá olyan események melyek normális esetben ugyan hibajelzést nem produkálnak, de nem kívánatosak (mint például egy nagypontosságú számításnál a kerekítés), és számos paraméter egyenkénti vizsgálata helyett néha egyszerûbb csak a töredék esetben elôforduló "nem kívánatos" eseményeket figyelni.
A hibakezelés két utasításon keresztül történik, a SIGNAL ON HIBATÍPUS illetve a CALL ON HIBATÍPUS utasítással. Az elôbbi (a SIGNAL utasítással analóg módon) hiba esetén ráugrik a hibakezelô rutinra, melybôl nincs lehetôség visszatérésre, a program folytatására. Ilyenkor csak a szükséges kilépés elôtti teendôk elvégzésére marad lehetôségünk. Csak ezzel a módszerrel használhatóak az alábbi hibaesetek:
| nomethod | Egy objektumnak egy ismeretlen üzenet küldése esetén aktivizálódik, ha az objektum amúgy ezt nem kezeli le az UNKNOWN metódussal. |
| lostdigits | Akkor lép életbe ha számítások közben a beállított számábrázolási pontosság mellett (NUMERIC DIGITS) számjegyek vesznek el. |
| syntax | Olyan szintaktikai hibákra érzékeny, melyek a program indításkori fordítása során nem derültek ki. Sajnos ide kerültek a "kritikus" számítási hibák is, mint például a nullával való osztás vagy a számábrázolási lehetôségek teljes túllépése. |
| novalue | Akkor aktivizálódik amikor egy olyan változóra hivatkozunk, melynek nem adtunk még értéket. Ez a "hiba" a program normális futása során soha nem kerül elô, hiszen minden változó "saját nevét" tartalmazza mindaddig, míg értéket nem adunk neki. |
A másik lehetôségünk a CALL ON utasítás használata, mely - hasonlóan a CALL-hoz - lehetôséget ad a feldolgozás után a program folytatására. Az alábbi hibacsapdák hívhatóak meg ilyen módon:
| halt | Ez a "hiba" azt jelzi, hogy a felhasználó kérte a program futásának megszakítását a CTRL-C vagy CTRL-Break billentyûkombinációval. |
| notready | A megegyezô nevû Stream üzenetre reagál, vagyis mikor egy stream-mel dolgozva ez az állapot áll elô: a file nem létezik, nem olvasható vagy például megpróbálunk a file vége után olvasni. |
| failure | Ha egy rutin hívása meghiúsult (általában külsô parancsoknál) akkor jut ide a vezérlés. |
| error | Ha egy rutin "hibás" értékkel lép ki (általában ez külsô - pl. DOS, OS2 - parancsok esetén fordul elô) akkor aktivizálódik a rutin. |
| any | Az a "mindenben illetékes" trap, minden olyan hibajelenségre aktivizálódik melyre egyébként nem definiáltunk specifikus trap-et. Mivel - mint azt késôbb láthatjuk - a hiba pontos okát késôbb is megtudhatjuk, sokszor egyszerûbb így elkapni "mindent", és utána dönteni a pontos eljárásról. |
A signal vagy call utasítás alapértelmezés szerint a hibatípusnak megfelelô nevû címkére ugrik. Lehetôségünk van saját címkét is megadni a NAME CIMKE sor végére biggyesztésével, ha erre lenne szükségünk.
Az egyes hibafigyelési módokat ki is tudjuk kapcsolni, a SIGNAL OFF HIBATÍPUS formában.
A hibakezelô rutinban természetesen kapunk információkat arról, hogy milyen hiba történt, hol, valamint némi plusz segítséget amennyiben erre lehetôség van. A legalapvetôbb segítség a SIGL változó, mely azt a sort adja meg, ahol a hiba jelentkezett. (A mellékelt példaprogramban [TRAP1.CMD] láthatunk egy módszert arra, hogy lehet ezt ötletelesen felhasználni a sor forrásprogramból való kikeresésére.) A legtöbb hibatípus esetén rendelkezésre áll az RC változó (illetve a .RC objektum) is a hiba kódszámával.
Emellett használhatjuk még a CONDITION utasítást is, mely számos hasznos (és kevésbé hasznos) információval láthat el bennünket, a hibás változó értéke mellett például azt is megmondja, hogy pontosan milyen hiba történt, és ezt pl. felhasználhatjuk egy CALL ON ANY típusú hibacsapda esetén a lehetôségek szétválogatására.
De ejtsünk néhány szót magáról a hibakezelô rutinról, hiszen ez is egy futó programrész, így elvileg itt is történhetnek hibák (noha ezek elôzetes kiszûrésére fordítsunk sokkal nagyobb figyelmet). Amikor egy hibaesemény egy SIGNAL ON utasítással hibakezelôre ugrik, az adott hiba automatikusan SIGNAL OFF állapotba kerül, így egy újabb elôfordulása immár az OREXX beépített hibakezelésének hatáskörében fog lezajlani. A CALL ON hibák általában egy speciális állapotba teszik az adott hiba hibakezelôjét, ez a DELAY, vagy késleltetett mód. Ilyenkor az újabb hibák a hibakezelô programrész befejezéséig (RETURN) várakoznak, majd szépen sorban lekezelésre kerülnek.
Lehetôségünk van a fenti viselkedést befolyásolni, mivel a hibakezelô programrészben is (szinte) szabadon alkalmazhatjuk a SIGNAL ON/CALL ON utasítást, így esetleg külön hibakezelô részeket írni a "hibakezelô részben elôforduló hibákra". (Igen, kedves olvasóm, jól gondolod, hogy a hibakezelô hibakezelôjében is keletkezhetnek hibák, de a Tökéletesen Hibavédett Programot helyhiány miatt most ne tárgyaljuk ennyire részletesen... Az elsô végtelen oldalszámú OS/2 Times-ban erre még visszatérünk. :-)) Azonban azt tudni kell, hogy a hibakezelô rutin befejezôdésekor visszaállítja minden egyes csapdának a hibakezelô meghívása elôtti állapotát. Œgy tehát nyugodtan módosíthatjuk ôket, ettôl a programunkban beállított értékek nem fognak megváltozni.
Osztály nélküli metódusok és menet közben készített objektumok
Érdekes lehetôség, melyet már említettem régebben, hogy a nyelv lehetôvé teszi számunkra azt, hogy a program futása közben készítsünk új osztályokat, objektumokat. A legtöbb esetben erre nincs szükség, a szükséges osztályok tartalmát a program futása általában nem befolyásolja. Néha azonban jól jöhet a menet közbeni módosíthatóság, és interpretált (menet közben értelmezett) nyelvrôl lévén szó az alkotók részérôl vétkes hanyagság lett volna kihagyni ezt a lehetôséget.
A Classless.CMD nevû példaprogram egy egyszerû példája a menet közbeni objektum-varázslatnak. A folyamat maga egyszerû, mert szinte pontosan követi a szokványos osztály- és objektum-készítés módszerét. Elôször el kell döntenünk, hogy az új osztályunk melyik - már létezô - osztályból óhajtjuk származtatni. Ez a kiválasztott osztály a SUBCLASS('ujnev') üzenet hatására készít egy új osztályt ujnev néven, melynek ô lesz az ôse. Az új osztály ebben a pillanatban még ugyanazon metódusokkal rendelkezik, mint ôse, azonban hamarosan változtatunk ezen tarthatatlan állapoton a DEFINE üzenettel.
A DEFINE('metodusnev', MethodObject) formájú üzenettel boldogítva új osztályunkat annak új metódust tudunk megtanítani, melynek programkódját a MethodObject (Method tipusú objektum, melyekrôl már volt szó sorozatunkban) adja meg. Egyszerû, tiszta módszer. Lehetôség van még a DEFINE('metodusnev', String) módon új metódusokat biggyeszteni osztályunkhoz, ahol a String (vagy Array objektum) tartalma maga a programkód szöveges formában. Œly módon tetszôlegesen bôvíthetjük osztályunkat, és természetesen abból akár újabb osztályokat is leszármaztathatunk a SUBCLASS üzenettel.
Ha készen vagyunk az osztályunk építésével akkor ugyanúgy, mint ahogy eddig, elkezdhetünk létrehozni objektum példányokat a NEW üzenettel. Voil…, kész a menet közben fabrikált objektum.
Talán csak az érdeklôdôbb olvasókban merül fel (és valószínûleg ôk azok, akiket a programozók ökölbe szorított kézzel emlegetnek, hogy miattuk kell mindenre gondolniuk): mi történik akkor, ha az osztályból már készítettünk objektumokat, esetleg akár származtattunk is belôle új osztályokat, és azokból is készítettünk objektumokat, és így tovább; szóval hogy mi történik a már elkészített példányokkal akkor, ha nekiállunk módosítgatni a "szülô" osztályukat? Kíváncsian indultam neki ennek a kérdésnek, és érdekes dolgokat tapasztaltam, melyet a DynaMethod.CMD nevû programocskában osztok meg mindenkivel.
Az történt, hogy a kiszemelt metódus módosítása után az osztályomból közvetlenül készített régi objektumok nem vettek tudomást a módosításról, és a metódus "régi kódját" használták. Azonban - legnagyobb meglepetésemre - a osztályból származtatott alosztályból készített objektumok (kicsit mindig félek, hogy valaki meg akar verni, mikor ilyeneket leírok... :-)) tudtak a változásról, és már az új kódot használták a változtatás pillanatától kezdve. Elgondolkoztam, hogy ennek mi lehet az értelme, és ha nem tévedek (ami persze elôfordulhat), akkor itt láthattam azoknak a bizonyos "virtuális metódusoknak" a mûködését, amikrôl réges-rég említést tettem. Az osztályom alosztálya tehát nem "másolta le" az osztályom metódusait, melyeket örökölt, hanem csak megjegyezte, hogy ha szükség van rájuk, akkor az osztályomtól kell elkérni a kódot. (Œgy ha sokan örökölnek egy metódust, akkor a kód nem kerül mindannyiszor lemásolásra, memóriát és egyéb erôforrásokat használva.) Azonban az osztályból készült objektum-példányok fizikailag is lemásolták a saját osztályukhoz tartozó kódot. Œgy azok, akik "másolgattak" azok nem tudtak a változásról, míg azok, akik csak annyit jegyeztek meg hogy "itt kell megkérdezni ha szükség van rá", mindig a legfrisebb kódot kapták. Érdekes bepillantás ez a kulisszák mögé, az OREXX mûködésébe.
De térjünk vissza a címben is jelzett osztály nélküli metódusokra. Ennek így elsô hallásra nem sok értelme van, hiszen hogyan létezhetne metódus objektum nélkül? De ha emlékezünk a Method osztályra és objektumaira akkor termésetesen már találkoztunk ilyenekkel. De itt most nem a menet közben készülô metódusokról lesz szó, hanem a "szokásoshoz" hasonlóan a programkód végére írtakról. Ezek elônye - azon kívül hogy a "szokványos módszerrel" készülnek - az, hogy a program indításakor kerülnek ellenôrzésre és lefordításra, így - fôleg bonyolultabb programrész esetén - gyorsabban futnak. Tehát készítsünk osztály nélküli metódusokat egyszerûen úgy, hogy a legelsô ::CLASS sor elé írjuk a metódusok definícióit (::METHOD sorok). Azonban felmerül a kérdés, hogy "jó-jó, megírtuk, de hogyan lehet hozzájuk férni?"
Erre a válasz a rendszer által rendelkezésünkre bocsájtott .METHODS Directory objektum, mely tartalmazza az osztály nélküli ("classless") metódusokat. Ezeket innentôl ugyanúgy használhatjuk, mint bármelyik Method objektumot, különös tekintettel a fentebb leírt menet közben készülô osztályokra. Œgy a metódusok a program "objektum orientált részén" (a végén) találhatóak, esetleg külön file-ban, és csak az osztályokhoz rendelésüket kell a programon "belül" megoldanunk.
Példaprogramjaimat gyártva saját káromon tanultam meg, hogy gondosan tartsuk észben, hogy a metódusok neveit a rendszer nagybetûsre alakítja. A .METHODS directoryból kisbetûs nevekkel nem sok hasznos dolgot fogunk kinyerni.
| Gervai Péter 1997. 12. 14. | [ Elôzô lecke | Következô lecke | Tartalom ] |