|
Politica de confidentialitate |
|
• domnisoara hus • legume • istoria unui galban • metanol • recapitulare • profitul • caract • comentariu liric • radiolocatia • praslea cel voinic si merele da aur | |
Organizarea codului, programarea nestructurata, procedurala, modulara | ||||||
|
||||||
k5i17il Ca urmare a cresterii complexitatii aplicatiilor software, in decursul dezvoltarii informaticii s-a impus o mai buna organizare a codului sursa, astfel incit acesta sa poata fi mai usor de inteles si de intretinut. Softul industrial trebuie intotdeauna extins si/sau modificat. Dezvoltarea rapida a unui nou produs program presupune reutilizarea codului existent. Aceste cerinte nu pot fi indeplinite practic fara a dispune de o buna organizare a codului. In timp, au aparut mai multe propuneri de organizare a codului intr-un mod sistematic. Vom numi aceste moduri sistematice de organizare a codului sursa stiluri - vezi nota 1 - (sau paradigme) de programare. Distingem urmatoarele stiluri de programare (prezentate in ordinea crescatoare a gradului de organizare): Programarea nestructurata Daca un limbaj de programare permite scrierea de programe care sa respecte modul de organizare a codului specificat de catre un anumit stil, fara ca acest lucru sa implice utilizarea unor "trucuri" de programare, vom spune ca acel limbaj suporta stilul respectiv. Daca modul de organizare al codului specificat de catre un stil este respectat prin utilizarea unor "trucuri" de programare, vom spune ca limbajul permite utilizarea stilului respectiv. De exemplu, limbajul C nu suporta programarea cu tipuri abstracte de date sau programarea orientata pe obiecte, insa el permite (cu pretul scrierii unor programe mai dificil de inteles - contrar scopului adoptarii unui anumit stil) utilizarea acestor stiluri. Programarea nestructurata Programarea procedurala Programarea modulara Odata cu aparitia unitatilor de compilare, s-a pus problema identificarii de criterii bune pentru gruparea procedurilor. Deoarece reutilizarea cea mai eficienta este la nivel de cod compilat, un criteriu major de apreciere a calitatii unei impartiri in unitati de compilare il constituie minimizarea necesitatii de recompilare in cazul operarii unor modificari. In mod ideal, atunci cind o procedura trebuie modificata (fara insa a fi necesara modificarea modului de apel al procedurii), ar trebui recompilat doar fisierul care contine aceasta procedura. Deoarece fisierul care contine procedura de modificat trebuie oricum recompilat, se pot opera modificari in procedurile din acelasi fisier (fara a le modifica modul de apelare), acest fapt neconducind la cresterea costului compilarii. Prin urmare, este convenabil a grupa in acelasi fisier proceduri care se apreciaza ca vor trebui modificate impreuna in caz de necesitate. Practica programarii a demonstrat ca acest lucru poate fi realizat grupind in acelasi fisier proceduri care opereaza asupra unor date comune. Multe din schimbarile care trebuie realizate intr-un program se refera la schimbarea modului de reprezentare a unor date (aceasta schimbare poata fi impusa de exemplu de descoperirea unor algoritmi mai performanti care necesita un alt tip de reprezentare). Cresterea dimensiunii programelor a impus ca acestea sa fie scrise de echipe tot mai numeroase de programatori, fiecare programator avind propriul sau stil de programare si propriile solutii de implementare. Inevitabil, fiecare programator utilizeaza o anumita structura de date facind anumite presupuneri asupra modului de utilizare a acelei structuri. Din pacate, presupunerile sale pot sa nu fie corelate cu cele ale unui alt programator care lucreaza la acelasi program. De exemplu, sa presupunem ca un programator implementeaza o structura de date care contine codul de judet format din doua litere (cel care apare pe numerele de inmatriculare ale autovehiculelor): struct NrInmatriculare A char codJudeta2i; char *codLitere; S nrInmatriculare; Programatorul a facut presupunerea ca nimeni nu va utiliza cimpul codJudet in functii care manipuleaza siruri de caractere terminate prin caracterul nul (cod ASCII 0). Daca un alt programator va utiliza insa urmatoarea instructiune: strcpy(nrInmatriculare.codJudet,"IS"); rezultatul va fi coruperea pointerului nrInmatriculare.codLitere, cu efecte potential dezastruoase asupra programului (coruperile de memorie fiind printre cele mai greu de depanat erori). Exemplul de mai sus evidentiaza necesitatea ascunderii reprezentarii structurilor de date (data hiding), principiu care sta la baza implementarilor robuste. Conform acestui principiu, reprezentarea unei structuri de date trebuie sa nu fie accesibila decit procedurilor care in mod necesar trebuie sa lucreaze direct cu acea structura (de exemplu procedurile care creaza, modifica si distrug structura respectiva). Principiul ascunderii datelor, in conjunctie cu modul ideal de structurare a programelor in unitati de compilare a dus la aparitia notiunii de modul program. Un modul program este un fisier sursa care contine date si proceduri destinate manipularii nemijlocite a acelor date. Structurile de reprezentare a datelor trebuie sa fie accesibile numai procedurilor care fac parte din acel modul. Procedurile trebuie sa ofere un set complet de operatii care pot fi realizate asupra structurilor de date. In acest mod se va asigura faptul ca vor fi realizate numai operatii permise asupra datelor. Organizarea programelor in programarea modulara se face in module. Desi acestea pot fi determinate de tipurile de date utilizate in program, acest lucru nu este in mod necesar preponderent. Pentru a comunica intre ele, modulele definesc interfete. In cadrul interfetelor sunt declarate datele si procedurile care sunt accesibile din exteriorul modulului. Trebuie sa facem diferentierea intre unitati de compilare si module. O unitate de compilare este un fisier sursa. Un modul este o constructie sintactica a unui limbaj de programare care este capabila sa ascunda reprezentarea datelor si sa defineasca interfete. Este posibil ca o unitate de compilare sa contina mai multe module. Limbajele Modula si Ada suporta in mod direct modulele. Nu exista o implementare directa a modulelor in C si nici in C++. Limbajul C doar permite utilizarea modulelor (prin intermediul unitatilor de compilare si a fisierelor antet). Limbajul C++ permite utilizarea modulelor (fiind o extensie a limbajului C) si ofera un mecanism superior de ascundere a datelor si definire a interfetelor prin intermediul claselor. Vom exemplifica mai jos modul de implementare a modulelor in C in urmatorul exemplu. Fisierul inmatr.h va juca rolul interfetei unui modul care implementeaza structura de date (si operatiile asupra acestei structuri). . /* inmatr.h - defineste interfata inmatriculare */ char* citesteCodJudet(); void scrieCodJudet(char*); Fisierul inmatr.c realizeaza implementarea interfetei: /* inmatr.c - implementarea interfetei definite in inmatr.h */ struct NrInmatriculare A char codJudeta2i; char *codLitere; S nrInmatriculare;
void scrieCodJudet(char* cod) A nrInmatriculare.codJudeta0i = coda0i; nrInmatriculare.codJudeta1i = coda1i; S ... Celorlalti programatori li se va pune la dispozitie numai fisierul antet inmatr.h si codul obiect rezultat in urma compilarii fisierului inmatr.h, fara a avea acces la fisierul inmatr.c. Astfel, structura NrInmatriculare si variabila nrInmatriculare nu sunt cunoscute in exterior. Se previne astfel atit utilizarea eronata a acestei date, cit si necesitatea recompilarii altor fisiere in cazul modificarii structurii de date si/sau functiilor din fisierul inmatr.c: /* main.c */ #include "inmatr.h" /* acces la interfata */ main() A scrieCodJudet("IS"); S Cum este asigurata insa ascunderea reprezentarii daca este necesar sa se utilizeze in program mai multe variabile de tip NrInmatriculare? Aici intervine acel "truc de programare" despre care aminteam la inceputul acestui capitol. Fiecare variabila de tip NrInmatriculare va fi accesata prin intermediul unui pointer. Implementarea va trebui sa asigure crearea si distrugerea dinamica a unei structuri de tip NrInmatriculare. Prezentam mai jos modificarile care sunt necesare: /* inmatr.h */ struct NrInmatriculare; /* Declara dar nu defineste structura */ typedef struct NrInmatriculare* PNrInmatriculasre; PNrInmatriculare creaza_nrInmatriculare(char*, char*); distruge_nrInmatriculare(PNrInmatriculare); char* citesteCodJudet(PNrInmatriculare); void scrieCodJudet(PNrInmatriculare,char*); ... /* main.c */ #include "inmatr.h" main() A PNrInmatriculare nr1, nr2; nr1 = creaza_nrInmatriculare("IS","ABCD"); nr2 = creaza_nrInmatriculare("","GHIJ"); scrieCodJudet(nr2,citesteCodJudet(nr1)); ... distruge_nrInmatriculare(nr1); distruge_nrInmatriculare(nr2); S Implementarea este lasata ca exercitiu cititorului. Programarea cu tipuri abstracte de date Tipurile abstracte de date inlocuiesc conceptul de ascundere a reprezentarii datelor cu cel de incapsulare a datelor. In incapsularea datelor, nu mai este esential ca structura datelor sa fie ascunsa, deoarece ea este protejata. Metafora capsulei este destul de reusita, in special daca ne imaginam capsula ca fiind transparenta (insa deosebit de rezistenta). Putem contrasta cele doua principii facind urmatoarea paralela: Putem transmite codul unui caracter de la tastatura unui calculator fara a face apel la tastatura, doar aplicind curenti avind diverse caractertistici in anumite contacte ale circuitelor calculatorului. Nimeni nu garanteaza insa ca ceea ce vom transmite va fi chiar caracterul dorit sau daca nu vom strica calculatorul. Pentru a impiedica ca acest lucru sa se intimple, principiul ascunderii reprezentarii datelor, transporta un calculator fara carcasa intr-o alta incapere. Incapsularea datelor ne lasa calculatorul pe birou, avind insa grija ca acesta sa dispuna de o carcasa (poate fi si transparenta) nedemontabila. Exista insa situatii (de exemplu atunci cind se doreste evitarea operatiilor de recompilare) in care incapsularea trebuie dublata de ascunderea reprezentarii datelor. In programarea orientata pe obiecte, incapsularea poate fi utilizata pentru a ascunde reprezentarea datelor -vezi nota 3. Cel mai cunoscut limbaj care suporta tipuri abstracte de date (insa nu suporta programarea orientata pe obiecte) este Ada. Toate limbajele de programare orientate pe obiecte suporta tipurile abstracte de date. Vom exemplifica utilizarea tipurilor abstracte de date in C++: // inmatr.h class NrInmatriculare A protected: char codJudeta2i; char* codNumeric; public: NrInmatriculare(char*, char*); // constructor char citesteNrInmatriculare(); void scrieNrInmatriculare(char*); ... S; Desi reprezentarea tipului NrInmatriculare nu mai este ascunsa, ea nu este accesibila in exterior: // main.c #include "inmatr.h" main() A NrInmatriculare nr1("IS","ABCD"); nr1.scrieNrInmatriculare("BC"); // corect strcpy(nr1.codJudet,"BT"); // eroare compilare ... S In programarea cu tipuri abstracte de date, programul este organizat in functie de tipurile de date utilizate. Neajunsurile programarii cu tipuri abstracte de date enum TIP A triunghi, cerc, patrat S; class Culoare A ... S; class Punct A ... S; class Figura A protected: TIP tip; Culoare culoare; Punct punct;.... public: void deseneaza(); S; void Figura::deseneaza() A switch(tip) A case cerc: // deseneaza cerc ... break; case triunghi: // deseneaza triunghi ... break; case patrat: // deseneaza patrat ... S S Functia deseneaza() trebuie sa stie totul despre afisarea fiecarui tip de figura. Aceasta functie va trebui modificata, de fiecare data cind programul este extins pentru a mai desena un tip de figura. Programarea orientata pe obiecte In programarea orientata pe obiecte este posibil de a se realiza apeluri ale unor functii care nu sunt complet identificate in momentul compilarii: class Forma A protected: Culoare culoare; Punct centru; ... public: virtual void deseneaza(); ... S; class Cerc : public Figura A protected: double raza; ... public: void draw(); ... S; ... void deseneaza_toate_figurile(Figura* figuriai, int nrFiguri) A for(int i=0; i<nrFiguri; ++i) figuriaii->deseneaza(); S In momentul compilarii, nu se cunoaste care din functiile deseneaza va fi apelata pentru un indice i dat. Organizarea programelor in programarea orientata pe obiecte se face in jurul obiectelor organiyate in clase -vezi nota 4. Programarea orientata pe obiecte asigura incapsularea asemenea cu programarea cu tipuri abstracte de date, oferind in plus o flexibilitate sporita si o mai buna reutilizare a codului, asigurate de mecanismul de derivare. Mai multe amanunte despre clase si derivarea de clase, in capitolele urmatoare. Note: Chiar daca unele limbaje de programare, precum limbajele de programare logica nu ofera explicit modalitati de exprimare a unei proceduri, se poate identifica un concept echivalent (definitia unui predicat). Exista autori care nu fac distinctie intre incapsulare si ascunderea reprezentarii
datelor, in principal datorita faptului ca incapsularea, dublata de posibilitatea
de a defini clase abstracte (in programarea orientata pe obiecte) asigura pe
deplin ascunderea reprezentarii datelor. |
||||||
|
||||||
|
||||||
Copyright© 2005 - 2024 | Trimite document | Harta site | Adauga in favorite |
|