CA-Visual Objects László István
Subsistemul OOP Acest material este o continuare a articolului despre mecanismele automate din CA-Visual Objects. Se presupune, că cititorul a avut contact cu un limbaj orientat obiect, măcar la nivelul obiectelor limbajului CA-Clipper. În mare parte, subsistemul OOP al limbajului CA-Visual Objects se conformează standardului în cea ce privește, în general, limbajele orientate obiect. Totuși - parțial datorat prezenței unui depozit de date active - există anumite diferențe de implementare. Pe lângă aceste diferențe, mai puțin importante, CA-Visual Objects oferă o serie de noutăți excitante și soluții elegante, care nu pot fi regăsite la alte limbaje. Cei doi factori, prezența depozitului activ de date, respectiv noutățile prezente fac din CA-Visual Objects un limbaj promițător. Merită menționat, de la început, că entitătile din CA-Visual Objects nu sunt delimitate de clauze de tipul BEGIN - END sau FUNCTION - RETURN. Entitătile sunt păstrate independent în depozitul de date al mediului (repository) -aceasta are implicația interesantă, pe de o parte că secventialitatea entităților nu are nici o importanță, pe de altă parte o entitate pur și simplu se termină acolo, unde începe o nouă entitate... Noțiuni de bazăÎn CA-Visual Objects sunt prezente diverse clase predefinite care pot fi utilizate direct, dar și programatorului i se permite definirea claselor proprii. În acest sens, sunt disponibile toate mecanismele obișnuite și chiar și facilități noi în privința mecanismelor orientate obiect. Declararea unei clase class MyClass Clasa astfel declarată este cea mai simplă clasă, deoarece un obiect al acestei clase nu are nici atribute (numite variabile de instantă), nici rutine de servicii sau acțiuni asociate (numite metode). O clasă sau o declarație singuratică de clasă nu are nici o utilitate, deoarece așa cum este nu poate să facă nimic; este doar o definiție a proprietătilor pe care le-ar avea un obiect al clasei, și comportamentul lui - dacă acest obiect ar exista... Diferența între o clasă și un obiect este critică: pe când obiectul există în spatiu și în timp, clasa este o definiție abstractă, o rețetă sau un plan de a construi obiectul respectiv. Puțin simplificat , o clasă există în timpul compilării, și obiectele doar în timpul execuției programului. Instanțierea obiectelorComanda executabilă de creare a unui obiect, sau a unei instanțe a unei clase, se dă prin numele clasei, urmat de acolade ({}). În contextul exemplului precedent: MyClass{} Ca urmare a acestei comenzi executabile, sistemul va crea un obiect, un exemplar al clasei MyClass. Se poate testa tipul rezultatului returnat, folosind funcția ValType(). ? ValType( MyClass{} ) // "O" de la Obiect Obiecte ale claselor predefinite pot fi instanțiate exact la fel, doar că nu se declară clasele respective - declarația lor se găsește în diverse biblioteci. În anumite situații, obiectele create conform exemplului precedent sunt utile prin efectele laterale ale procesului de creare. Dacă în viitor se dorește accesarea obiectului creat, este nevoie de un mecanism prin care se asigură regăsirea obiectului respectiv. Implementare referențialăCa și rezultat al instanțierii unei clase, sistemul returnează o referință la obiectul creat; această valoare (referință) poate fi păstrată într-o variabilă. Operatiile pe obiecte se pot face numai prin variabile, care conțin o referință la obiectul respectiv. oObiect := MyClass{} Valoarea referinței este o valoare totalmente obișnuită, este de tipul O (de la Object), ea poate fi atribuită unei variabile, transmisă unei funcții ca și parametru, sau returnată de aceasta. Fără să intrăm în detalii, propun studierea următoarei secvențe de cod dat, pentru exemplificarea caracterului referențial: local oOne, oTwo oOne := MyClass{} oTwo := MyClass{} ? oOne == oTwo // afiseaza FALSE oTwo := oOne ? oOne == oTwo // afiseaza TRUE oTwo:MyExportVar := 10 ? oOne:MyExportVar // afiseaza 10 Tipizarea variabilelorDin motive de productivitate și eficiență, CA-Visual Objects suportă atât variabile netipizate, cât și variabile strict tipizate. (Parantezele drepte încadrează clauze opționale.) local oObiect [as usual] Linia de mai sus declară o variabilă locală de tip nedefinit sau polimorfă. Compilatorul nu verifică și nu restricționează sub nici o formă tipul valorilor care pot fi păstrate în variabile polimorfe. CA-Visual Objects oferă posibilitatea tipizării statice a variabilelor.
În acest context, cuvântul static nu se referă la delimitarea vizibilitătii,
ci la restricționarea definitivă doar la un singur tip a valorilor care
pot fi păstrate în variabila respectivă. Variabilele utilizate, indiferent de valabilitatea lor, pot fi deci strict tipizate. local oObiect as Pe lângă tipurile de bază ale limbajului (SYMBOL; INT, FLOAT, SHORT, LONG,
BYTE, WORD, DWORD, REAL4, REAL8; DATE; LOGIC; VOID; PTR, PSZ, AS Tipul OBJECT este un tip de bază predefinit.
Prin această comandă, se declară o variabilă, care poate păstra valori
de tip OBJECT. Orice altă tentativă de atribuire se va solda cu eroare.
Problema este că, în acest fel, compilatorul nu va fi capabil să verifice
cu exactitate clasa. Nivelul următor de severitate în declararea tipului
se poate regăsi în exemplul de mai jos:
Folosind o tipizare strictă, indiferent de nivelul de severitate, aceasta
oferă nu numai un cod mai rapid, dar și posibilitatea eliminării unor erori
latente, deja din timpul compilării.
Bineînțeles, și varianta mai slab tipizată are avantajele ei: în acest
mod se permite definirea unui cod polimorf, care de exemplu poate prelucra
obiectele diverselor clase.
În concluzie, se poate lucra cu următoarele feluri de variabile: Tipizarea variabilelor, care vor conține referințe la obiecte, poate fi
făcută cu diverse grade de libertate: În ideea refolosirii componentelor, una din proprietătile de importanță
deosebită a limbajului este posibilitatea definirii unei clase, bazată pe
definiția unei alte clase. În acest caz, clasele copil moștenesc toate atributele
și comportamentul de la clasa părinte. Prin această tehnică, este posibilă
extinderea unei clase, fără perturbarea funcționalității.
Proprietățile, atributele unui obiect se materializează în structuri de
date de o complexitate oarecare. Fiecare exemplar (instanță) al clasei la
crearea ei primește o copie a întregului set de variabile. Declararea
acestor variabile, adică a variabilelor de instanță se face la nivelul clasei,
dar în acel moment (la compilare) reprezintă doar șablonul după care se
vor crea obiectele clasei respective.
Variabilele de instanță se conformează acelorași reguli de tipizare ca
și toate variabilele: pot fi polimorfe, sau - folosind clauza opțională
AS Valoare inițială a variabilelor de instanță Variabilele tipizate, la crearea lor, automat se inițializează la valoarea
nulă a tipului respectiv (NULL_ARRAY, NULL_CODEBLOCK, NULL_DATE, FALSE,
Valabilitatea și vizibilitatea variabilelor de instanță Valabilitatea variabilelor de instanță este strict legată de valabilitatea
obiectului din care fac parte, dar vizibilitatea lor este determinată de
modificatorul aplicat la declararea lor.
Vizibilitatea variabilelor de instanță din CA-Visual Objects se conformează
tabelului următor:
Deoarece diverse obiecte ale aceleiași clase (sau al unei alte clase)
au inevitabil variabile de instanță cu același nume, nu este suficientă
specificarea numelui variabilei de instanță. Pentru a înlătura nesiguranța
la referirea unei variabile de instanță, este nevoie și de specificarea
obiectului de care aparține variabila. Pentru a referi o variabilă de instanță
se folosește operatorul send (:).
Variabilele de instanță exportate fac parte din interfața publică a obiectelor,
reflectă starea reală a acestora. Variabilele EXPORT care sunt deci vizibile
codului exterior clasei violează principiul încapsulării, deoarece permit
acces necontrolat la atributele obiectelor. Ca urmare, vom reveni cu o soluție
la această problemă după o scurtă introducere a folosirii metodelor...
Comportamentul, serviciile specifice unei clase se materializează într-un
grup de funcții sau așa-zise metode, care sunt strict legate de clasa respectivă.
O metodă conform definiției din CA-Visual Objects este codul unei singure
acțiuni în comportamentul obiectului; în general, metodele sunt ca și funcțiile
(au parametri, conțin declarații și instrucțiuni, comenzi și valori returnate),
cu anumite diferențe: Definirea metodelor Corpul metodei poate conține și una sau mai multe comenzi RETURN [ Invocarea metodelor La recepționarea unui mesaj, se execută metoda cu același nume - dacă
aceasta există. Invocarea unei metode prin sistemul de mesaje este similară
apelării unei funcții sau a unei subrutine.
Referințe la obiectul curent Linia marcată cu 1 este eronată, deoarece - vezi liniile 1 și 3 - apelul
unei metode se poate confunda cu apel de funcție. Soluția este folosirea
unei referințe la obiectul curent SELF și a operatorului send (:) pentru
a înlătura nesiguranța - vezi linia 2.
Merită făcută observația, că limbajul este consecvent în modul de invocare
al funcțiilor și metodelor: funcțiile se apelează totdeauna prin simplul
nume, iar metodele folosind o valoare de tip referință (variabilă sau SELF),
operatorul send și numele metodei.
Valoarea returnată Vizibilitatea metodelor Există două cuvinte cheie care pot fi folosite pentru a modifica vizibilitatea
metodelor: PROTECT și HIDDEN. Metodele protejate sunt vizibile din clasa
curentă și subclasele ei, iar cele ascunse doar din clasa pentru care s-a
definit metoda.
Începând cu versiunea 2.0 CA-Visual Objects permite definirea și a metodelor
strict tipizate. Metodele tipizate - cu totul, că flexibilitatea lor este
mult mai mică, sunt utile deoarece prin folosirea lor se obține un cod mult
mai stabil și mai rapid (legarea metodelor poate fi făcută timpurie, deja
în timpul compilării).
Pentru a declara metodele tipizate, se folosește clauza opțională DECLARE
a definiției de clasă:
Definiția metodei se extinde cu specificarea tipului returnat, și a convenției
de apel, ca și la funcții. (Datorită caracterului hibrid al limbajului,
sunt prezente mai multe posibilităti de a declara modul de apel al funcțiilor
și procedurilor. CALLBACK respectă convenția Windows, PASCAL asigură eficiență
prin rigiditatea ei, respectiv CLIPPER oferă flexibilitatea obișnuită la
limbajele Xbase.)
Avantajul variantei tipizate este viteza mai mare, respectiv siguranța,
corectitudinea mărită a codului, față de flexibilitatea superioară a metodelor
netipizate.
Metoda Init() De obicei, metoda Init() este folosită la executarea unor inițializări
(de exemplu, la setarea unor valori implicite pentru variabilele de instanță
sau alocarea unor resurse - ca și deschiderea unui fișier - necesare funcționării
obiectului).
Metoda Axit() De remarcat este, conform filozofiei CA-Visual Objects, că timpul de viață
al unui obiect este strâns legat de posibilitatea de referențiere a ei.
Adică, până când există o variabilă prin care obiectul poate fi referit,
el există, dar odată cu dispariția tuturor referințelor asupra ei, obiectul
este osândit distrugerii. Acest proces de distrugere este executat de un
mecanism automat de colectare a reziduurilor.
Dacă utilizatorul definește metoda Axit(), aceasta este invocată automat,
înainte de distrugerea obiectului.
Cum am mai discutat, obiectele au o parte statică - care se reflectă prin
structură, prin relații și atribute; și o parte dinamică - oglindită prin
comportament, sau mesaje și servicii (sinonimă: metode).
Fiecare obiect are o interfață bine definită, care determină care sunt
stimulii care sunt acceptați de obiect, adică ce operații pot fi
efectuate asupra obiectelor. (...) Fiecare stimul cauzează executarea unei
operații, sau accesează direct o variabilă de instanță.
Folosind mecanismul subclasării, se asigură moștenirea structurii și comportamentului
clasei părinte de către clasele copil.
Funcționalitatea noii clase se poate extinde prin Din exteriorul unei clase doar variabilele de instanță EXPORT sunt vizibile,
celelalte variabile interne sau neexportate sunt neaccesibile din funcții
sau metode externe clasei. Pentru a accesa variabilele interne se pot folosi
metode. Aceasta corespunde principiului încapsulării, dar este o soluție
incomodă.
O soluție mult mai elegantă - și extrem de flexibilă - este oferită de
metodele speciale ACCESS și ASSIGN. Aceste metode se folosesc la implementarea
variabilelor de instanță virtuale, care de fapt simulează prin intermediul
metodelor ACCESS și ASSIGN o variabilă de instanță obișnuită.
Sintaxa pentru accesarea, respectiv pentru asignarea acestor variabile
virtuale este aceeași ca și pentru o variabilă reală - singura diferență
este că accesul prin acest mod devine controlat.
Cum se observă, se pot defini fără nici o restricție sau obligativitate
metode ACCESS și ASSIGN cu același nume ca și variabila de instanță la care
au rolul de a controla accesul.
Dat fiind faptul că nu se impune ca să existe o variabilă concretă care
să stea în spatele metodelor de acces și asignare, se pot defini metode
speciale care implementează variabile virtuale read-only, write-only, sau
read-write; aceste variabile sunt vizibile doar, sau dacă doriți, există
prin intermediul acestor metode speciale.
Cuvântul cheie SUPER servește la referirea predecesorului direct al unei
clase. Prin acest cuvânt cheie, este posibilă referirea metodelor și a variabilelor
de instanță vizibile ale superclasei (sau a părintelui).
Dacă pentru o subclasă se definește o metodă cu un nume, care se suprapune
cu denumirea unei metode a părintelui, pentru obiectele subclasei la un
apel se va identifica și executa noua metodă. Practic noua metodă înlocuiește
vechea metodă a superclasei. Prin aceasta, funcționalitatea clasei se modifică.
Sunt situații, când se dorește - fără perturbarea funcționalității - o
extindere a metodei redefinite. Pentru aceasta, nu este necesară cunoașterea
structurii clasei-părinte: folosind cuvântul cheie SUPER, se poate executa
și metoda superclasei.
Folosirea acestei tehnici este singura posibilitate de a extinde funcționalitatea
unei metode la o subclasă.
Exemplu clasic este metoda specială Init(), care trebuie să execute anumite
inițializări la nivelul clasei curente - fără ca să se cunoască structura
sau nevoile superclasei.
Pentru a detecta mesajele la care în mod direct nu poate răspunde clasa,
sunt dedicate trei metode. În funcție de sintaxa prin care se trimite mesajul
obiectului, se invocă metoda corespunzătoare.
NoIVarGet() se apelează la referirea pentru citire (operatia get) a unei
variabile de instanță inexistentă conform sintaxei NoIVarPut() se apelează la referirea pentru atribuire (operatia put) a
unei variabile de instanță inexistentă conform sintaxei NoMethod() se apelează la receptionarea de către un obiect a unei mesaj
pentru care nu există metoda corespunzătoare, conform sintaxei Ideea de bază este ca, în cazul în care obiectul primește un mesaj pe
care mecanismul automat nu îl poate controla direct, să nu se anunțe direct
sistemul de erori, ci să fie apelat un mecanism care poate decide comportamentul
obiectului în cauză.
O utilizare deosebit de interesantă a acestei tehnici, specifice limbajului
CA-Visual Objects, este posibilitatea creării obiectelor care sunt capabile
să-și modifice structura sau comportamentul în runtime.
Ca și exemplu de valoare clasică, se poate prezenta problema reflectării
structurii unei baze de date (.DBF) în obiectul de interfață, să o numim
DBServer - în cazul în care structura fișierului de date nu este în prealabil
cunoscută.
O funcționalitate extrem de interesantă, oferită de CA-Visual Objects,
este posibilitatea extinderii elegante a sintaxei operatorilor folositi
la tipuri de date simple și pe clase (tipuri) definite de utilizator.
Operatorii care pot fi redefiniți, respectiv corespondența dintre operatori
și metodele care joacă rolul operatoarelor este determinată intern.
Lista metodelor dublate prin operatori, care pot fi definite pentru orice
clasă, este: Add(), Mul(), Sub(), Div(), Pow(), Gtr(), Less(), GtrEqu(),
LessEqu(), Neg(), Not()
În fond, pentru operatorii redefiniți se vor apela pur și simplu metodele
corespunzătoare. Avantajul major al acestei tehnici este că acest apel se
va face conform sintaxei operatorilor.
Avantajul major al acestei tehnici este că se poate lucra mult mai natural
cu obiecte, folosind sintaxa deja obișnuită a operatorilor pentru operații
considerate clasice.
Interfața publică a unei clase poate fi interogată prin intermediul unor
funcții speciale. În cele ce urmează, prezentăm câteva categorii - însă
fără dorinta să fim compleți Domeniul fiind prea vast, în acest moment nu intenționăm să intrăm în
detalii. În cazul în care un vector conține obiecte similare, același mesaj poate
fi trimis la toate obiectele vectorului printr-o sintaxă foarte simplă:
Expresia
este perfect echivalentă cu următoarea secvență de comenzi
Subsistemul obiectual al limbajului CA-Visual Objects este robust și
comprehensiv. El se bazează pe sâmburele obiectual al limbajului CA-Clipper,
și ca urmare este o extensie naturală a unui limbaj Xbase, dedicat prelucrării
bazelor de date. Eficient și flexibil, este la fel de competitiv ca și celelalte
limbaje obiectuale populare. Dar CA-Visual Objects este caracterizat de
simplitate și de o eleganță deosebită.
Subsistemul obiectual al CA-Visual Objects, împreună cu mecanismele automate
și serviciile oferite, îi conferă limbajului puterea necesară ca aceasta
să poate aspira spre a deveni standard industrial.
BYTE România - decembrie 1997
(C) Copyright Computer Press Agora |