Capitolul III
Reprezentarea informațiilor cu obiecte

3.1 Obiecte
3.2 Încapsularea informațiilor în interiorul obiectelor
3.3 Clase de obiecte
3.4 Derivarea claselor de obiecte
3.5 Interfețe spre obiecte

3.1 Obiecte

Informațiile pe care le reprezentăm în memoria calculatorului sunt rareori atât de simple precum culorile sau literele. În general, dorim să reprezentăm informații complexe, care să descrie obiectele fizice care ne înconjoară sau noțiunile cu care operăm zilnic, în interiorul cărora culoarea sau o secvență de litere reprezintă doar o mică parte. Aceste obiecte fizice sau noțiuni din lumea reală trebuiesc reprezentate în memoria calculatorului în așa fel încât informațiile specifice lor să fie păstrate la un loc și să se poată prelucra ca un tot unitar. Să nu uităm însă că, la nivelul cel mai de jos, informația atașată acestor obiecte continuă să fie tratată de către compilator ca un șir de numere naturale, singurele informații reprezentabile direct în memoria calculatoarelor actuale.

Pentru a reprezenta în memoria internă obiecte fizice sau noțiuni, este nevoie să izolăm întregul set de proprietăți specifice acestora și să îl reprezentăm prin numere. Aceste numere vor ocupa în memorie o zonă compactă pe care, printr-un abuz de limbaj, o vom numi, într-o primă aproximare, obiect. Va trebui însă să aveți întotdeauna o imagine clară a deosebirii fundamentale dintre un obiect fizic sau o noțiune și reprezentarea acestora în memoria calculatorului.

De exemplu, în memoria calculatorului este foarte simplu să creăm un nou obiect, identic cu altul deja existent, prin simpla duplicare a zonei de memorie folosite de obiectul pe care dorim să-l dedublăm. În realitate însă, este mult mai greu să obținem o copie identică a unui obiect fizic, fie el o simplă foaie de hârtie sau o bancnotă de 10000 de lei.

Să revenim însă la numerele naturale. Din moment ce ele sunt singurele entități reprezentabile în memoria calculatorului, este firesc ca acesta să fie echipat cu un set bogat de operații predefinite de prelucrare a numerelor. Din păcate, atunci când facem corespondența dintre numere și litere de exemplu, nu ne putem aștepta la un set la fel de bogat de operații predefinite care să lucreze cu litere. Dar, ținând cont de faptul că în cele din urmă lucrăm tot cu numere, putem construi propriile noastre operații specifice literelor, combinând în mod corespunzător operațiile numerice predefinite.

De exemplu, pentru a obține dintr-o literă majusculă, să spunem ‘A’, corespondenta ei minuscul㠑a’, este suficient să adunăm un deplasament numeric corespunzător, presupunând că literele mari și cele mici sunt numerotate în ordine alfabetică și imediat una după cealaltă în convenția de reprezentare folosită. În setul ASCII deplasamentul este 32, reprezentarea lui ‘A’ fiind 65 iar reprezentarea lui ‘a’ fiind 97. Acest deplasament se păstrează pentru toate literele din alfabetul englez și român, la cel din urmă existând totuși o excepție în cazul literei ‘Ș’.

Putem să extindem cerințele noastre mai departe, spunând că, atunci când analizăm un obiect fizic sau o noțiune pentru a le reprezenta în calculator, trebuie să analizăm nu numai proprietățile acestora dar și modul în care acestea pot fi utilizate și care sunt operațiile care pot fi executate asupra lor sau cu ajutorul lor. Acest set de operații va trebui ulterior să-l redefinim în contextul mulțimii de numere care formează proprietățile obiectului din memoria calculatorului, și să îl descompunem în operații numerice preexistente. În plus, trebuie să analizăm modul în care reacționează obiectul atunci când este supus efectului unor acțiuni exterioare. Uneori, setul de operații specifice unui obiect împreună cu modul în care acesta reacționează la stimuli exteriori se numește comportamentul obiectului.

De exemplu, dacă dorim să construim un obiect care reprezintă o minge de formă sferică în spațiu, este necesar să definim trei numere care să reprezinte coordonatele x, y și z relativ la un sistem de axe dat, precum și o valoare pentru raza sferei. Aceste valori numerice vor face parte din setul de proprietăți ale obiectului minge. Dacă mai târziu vom dori să construim o operație care să reprezinte mutarea în spațiu a obiectului minge, este suficient să ne folosim de operațiile cu numere pentru a modifica valorile coordonatelor x, y și z.

Desigur, obiectul minge este insuficient descris prin aceste coordonate și, pentru a simula în calculator obiectul real este nevoie de multe proprietăți suplimentare precum și de multe operații în plus. Dar, dacă problema pe care o avem de rezolvat nu necesită aceste proprietăți și operații, este preferabil să nu le definim în obiectul folosit pentru reprezentare. Rezultatul direct al acestui mod de abordare este acela că vom putea defini același obiect real în mai multe feluri pentru a-l reprezenta în memoria internă. Modul de definire depinde de problema de rezolvat și de programatorul care a gândit reprezentarea. De altfel, aceste diferențe de percepție ale unui obiect real există și între diverși observatori umani.

Din punctul de vedere al programării, un obiect este o reprezentare în memoria calculatorului a proprietăților și comportamentului unei noțiuni sau ale unui obiect real.

Figura 3.1 Modelul de reprezentare al unui obiect în memorie. Stratul exterior reprezintă doar operațiile care oferă calea de a interacționa cu proprietățile obiectului și nu are corespondent direct în zona de memorie ocupată de obiect. ^

3.2 Încapsularea informațiilor în interiorul obiectelor

Există situații în care accesul din exterior la proprietățile unui obiect poate să pună probleme acestuia. Ce s-ar întâmpla de exemplu dacă s-ar putea accesa direct valorile care definesc funcționalitatea corpului uman? Desigur, există cazuri în care acest lucru ar fi îmbucurător. N-ar mai fi nevoie să acționăm indirect asupra concentrațiilor de enzime în corp ci am putea să modificăm aceste valori în mod direct. Dar, în același timp, am putea provoca mari necazuri în cazul în care am modifica aceste valori în afara pragului suportabil de către organism. Din aceste motive, este preferabil să lăsăm modificarea acestor parametri în sarcina exclusivă a unor operații definite de către obiect, operații care vor verifica noile valori înainte de a le schimba în interiorul obiectului. În lipsa acestui filtru, putem să stricăm coerența valorilor memorate în interiorul unui obiect, făcându-l inutilizabil.

Din acest punct de vedere, putem privi obiectul ca pe un set de valori care formează miezul obiectului și un set de operații care îmbracă aceste valori, protejându-le. Vom spune că proprietățile obiectului sunt încapsulate în interiorul acestora. Mai mult, obiectul încapsulează și modul de funcționare a operațiilor lui specifice, din exterior neputându-se observa decât modul de apelare a acestor operații și rezultatele apelurilor. Cu alte cuvinte, procesul de încapsulare este procesul de ascundere a detaliilor neimportante sau sensibile de construcție a obiectului.

Dar nu numai proprietățile unui obiect trebuiesc protejate ci și operațiile definite de către acesta. Unele dintre operațiile definite pentru un obiect, cum ar fi de exemplu procesul respirator al omului, sunt periculos de lăsat la dispoziția oricui. Este preferabil să putem controla foarte exact cine ce operații poate apela pentru un anumit obiect. În acest mod, din punctul de vedere al unui observator străin, omul este perceput ca un obiect mult mai simplu decât este în realitate, pentru că acel observator nu poate vedea decât acele valori și nu poate apela decât acele operații care sunt făcute publice.

Desigur, în practică este nevoie de o oarecare rafinare a gradului de protejare a fiecărei operații sau proprietăți în așa fel încât însuși accesul observatorilor exteriori să poată fi nuanțat în funcție de gradul de similitudine și apropiere al observatorului față de obiectul accesat. Rafinarea trebuie să meargă până la a putea specifica pentru fiecare proprietate și operație în parte care sunt observatorii care au acces și care nu.

Această protejare și încapsulare a proprietăților și operațiilor ce se pot executa cu ajutorul unui obiect are și o altă consecință și anume aceea că utilizatorul obiectului respectiv este independent de detaliile constructive ale obiectului respectiv. Structura internă a obiectului poate fi astfel schimbată și perfecționată în timp fără ca funcționalitatea de bază să fie afectată. ^

3.3 Clase de obiecte

În lumea reală se pot identifica ușor familii de obiecte. Este greu să descriem într-un limbaj de programare fiecare minge din lume dar, pentru a putea folosi orice minge din lume, este suficient să descriem o singură dată care sunt proprietățile unei mingi în general, precum și operațiile care pot fi executate cu aceasta. Aceasta nu înseamnă că toate obiectele minge din lume sunt identice. Diferența dintre ele se află reprezentată în primul rând în valorile proprietăților lor care sunt mărimi numerice variabile, adică diferă de la un obiect de același fel la altul. De exemplu, în fiecare obiect minge vom avea un număr natural care reprezintă culoarea mingii. Acest număr poate să difere de la o minge la alta exact așa cum, în realitate, culoarea diferă de la o minge la alta. La fel coordonatele poziției mingii la un moment dat sau raza mingii precum și materialul din care este confecționată au valori care variază de la o minge la alta.

Cu alte cuvinte, fiecare minge din lume are același set de proprietăți, dar valorile acestora pot să difere de la o minge la alta. Modelul de reprezentare în memorie a unui obiect este întotdeauna același, dar valorile memorate în locațiile corespunzătoare proprietăților sunt în general diferite.

În ceea ce privește operațiile, acestea sunt întotdeauna aceleași dar rezultatul aplicării lor poate să difere în funcție de valorile proprietăților obiectului asupra căruia au fost aplicate. De exemplu, atunci când aruncăm o minge spre pământ ea va ricoșa din acesta ridicându-se din nou în aer. Înălțimea la care se va ridica însă, este dependentă de dimensiunile și materialul din care a fost confecționată mingea. Cu alte cuvinte, noua poziție în spațiu se va calcula printr-o operație care va ține cont de valorile memorate în interiorul obiectului. Se poate întâmpla chiar ca operația să hotărască faptul că mingea va străpunge podeaua în loc să fie respinsă de către aceasta.

Să mai observăm că operațiile nu depind numai de proprietățile obiectului ci și de unele valori exterioare acestuia. Atunci când aruncăm o minge spre pământ, înălțimea la care va ricoșa aceasta depinde și de viteza cu care a fost aruncată mingea. Această viteză este un parametru al operației de aruncare. Nu are nici un rost să transmitem ca parametrii ai unei operații valorile proprietăților unui obiect pentru că acestea sunt întotdeauna disponibile operației. Nici o operație nu se poate aplica asupra unui obiect fără să știm exact care este obiectul respectiv și ce proprietăți are acesta. Este absurd să ne gândim la ce înălțime se va ridica o minge în general, fără să facem presupuneri asupra valorilor proprietăților acesteia. Să mai observăm însă că, dacă toate mingile ar avea aceleași valori pentru proprietățile implicate în operația descrisă mai sus, am putea să calculăm înălțimea de ricoșeu în general, fără să fim dependenți de o anumită minge.

În concluzie, putem spune că obiectele cu care lucrăm fac parte întotdeauna dintr-o familie mai mare de obiecte cu proprietăți și comportament similar. Aceste familii de obiecte le vom numi în continuare clase de obiecte sau concepte în timp ce obiectele aparținând unei anumite clase le vom numi instanțe ale clasei de obiecte respective. Putem vorbi despre clasa de obiecte minge și despre instanțele acesteia, mulțimea tuturor obiectelor minge care există în lume. Fiecare instanță a clasei minge are un loc bine precizat în spațiu și în timp, un material și o culoare. Aceste proprietăți diferă de la o instanță la alta, dar fiecare instanță a aceleiași clase va avea întotdeauna aceleași proprietăți și aceleași operații vor putea fi aplicate asupra ei. În continuare vom numi variabile aceste proprietăți ale unei clase de obiecte și vom numi metode operațiile definite pentru o anumită clasă de obiecte.

Pentru a clarifica, să mai reluăm încă o dată: O clasă de obiecte este o descriere a proprietăților și operațiilor specifice unui nou tip de obiecte reprezentabile în memorie. O instanță a unei clase de obiecte este un obiect de memorie care respectă descrierea clasei. O variabilă a unei clase de obiecte este o proprietate a clasei respective care poate lua valori diferite în instanțe diferite ale clasei. O metodă a unei clase este descrierea unei operații specifice clasei respective.

Să mai precizăm faptul că, spre deosebire de variabilele unei clase, metodele acesteia sunt memorate o singură dată pentru toate obiectele. Comportarea diferită a acestora este dată de faptul că ele depind de valorile variabilelor.

O categorie aparte a claselor de obiecte este categoria acelor clase care reprezintă concepte care nu se pot instanția în mod direct, adică nu putem construi instanțe ale clasei respective, de obicei pentru că nu avem destule informații pentru a le putea construi. De exemplu, conceptul de om nu se poate instanția în mod direct pentru că nu putem construi un om despre care nu știm exact dacă este bărbat sau femeie. Putem în schimb instanția conceptul de bărbat și conceptul de femeie care sunt niște subconcepte ale conceptului om.

Clasele abstracte, neinstanțiabile, servesc în general pentru definirea unor proprietăți sau operații comune ale mai multor clase și pentru a putea generaliza operațiile referitoare la acestea. Putem, de exemplu să definim în cadrul clasei de obiecte om modul în care acesta se alimentează ca fiind independent de apartenența la conceptul de bărbat sau femeie. Această definiție va fi valabilă la amândouă subconceptele definite mai sus. În schimb, nu putem decât cel mult să precizăm faptul că un om trebuie să aibă un comportament social. Descrierea exactă a acestui comportament trebuie făcută în cadrul conceptului de bărbat și a celui de femeie. Oricum, este interesant faptul că, indiferent care ar fi clasa acestuia, putem să ne bazăm pe faptul că acesta va avea definit un comportament social, specific clasei lui.

Cele două metode despre care am vorbit mai sus, definite la nivelul unui superconcept, sunt profund diferite din punctul de vedere al subconceptelor acestuia. În timp ce metoda de alimentație este definită exact și amândouă subconceptele pot să o folosească fără probleme, metoda de comportament social este doar o metodă abstractă, care trebuie să existe, dar despre care nu se știe exact cum trebuie definită.

Fiecare dintre subconcepte trebuie să-și definească propriul său comportament social pentru a putea deveni instanțiabil. Dacă o clasă de obiecte are cel puțin o metodă abstractă, ea devine în întregime o clasă abstractă și nu poate fi instanțiată, adică nu putem crea instanțe ale unei clase de obiecte abstracte.

Altfel spus, o clasă abstractă de obiecte este o clasă pentru care nu s-au precizat suficient de clar toate metodele astfel încât să poată fi folosită în mod direct. ^

3.4 Derivarea claselor de obiecte

O altă proprietate interesantă a claselor de obiecte este aceea de ierarhizare. Practic, ori de câte ori definim o nouă clasă de obiecte care să reprezinte un anumit concept, specificăm clasa de obiecte care reprezintă conceptul original din care provine noul concept împreună cu diferențele pe care le aduce noul concept derivat față de cel original. Această operație de definire a unei noi clase de obiecte pe baza uneia deja existente o vom numi derivare. Conceptul mai general se va numi superconcept iar conceptul derivat din acesta se va numi subconcept. În același mod, clasa originală se va numi superclasă a noii clase în timp ce noua clasă de obiecte se va numi subclasă a clasei derivate.

Uneori, în loc de derivare se folosește termenul de extindere. Termenul vine de la faptul că o subclasă își extinde superclasa cu noi variabile și metode.

În spiritul acestei ierarhizări, putem presupune că toate clasele de obiecte sunt derivate dintr-o clasă inițială, să-i spunem clasa de obiecte generice, în care putem defini proprietățile și operațiile comune tuturor obiectelor precum ar fi testul de egalitate dintre două instanțe, duplicarea instanțelor sau aflarea clasei de care aparține o anumită instanță.

Ierarhizarea se poate extinde pe mai multe nivele, sub formă arborescentă, în fiecare punct nodal al structurii arborescente rezultate aflându-se clase de obiecte. Desigur, clasele de obiecte de pe orice nivel pot avea instanțe proprii, cu condiția să nu fie clase abstracte, imposibil de instanțiat.

Figura 3.2 O ierarhie de clase de obiecte în care clasele sunt reprezentate în câmpuri eliptice iar instanțele acestora în câmpuri dreptunghiulare. Clasele abstracte de obiecte au elipsa dublată.

Desigur, este foarte dificil să construim o ierarhie de clase de obiecte completă, care să conțină clase de obiecte corespunzătoare fiecărui concept cunoscut. Din fericire, pentru o problemă dată, conceptele implicate în rezolvarea ei sunt relativ puține și pot fi ușor izolate, simplificate și definite. Restrângerea la minimum a arborelui de concepte necesar rezolvării unei anumite probleme fără a se afecta generalitatea soluției este un talent pe care fiecare programator trebuie să și-l descopere și să și-l cultive cu atenție. De alegerea acestor concepte depinde eficiența și flexibilitatea aplicației.

O clasă de obiecte derivată dintr-o altă clasă păstrează toate proprietățile și operațiile acesteia din urmă aducând în plus proprietăți și operații noi. De exemplu, dacă la nivelul clasei de obiecte om am definit forma bipedă a acestuia și capacitatea de a vorbi și de a înțelege, toate acestea vor fi moștenite și de către clasele derivate din clasa om, și anume clasa bărbaților și cea a femeilor. Fiecare dintre aceste clase de obiecte derivate își vor defini propriile lor proprietăți și operații pentru a descrie diferența dintre ele și clasa originală.

Unele dintre proprietățile și operațiile definite în superclasă pot fi redefinite în subclasele de obiecte derivate. Vechile proprietăți și operații sunt disponibile în continuare, doar că pentru a le putea accesa va trebui să fie specificată explicit superclasa care deține copia redefinită. Operația de redefinire a unor operații sau variabile din interiorul unei clase în timpul procesului de derivare o vom numi rescriere.

Această redefinire ne dă de fapt o mare flexibilitate în construcția ierarhiei unei probleme date pentru că nici o proprietate sau operație definită într-un punct al ierarhiei nu este impusă definitiv pentru conceptele derivate din acest punct direct sau nu.

Revenind pentru un moment la protejarea informațiilor interne ale unui obiect să precizăm faptul că gradul de similitudine de care vorbeam mai sus este mărit în cazul în care vorbim de două clase derivate una din cealaltă. Cu alte cuvinte, o subclasă a unei clase are acces de obicei la mult mai multe informații memorate în superclasa sa decât o altă clasă de obiecte oarecare. Acest lucru este firesc ținând cont de faptul că, uneori, o subclasă este nevoită să redefinească o parte din funcționalitatea superclasei sale. ^

3.5 Interfețe spre obiecte

Un obiect este o entitate complexă pe care o putem privi din diverse puncte de vedere. Omul de exemplu poate fi privit ca un mamifer care naște pui vii sau poate fi privit ca o ființă gânditoare care învăță să programeze calculatoare sau poate fi privit ca un simplu obiect spațio-temporal care are propria lui formă și poziție în funcție de timp.

Această observație ne spune că trebuie să dăm definiții despre ce înseamnă cu adevărat faptul că un obiect poate fi privit ca un mamifer sau ca o ființa gânditoare sau ca un obiect spațio-temporal. Aceste definiții, pe care le vom numi în continuare interfețe, sunt aplicabile nu numai clasei de obiecte om dar și la alte clase de obiecte derivate sau nu din acesta, superclase sau nu ale acesteia. Putem să găsim o mulțime de clase de obiecte ale căror instanțe pot fi privite ca obiecte spațio-temporale dar care să nu aibă mare lucru în comun cu omul. Practic, atunci când construim o interfață, definim un set minim de operații care trebuie să aparțină obiectelor care respectă această interfață. Orice clasă de obiecte care declară că respectă această interfață va trebui să definească toate operațiile.

Operațiile însă, sunt definite pe căi specifice fiecărei clase de obiecte în parte. De exemplu, orice obiect spațial trebuie să definească o operație de modificare a poziției în care se află. Dar această operație este diferită la un om, care poate să-și schimbe singur poziția, față de o minge care trebuie ajutată din exterior pentru a putea fi mutată. Totuși, dacă știm cu siguranță că un obiect este o instanță a unui clase de obiecte care respectă interfața spatio-temporală, putem liniștiți să executăm asupra acestuia o operație de schimbare a poziției, fără să trebuiască să cunoaștem amănunte despre modul în care va fi executată această operație. Tot ceea ce trebuie să știm este faptul că operația este definită pentru obiectul respectiv.

În concluzie, o interfață este un set de operații care trebuiesc definite de o clasă de obiecte pentru a se înscrie într-o anumită categorie. Vom spune despre o clasă care definește toate operațiile unei interfețe că implementează interfața respectivă.

Cu alte cuvinte, putem privi interfețele ca pe niște reguli de comportament impuse claselor de obiecte. În clipa în care o clasă implementează o anumită interfață, obiectele din clasa respectivă pot fi privite în exclusivitate din acest punct de vedere. Interfețele pot fi privite ca niște filtre prin care putem privi un anumit obiect, filtre care nu lasă la vedere decât proprietățile specifice interfeței, chiar dacă obiectul în vizor este mult mai complicat în realitate.

Interfețele crează o altă împărțire a obiectelor cu care lucrăm. În afară de împărțirea normală pe clase, putem să împărțim obiectele și după interfețele pe care le implementează. Și, la fel cu situația în care definim o operație doar pentru obiectele unei anumite clase, putem defini și operații care lucrează doar cu obiecte care implementează o anumită interfață, indiferent de clasa din care acestea fac parte. ^

[cuprins]
(C) IntegraSoft 1996-1998