Programarea orientata pe obiecte
Introducere
Obiectul reprezinta în cadrul unui program, o entitate compusa dintr-o structura proprie de date si dintr-un set de subprograme, care actioneaza asupra structurii proprii de date. Daca obiectul a fost corect definit din punct de vedere logic, acesta devine un modul specializat în realizarea unor anumite functiuni. Din acest punct de vedere, s-ar putea spune ca este similar unei biblioteci de subprograme, specializate pe o anumita directie. Exista însa câteva mari deosebiri:
programarea orientata pe obiecte, prin facilitatile create în cadrul acestei tehnici de programare, ofera un grad mult mai înalt de modularizare si de independenta între module;
utilizarea obiectelor în programul propriu-zis, permite crearea unui stil de programare deosebit de cel traditional, prin aparitia unui limbaj mai apropiat de u 17517t197r tilizator (referirea este la programatorul de aplicatie), limbaj ce poate fi specific unui anumit proiect sau unui set de proiecte într-un anumit domeniu.
Programarea orientata pe obiecte este o tehnica de programare, ce implica pentru limbajul gazda, existenta unui mecanism-suport necesar acestui stil de programare. Rezulta de aici ca nu orice limbaj de programare admite acest stil de lucru. Primul limbaj orientat pe obiecte, Simula, a fost dezvoltat la mijlocul anilor '60. Urmatorul limbaj reprezentativ pentru programarea orientata pe obiecte a fost Smalltalk. Acestea nu au cunoscut o raspândire semnificativa pâna la aparitia unor procesoare puternice, cum ar fi cele din familia Intel 80386 sau 80486. Suportul tehnic superior oferit de aceste procesoare, a determinat dezvoltarea suportului necesar programarii orientate pe obiecte si în limbajele Java, C++, Pascal si Modula.
Obiectul reprezinta un nou tip de variabila, care se comporta ca si celelalte tipuri ale limbajului. Acest tip se obtine printr-o declaratie similara tipului record din Pascal sau struct din C, cu mentiunea ca structura descrisa contine pe lânga date si proceduri si functii. Datele declarate într-un obiect poarta numele de membri, iar procedurile si functiile se numesc metode. Definirea obiectului contine numai antetele metodelor, blocurile ce descriu continutul lor fiind specificate separat.
Sintaxa pentru definirea unui obiect in C++, este:
class <nume obiect>
Definirea unui obiect (a unei clase) se face de obicei in fisiere de tip header (cu extensia .h). Functiile ce descriu metodele unui obiect, se descriu intr-un fisier de tip sursa (cu extensia .cpp). Pentru a se preciza fiecare metoda carei clase apartine, numele metodei în cadrul blocului de descriere, trebuie prefixat cu numele clasei.
Iata un exemplu pentru aceste notiuni introductive:
class Persoana
sau:
class Lista
unde elementLista este:
typedef struct elemLista
elementLista;
Terminologia adoptata în Pascal pentru lucrul cu obiecte, difera de cea a altor limbaje orientate pe obiecte. Astfel, în C++ se utilizeaza denumirea de clasa pentru tipul obiect (dupa cum rezulta si din exemplele anterioare), iar in Pascal denumirea de obiect.
Unul din scopurile programarii orientate pe obiecte este utilizarea obiectelor ca entitati complete, de sine statatoare. Deci este recomandabil ca nici unul din câmpurile structurii proprii de date ale unui obiect, sa nu fie direct accesibil utilizatorului, orice operatie asupra sa realizându-se prin intermediul metodelor obiectului metodelor obiectului respectiv. Astfel, apare ideea încapsularii obiectelor, deoarece utilizatorul nu trebuie sa fie interesat de modul de realizare a unui obiect, ci doar de comportarea lui.
Aceasta implica posibilitatea restrictionarii accesului utilizatorului unui obiect la câmpurile de date ale acestuia. În acest sens, în cadrul definirii unui obiect, exista posibilitatea declararii identificatorilor folositi ca publici sau privati.
Domeniul de valabilitate al unui identificator declarat privat, se rezuma la modulul care contine declaratia tipului de obiect, în afara acestuia identificatorul fiind necunoscut si deci inaccesibil. Un sir de identificatori devine privat, daca este precedat de cuvântul cheie private.
Domeniul de valabilitate al unui identificator public este modulul care contine declaratia tipului de obiect, precum si programele care invoca modulul de definitie. Un sir de identificatori devine public, daca este precedat de cuvântul cheie public. Identificatorii folositi în definitia unui obiect sunt implicit publici. Zona de actiune a cuvântului cheie public se întinde pâna la primul cuvânt cheie private întâlnit. Regula este valabila si pentru zona de actiune a cuvântului cheie private.
Din cele prezentate pâna acum se poate concluziona ca membrii unui obiect, este recomandabil sa fie privati, iar pentru a se putea permite accesul celor care instantiaza obiectul la acesti membri, in zona publica trebuie sa existe metode pentru citirea (get-uri) si setarea (set-uri) acestora.
Fiecare obiect contine doua metode standard numite constructor si destructor. In C++ constructorul este metoda care are acelasi nume cu clasa respectiva. Constructorul executa operatii de initializare a membrilor obiectului, alocarea unor variabile dinamice, etc. El joaca rolul unei proceduri de initializare a lucrului cu un obiect. Un obiect poate avea mai multi constructori cu antete diferite. Un obiect poate avea mai multi constructori, diferentiati prin parametri.
Functia inversa celei a constructorului este realizata de o alta metoda speciala, numita destructor. Destructorul are acelasi nume cu clasa respectiva, care este prefixat cu "~". Prezenta destructorului nu este strict necesara în definitia unui obiect O clasa poate avea un singur destructor.
Toate notiunile discutate pana acum in acest paragraf ajuta la definirea obiectelor sau claselor. Cum se pot folosi aceste clase sau obiecte dupa ce au fost definite? Odata ce a fost definit un obiect (sau o clasa), acesta poate fi utilizat în programe declarând instante ale sale. Acestea pot fi declarate prin variabile statice sau prin variabile dinamice:
// instantiere prin variabila statica
Persoana pers1; // variabila pers1 declarata de tip Persoana
// instantiere prin variabila dinamica
Persoana* pers2 = new Persoana;
// variabila pers2 declarata ca pointer la o
variabila de tip Persoana
Spatiul de memorie ocupat de o instanta creata prin variabila statica se compune din spatiul de memorie aferent structurii proprii de date, care se rezerva în segmentul de date si spatiul de memorie aferent metodelor, care se rezerva în segmentul de cod. În cazul în care acelasi obiect are mai multe instante într-un program, codul aferent metodelor este atasat zonei de cod într-un singur exemplar.
Pentru obiectele instantiate prin variabile dinamice, deosebirea consta in faptul ca spatiul de memorie aferent structurii proprii de date se aloca "la cerere" in timpul executiei programului din zona de memorie libera (numita Heap, la PC-uri).
Un câmp din structura de date, proprie unui obiect, poate fi adresat în cadrul programului, prin numele sau prefixat de identificatorul corespunzator instantei respective. Precizam ca lucrul cu elemente din structura de date proprie unui obiect, la nivelul programului nu este recomandabil. Acest lucru este posibil tehnic, dar este în contradictie cu ideea de modularizare a activitatii de programare, si deci, cu însasi ideea de programare orientata pe obiecte. Accesul la structura de date proprie unui obiect, trebuie asigurata de metodele obiectului respectiv.
Mostenirea obiectelor
În limbajul C clasic, tipul struct definit de utilizator permite crearea unor structuri complexe, prin posibilitatea includerii în definirea unui tip, a unor tipuri anterior definite. Iata un exemplu în acest sens:
typedef struct _persoanaTag
persoana;
typedef struct _student
student;
Structura student contine în ea, pe lânga elemente proprii si structura persoana. Se poate spune ca structura student, "mosteneste" structura persoana, la care se adauga elemente proprii. O astfel de imbricare este permisa si în cadrul tipului obiect, mostenirea în acest caz referindu-se atât la structura de date cât si la metode. Deci un obiect poate contine în definirea sa, o referire la un obiect anterior, de la care vor fi mostenite atât structura de date cât si metodele. La acestea noul obiect, numit si descendent, adauga o structura proprie de date si metode proprii.
Sintactic mostenirea unui obiect se declara prin:
class <nume obiect>: public Persoana
În exemplul urmator este prezentat un obiect ce mosteneste obiectul Persoana, descris anterior:
class Student: public Persoana
Un obiect stramos este compatibil cu orice descendent al sau, dar nu si invers (compatibilitatea se refera, evident, la structura de date a obiectelor). Aceasta regula referitoare la compatibilitatea obiectelor se extinde si la variabile dinamice: o variabila referinta catre un tip obiect, poate referi o instanta de acel tip sau de un tip descendent, invers însa nu.
Metodele unui obiect sunt mostenite de obiectele descendente. Aceasta datorita faptului ca între un obiect si metodele sale, definite fara declaratii suplimentare, se stabileste o legatura fixa (statica) la compilare. Datorita acestei legaturi fixe metodele sunt denumite metode statice.
Exista si posibilitatea obtinerii unei legaturi dinamice între un obiect si metodele sale, care sa fie realizata în timpul executiei programului, prin utilizarea metodelor virtuale. Sintactic, declararea unei metode virtuale se face prin adaugarea cuvântului rezervat "virtual" in antetul metodei din definitia obiectului (vezi definirea clasei Persoana - program atasat separat). Utilizarea metodelor virtuale permite rescrierea acestora de catre fiecare descendent care doreste ca metoda respectiva sa aibe un comportament specific.
Spre deosebire de alte metode constructorii nu pot fi virtuali.
Daca o metoda este declarata virtuala într-un obiect, ea trebuie declarata virtuala în toti descendentii acestuia, pentru a putea fi rescrisa pe un anumit nivel de mostenire. Nedeclarea metodei ca virtuala pe un nivel de mostenire împiedica asocierea unei metode cu acelasi nume dar cu alt comportament pentru descendentii urmatori.
Implementarea metodelor virtuale se bazeaza pe existenta unei tabele de metode virtuale (VMT), care contine adresele metodelor declarate virtuale. Tabela este unica pentru toate instantele aceluiasi tip de obiect si este amplasata în segmentul de date al programului. Fiecare instanta, care este de tip obiect cu metode virtuale, are atasat în spatiul de memorie afectat structurii sale de date, un câmp special pentru pastrarea adresei acestui VMT. Aceasta structura contine printre altele si adresele metodelor virtuale specifice instantei respective. Crearea tabelei VMT a unui tip obiect se face de catre constructor. Executia oricarui constructor al unui obiect, în cazul ca sunt mai multi, determina crearea tabelei VMT a tipului de obiect respectiv. In acest moment se completeaza in tabela VMT adresele metodelor cu un comportament specific obiectului tocmai instantiat (adica a metodelor declarate virtuale in cadrul definirii obiectului descendent). In felul acesta se creaza acea legatura dinamica intre un obiect si unele dintre metodele sale, despre care am amintit anterior.
Polimorfism
Polimorfismul reprezinta proprietatea unor substante de a avea mai multe structuri cristaline, la momente diferite de timp, structuri care permit acestor substante sa aiba proprietati fizice si chimice diferite. Exista si polimorfism biologic (polimorfism al unei specii: exemplare apartinând aceleiasi specii care capata prin dezvoltare proprietati morfologice diferite, datorate de obicei conditiilor de dezvoltare).
În lucrul cu obiecte exista urmatoarele doua facilitati importante:
compatibilitatea obiectelor (din punctul de vedere al mostenirii acestora) se extinde si la variabile tip referinta: o variabila tip referinta catre un obiect, poate referi o instanta creata dinamic, de acel tip sau de tip descendent;
utilizarea metodelor virtuale.
Combinarea acestor doua facilitati ofera programatorului posibilitatea obtinerii unei forme foarte puternice de polimorfism în programare.
Sensul informatic ar fi urmatorul:
se porneste de la un obiect pe care îl putem considera "motiv";
acesta este dezvoltat de alte obiecte care îl mostenesc, din punct de vedere al:
structurii de date;
metodelor virtuale.
se va repeta pasul anterior obtinându-se mai multe "generatii" ale obiectului stramos "motiv";
se ajunge astfel la o diversitate de structuri de date ce pot fi manipulate cu metode identice ca nume, dar diferite ca actiuni. Dupa câteva "generatii" se poate obtine o diversitate în actiuni, pornindu-se de la o origine comuna.
Avantajul obtinut în realizarea si dezvoltarea ulterioara a programelor este deosebit. Aceasta tehnica de lucru, reprezinta tehnica cea mai moderna de analiza si programare a unui sistem informatic sau a unui pachet de programe, datorita productivitatii ridicate pe care o permite.
În cele ce urmeaza este prezentat un exemplu în acest sens. Reamintim ca obiectul Student este derivat din clasa Persoana. Exemplul mai contine o "lista" (în stânga tabloului Heap) care gestioneaza atât structurile de tip Persoana cât si structurile Student. Acest lucru este posibil prin existenta în partea de informatie utila a unui element din lista, a unui câmp de tip referinta care poate adresa ambele structuri. In acest fel, se poate ajunge la situatia in care parcurgând elementele listei si invocând pentru fiecare element executia unei metode virtuale existente in cele doua clase, se invoca de fapt folosind o unica declaratie, metode cu comportamente distincte.
|