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. |