Documente Academic
Documente Profesional
Documente Cultură
4 7 7 10 15 17 20 23 23 26 31 36 41 41 45 47 58 61 65 67 67 69 79 83 85
89 89 94 97 102 106 106 110 113 118 122 122 125 128 129 129 132 134 140 140 141 149 150 153 156 158
9. Concluzii ...........................................................................................................
Anexa 1 Lista de variabile .......................................................................................................
Bibliografie ..........................................................................................................
Introducere
Obiectivul lucrrii este prezentarea tehnicilor de programare cu avantajele i dezavantajele lor, precum i realizarea unei analize comparate a complexitii software pentru entitile text generate cu ajutorul acestor tehnici, folosind aceeai problem spre exemplificare. Codul surs al programelor este considerat entitate text pentru c are n alctuire cuvintele unui vocabular i respect regulile de construire a elementelor complexe de limbaj asociate acestuia. Necesitatea lucrrii este dat de evidenierea progresului nregistrat de trecerea de la o tehnic veche de programare la una nou, n raport cu criteriul creterii performanei produselor software, dar i cu cel al creterii complexitii software. Mijloacele ntrebuinate sunt diferite ntruct fiecare dintre tehnicile de programare au promovat un anumit limbaj care a evideniat n primul rnd avantajele generate. Limbaje precum FORTRAN sau ALGOL sunt deja istorie, iar limbajul COBOL mai este prezent doar prin intermediul numeroaselor sisteme informatice dezvoltate cu el. Limbajul C i evoluia sa C++ sunt folosite n continuare foarte intens, n special ca limbaje de dezvoltare pentru sisteme de operare, ns limbaje precum Java sau C# reprezint la acest moment principalele instrumente de dezvoltare a produselor software. Limbajele de programare ofer resurse diferite, dac prin resurse se nelege: tipuri de date, cuvinte cheie asociate instruciunilor, implementarea recursivitii, blocurile, expresiile compuse i bibliotecile de funcii. Capitolele prezint caracteristicile tehnicilor de programare, ipotezele de lucru i efectele acestora asupra structurii produsului finit programul pe care l construiesc. Se fac referiri asupra capacitii tehnicilor de programare de a dezvolta lucrul n echip, de a asigura un nivel calitativ ridicat produsului software i de a permite transmiterea experienei de la un proces la altul prin reutilizarea de componente. Dezvoltarea tehnicilor de programare este dinamic. Pentru a identifica trendul evoluiei tehnicilor de programare se studiaz influena acestora asupra complexitii software. La finalul fiecrui capitol dedicat
4
Tehnici de programare
unei tehnici de programare, se analizeaz complexitatea software a codului surs generat cu ajutorul tehnicii respective pentru rezolvarea problemei alese. n capitolul n care se prezint Ciclul de dezvoltare software sunt descrise etapele, sarcinile, intrrile i ieirile acestora, aa cum decurg din standardele ingineriei software actuale. De asemenea, se prezint conceptul de complexitate software i se definesc instrumentele de msurare ce sunt folosite n cadrul acestei lucrri. Tehnica de programare clasic este prezentat ntr-un capitol distinct, n care se accentueaz caracterul artizanal, individual al muncii de programare. Arta programrii joac un rol important, ntruct fiecare program este vzut ca un produs unicat, produs ce include foarte mult munc intelectual, inspiraie i, desigur, inteligen. Programarea standard este o tehnic de programare care pentru prima dat ia n considerare necesitatea constituirii de biblioteci de subprograme, dup nite reguli foarte precise. Orice aplicaie apare, n viziunea programrii standard, sub forma exclusiv a apelului de subprograme. Multe dintre ideile de finalizare specifice programrii standard se regsesc n programarea orientat obiect. n capitolul Abordarea structurat se evit teoretizarea legat de o baz formal care impune i dezvolt aceast tehnic. Teorema de structur, ipotezele privind transformarea programelor sunt numai abordri de care practica programrii nu a inut seama n nici un fel. n acest capitol se prezint structurile fundamentale, cerinele pe care un program trebuie s le ndeplineasc pentru a fi considerat program structurat. Sunt prezentate, de asemenea, avantajele privind omogenitatea textelor surs, capacitatea de a dezvolta aplicaii structurate i extinderea pe care au luat-o limbajele de programare n care instruciunea GOTO nu mai trebuie folosit. Revoluia n programare prin introducerea de clase, obiecte, proprieti i dezvoltarea tehnicilor de analiz i proiectare bazate pe aceste concepte este abordat n capitolul Programarea orientat obiect. Se descriu structurile claselor, proprietile de ncapsulare, motenire i polimorfism precum i modul n care acestea influeneaz filosofia ntregii activiti de programare. Programarea orientat obiect reprezint altceva. Continuarea pstrrii vechilor mentaliti din programare devine imposibil. Saltul realizat de programatori este vizibil, iar rezultatele sunt dintre cele mai spectaculoase.
Introducere
Utilizarea de componente reprezint o extindere fireasc a programrii orientate obiect. Se pune problema reutilizrii funcionalitii i nu neaprat a codului care o implementeaz. Sunt prezentate tehnici de definire a componentelor, utilizarea acestora n cadrul aplicaiilor eterogene din punct de vedere al limbajelor de programare i/sau tehnologiilor folosite la dezvoltarea lor, importana componentelor n implementarea interoperabilitii aplicaiilor. Lucrarea se ncheie cu concluzii n care se evideniaz necesitatea sintetizrii experienelor pozitive n vederea trecerii la noi tehnici de programare. Exemplificrile se fac numai n limbajul C++. Se definete problema PROB care este prezentat n capitolul Ciclul de dezvoltare software. n celelalte capitole sunt date soluii corespunztoare fiecrei tehnici de programare analizate pentru problema PROB. S-a adoptat aceast modalitate de lucru pentru a putea fi comparate efectele utilizrii fiecrei tehnici de programare n cazul implementrii aceluiai algoritm. S-a ales o problem cunoscut pentru a facilita urmrirea pailor algoritmului, simultan cu structurile de program pe care fiecare tehnic le genereaz. Analiza comparat se realizeaz prin soluionarea aceleai probleme, pentru a surprinde mai bine, diferenele de abordare pentru fiecare dintre tehnicile de programare. Pentru fiecare soluie se fac comentarii privind lungimea codului surs i complexitatea programelor. Autorii aduc mulumirile lor membrilor Catedrei de Informatic Economic din Academia de Studii Economice i, n special, colectivelor de programarea calculatoarelor ale cror lucrri au stat la baza cercetrilor ntreprinse i la obinerea rezultatelor incluse n capitolele destinate celor mai noi tehnici de programare aflate acum n atenia dezvoltrii de aplicaii informatice moderne. Lucrarea este elaborat n cadrul contractului de cercetare INFOSOC Sistem de evaluare a entitilor bazate pe text, nr.148/29.09.2004. Autorii mulumesc tuturor celor care, prin contribuii i sugestii, vor mbunti ediia viitoare a lucrrii de fa, lucrare care se adreseaz tuturor celor care dezvolt programe, pentru a gsi noi resurse i pentru a se perfeciona continuu.
Orice limbaj are la baz un alfabet i un vocabular format din cuvinte cheie i cuvinte construite de utilizatori. Limbajele de programare au nregistrat evoluii spectaculoase, trecnd prin stadii care au impus modificri sau chiar dispariia. Aa s-a ntmplat cu limbajul FORTRAN, care a devenit n evoluia sa FORTRAN-4, FORTRAN-77, FORTRAN-90, iar variantele care au urmat au avut o asimilare limitat, limbajul fiind cu o rspndire extrem de redus. n schimb limbajul C++ prin capacitatea sa de a asimila construcii cu grad de complexitate foarte variat, s-a impus i s-a dezvoltat. Orientarea spre programarea vizual are o larg rspndire prin sugestivitatea abordrii n raport cu problema de rezolvat i operaiile mecanice pe care le presupune implementarea unui algoritm. Limbajele de nivel sczut presupun instruciuni pentru operaiile elementare. Programatorul trebuie s gestioneze descompunerea expresiilor pentru evaluarea din aproape n aproape. Limbajele de nivel ridicat includ construcii complexe, compuneri de expresii i prin atribuirea de calificative se obin efecte speciale n ceea ce privete referirile de operanzi i operatori. Sunt implementate structuri dintre cele mai variate, iar mecanismele de definire de operanzi i operatori au corespondent mecanisme de referire care genereaz efecte ce asigur generalitate i eficien tuturor construciilor; numai aa se explic creterea complexitii i diversitii aplicaiilor informatice de azi. Exemplificarea limbajelor de programare este realizat folosind numai limbajul C++. Elementele care definesc un limbaj sunt: alfabetul limbajului, vocabularul, operatorii i precedenele lor, identificatorii, declaraiile de funcii i variabile. Vocabularul limbajului C++ include cuvintele cheie: asm catch continue dynamic_cast false if namespace public signed switch try unsigned wchar_t auto char default else float inline new register sizeof template typedef using while bool class delete enum for int operator reinterpret_cast static this typeid virtual break const do explicit friend long private return static_cast throw typename void case const_cast double extern goto mutable protected short struct true union volatile
8
Programarea calculatoarelor
Lista de operatori i prioritile sunt date n tabelul 1.1: Operatorii identificai n limbajul C++ Tabelul 1.1
Preceden Operatori 1 () [] :: ~ 2 ! - (operator unar) * (dereferire) & (adresa) Sizeof 3 ->* .* 4 * (nmulire) / 5 + 6 << >> 7 < <= 8 == != 9 & ( AND pe bii) 10 ^ 11 | 12 && 13 || 14 ?: 15 = += 16 , -> ++ . --
% > >=
-=
etc.
Identificatorul este numele dat de programator variabilelor, funciilor, etichetelor i a altor obiecte definite de el. Mecanismul de construire al acestuia este acelai n toate limbajele de programare importante: primul caracter este o liter sau un caracter de subliniere, iar caracterele care urmeaz sunt litere, cifre sau caractere de subliniere. Cuvintele cheie asociate limbajului nu sunt acceptate ca identificatori.
9
Exemple de identificatori valizi sunt: _matrice, mat1234, arb43bin, for123. Exemple de identificatori invalizi sunt: 12matrice, for, etc. Prototipul de funcie n C++ este urmtorul:
tip_rezutat nume_funcie ( list_parametrii ) { corp_funcie }
Exist numeroase lucrri, [IVANI98], [SMEU01], [SMEU02], [STROU87], care detaliaz limbajul C++ evideniind caracteristicile sale cele mai puternice, studiul acestui limbaj nefiind obiectivul acestei lucrri.
Operatorii arat modul n care sunt transformai operanzii, pentru a deveni din date iniiale, date prelucrate, adic rezultate care s determine aciuni sau s permit luarea unor decizii. Orice problem P are asociat un algoritm cruia i corespunde structura arborescent cu dou niveluri dat n figura 1.1.
10
Programarea calculatoarelor
Iniializarea se realizeaz prin: citire date din fiiere sau baze de date; introducere date de la tastatur; evaluarea expresiilor de atribuire. afiare rezultate pe ecranul monitorului; scrierea la imprimant; creare sau actualizare de fiiere, respectiv actualizare de baze de date.
Implementarea unui algoritm include instruciuni pentru una sau mai multe combinaii de iniializare, respectiv gestionare a rezultatelor. Exist numeroi algoritmi dai n literatura de specialitate i n crile de baz ale programrii. Sunt algoritmi verificai, adui la o form rafinat, programatorul trebuind numai s-i preia. Sunt i situaii n care, programatorul trebuie s-i construiasc algoritmi proprii. n toate cazurile, trebuie avute n vedere: generalitatea pe care trebuie s o asigure, nefiind interesant acel algoritm care soluioneaz o problem concret, ci acela care soluioneaz problemele aparinnd unei clase; corectitudinea, n sensul c pentru orice problem aparinnd clasei de probleme pentru care a fost construit algoritmul, trebuie s furnizeze rezultate corecte; reproductibilitatea, n ideea c repetnd prelucrrile pentru aceleai date, se vor obine aceleai rezultate, prelucrrile avnd caracter determinist; finitudine, n sensul c volumul de prelucrri trebuie limitat la o dimensiune acceptabil n raport cu obiectivul urmrit.
La implementarea unui algoritm se procedeaz mai nti la elaborarea unei scheme logice. Cu acest prilej, se analizeaz gradul de nelegere a problemei. Testul de birou folosind date de control are menirea de a da un plus de siguran soluionrii problemei prin metoda folosit. Pentru implementarea algoritmului sunt alese niveluri de detaliere. Este important ca indiferent de procedeul definit i utilizat, programul sa-i
11
ating obiectivul pentru care a fost elaborat. De exemplu, se dorete implementarea algoritmului de sortare prin interschimbare cu contorizarea interschimbrilor. Se consider irul:
irul devine:
irul devine:
12
Programarea calculatoarelor
irul devine:
irul devine:
13
Algoritmul este finit pentru c n momentul n care nu mai exist interschimbri, nseamn ca irul este sortat. Pentru implementare trebuie rspuns la ntrebrile: ct de lungi sunt irurile de termeni care se sorteaz; dac sortarea este nsoit i de interschimbarea altor cmpuri.
Funcie de rspunsurile date se procedeaz la definirea unui algoritm de sortare n fiiere sau la construirea unui algoritm de sortare exclusiv n memorie. Implementarea algoritmilor depinde de: experiena programatorilor; resursele hardware i software disponibile; resursele financiare care stau la baza demersului; timpul n care trebuie rezolvat problema.
n mod normal, trebuie s se porneasc de la problema de rezolvat. Aceasta impune cum se stocheaz datele iniiale, cum se gestioneaz rezultatele intermediare i cum sunt memorate rezultatele finale. Implementarea algoritmilor necesit efectuarea de calcule privind necesarul de memorie pentru a stoca rezultatele intermediare i finale, precum i datele iniiale n timpul prelucrrii. Sunt preferai algoritmii care nu necesit zone importante de memorie pentru rezultate intermediare. Pentru calculul mediei aritmetice care utilizeaz ct mai puin memorie, programul ce implementeaz algoritmul este urmtorul:
#include <stdio.h> int main() { int i, n, s, x; float xm; s = 0; printf("Introduce numarul scanf("%d", &n); for ( i = 0; i < n; i++ ) printf( "Introduce scanf("%d", &x); s += x; } xm = (float)s/n; printf( "Media aritmetica }
este %f", xm );
14
Programarea calculatoarelor
Se constat c pentru a reutiliza datele pentru alte calcule, acest deziderat este imposibil de realizat. Pentru a reutiliza datele, este necesar ca ele s se salveze n memorie, n structuri de date corespunztoare, i apoi s intre n prelucrarea efectiv. Algoritmul este un aspect, programul este o concretizare, una din concretizri, totul depinznd de programator.
Di = LA j + D0
j =1
i 1
15
Pentru a crete viteza de identificare a unui articol, fiierul F este mprit n r pri, figura 1.2.
Dac se efectueaz traversarea fiierului F pentru a gsi articolul cu cheia kx, sunt necesare suficient de multe operaii de citire. Dac se construiete irul de r perechi (k1`, p1`), (k2`, p2`)..(kr`, pr`) se traverseaz mai nti irul de perechi, se obine selectarea zonei din fiier unde se afl articolul cu cheia kx dup care se procedeaz la traversarea secvenial a zonei, articol cu articol, pn la identificarea celui cutat. Este important ca de la nceput articolele s fie dispuse n zonele fiierului F cu condiia: k1 < k2 < .. < kn Subirului de perechi i se asociaz un alt subir, cutarea secvenial efectundu-se pe dou niveluri. Raionamentul se dezvolt, fiind necesar un echilibru ntre numrul nivelurilor de indexare i efortul de traversare secvenial n cadrul fiecrui nivel. Dac se adaug atribute, se construiesc tripletele care se asociaz articolelor fiierului (k1,p1,a1), (k2,p2,a2), ...., (kn,pn,an). Operaiile de intersecie, reuniune de iruri de triplete conduc la extragerea din fiier a articolelor ale cror cmpuri au valori ce corespund unor combinaii de atribute. Viteza de derulare a tranzaciilor depinde strict de ntreaga filosofie de a organiza procesele de referire ale articolelor din zona de date a fiierului. Lucrul cu indexuri, cu deplasri i mai ales structurile arborescente asociate perechilor de cutare influeneaz duratele tranzaciilor.
16
Programarea calculatoarelor
1.4 Subprogramele
Acum se concepe dezvoltarea unei aplicaii numai folosind subprograme. Un subprogram este o entitate de sine stttoare, care concentreaz o prelucrare cu caracter repetitiv. Nu se scriu subprograme pentru prelucrri care nu vor mai fi realizate i cu alte prilejuri. Subprogramul are un nume sugestiv, uor de manevrat, fr a fi definite mai multe subprograme diferite ca prelucrare cu nume avnd coeficient de similitudine peste un prag considerat limit superioar. Spre exemplificare, se consider urmtoarea list de subprograme:
read_file() subprogram care citete un articol dintr-un fiier; update_file() subprogram pentru actualizarea unui fiier; write_file() subprogram pentru scrierea unui articol ntr-un fiier; addmat() subprogram care adun dou matrice; prodmat() subprogram care nmulete dou matrice; copymat() subprogram pentru copierea unei matrice ntr-o alt matrice; initmat() subprogram care iniializeaz o matrice cu o constant; sort_file() subprogram pentru sortarea fiierelor; sort_vect() subprogram pentru sortarea elementelor unui vector; concat_vect() subprogram care concateneaz doi vectori; concat_lists() subprogram care concateneaz dou liste simple; concat_listd() subprogram pentru concatenarea dou liste duble; concat_nlists() subprogram pentru concatenarea a n liste simple; concat_nlistd() subprogram care concateneaz n liste duble; concat_nvect() subprogram care concateneaz n vectori.
17
liste de parametri formali n care se regsesc operanzii care fac obiectul prelucrrii, operanzii ce conin rezultatele i variabile ce conin informaii privind modul n care s-au efectuat prelucrrile; listele fixe de parametri au aceeai structur i lungime, indiferent de numrul apelurilor; listele variabile au att tipuri diferite de parametrii, ct i numr variabil;
instruciuni care asigur revenirea n programul apelator, return; este preferabil s se construiasc subprograme care conin o singur instruciune return, folosind fie compuneri de expresii, fie o variabil de control utilizat ulterior; instruciuni repetitive pentru a asigura traversarea structurilor de date omogene (masiv, list, arbore, fiier), dac pentru evaluarea de expresii acest lucru este necesar; instruciuni de iniializare pentru variabile care i modific coninutul n vederea returnrii de rezultate care vor fi utilizate n programul apelator; programul apelator este i el tot un subprogram; int main() este, de asemenea, un subprogram, aa cum ntr-un arbore binar, rdcina este tot un nod; un tip de rezultat care se returneaz; n cazul n care lista rezultatelor returnate este vid, tipul subprogramului este void.
Este de dorit ca subprogramele s fie nzestrate cu un grad nalt de generalitate pentru a permite refolosirea lor ori de cte ori e nevoie. Testarea sistematic a subprogramelor le d un nivel de corectitudine care genereaz ncrederea utilizatorilor n a le ncorpora n programele proprii. Exist mai muli termeni care desemneaz acelai lucru: proceduri, subprograme, funcii; sunt concepte prin care, de regul, se nelege o construcie independent n raport cu un context, care se refer i care permite efectuarea de prelucrri ale cror efecte sunt utilizate ulterior.
18
Programarea calculatoarelor
se scriu mai multe forme de subprograme, care difer dup cum sunt transmii parametrii. n textul surs:
int suma ( int x[], int n ) { int s, i; for ( s = 0, i = 0; i < n ; i++ ) { s += x[i]; } return s; }
rezultatul se regsete n zona de memorie a crei adres se afl n variabila pointer ps. Faptul c lista de parametri include o mare diversitate de tipuri de date, inclusiv pointeri spre funcii, subprogramele devin cu grad de generalitate foarte mare. n loc s se scrie, de exemplu, dou subprograme pentru a gsi elementul minim, respectiv elementul maxim dintr-un ir, se va scrie un singur subprogram ce are n lista de parametri o funcie ce returneaz evaluri diferite de expresii condiionale, care vor determina aciuni diferite n cadrul subprogramului. Subprogramele trebuie s fie generale att prin dimensiunea problemelor de rezolvat, ct mai ales prin cuprinderea a ct mai multor cazuri care definesc clasa de probleme rezolvat. n programarea orientat obiect problematica tipului de rezultate returnate i a tipurilor de variabile din lista de parametrii se soluioneaz prin construcii de tip template. Limbajele de programare dispun de numeroase modaliti de transmitere a parametrilor, dintre care transmiterea prin valoare,
19
transmiterea prin adrese i transmiterea prin referin, sunt cele mai frecvent ntlnite. Este rezonabil cunoaterea n detaliu a acestor mecanisme pentru a nelege efectele. Dac n cazul transmiterii prin valoare operarea pe variabilele asociate parametrilor subprogramului este local n raport cu domeniul definit de subprogram, n celelalte cazuri aceste operaii conduc la modificarea coninutului zonelor de memorie asociate parametrilor formali, la nivelul subprogramului apelator. Subprogramele nu conin operaii de intrare/ieire, pentru a li se asigura un nalt grad de generalitate i mentenabilitate. Ele primesc datele de intrare prin intermediul listei de parametrii i ofer rezultate fie prin parametrii din list, fie prin valoarea returnat de subprogram, fie prin amndou modalitile.
e2
GO TO e1
Salturile nainte revin situaiilor n care etichetele e2 i e3 ale instruciunilor spre care se efectueaz saltul urmeaz instruciunilor GOTO, figura 1.5.
20
Programarea calculatoarelor
Dac se asociaz un graf unui program i se include ntr-o matrice instruciunile precedente i instruciunile urmtoare, n cazul unei structuri liniare, figura 1.6, matricea include asteriscuri numai deasupra diagonalei principale:
n cazul includerii n program a instruciunilor de salt necondiionat, matricea precedenelor devine mult mai complex. Pentru programul al crui graf este dat n figura 1.8, rezult matricea de preceden, din figura 1.9.
21
I1 I1 I2 I3 I4 I5 I6 I7
I2
I3
I4
I5
I6 I7
Programarea modular exclude apariia instruciunii GOTO, dei la nivelurile de programare n limbaj de asamblare este evident c nici o structur de control nu se implementeaz fr a folosi aceast instruciune. Sunt situaii n care optimizarea programelor impune o alt abordare care s elimine salturile necondiionate ntre componente aflate n segmente diferite. Programarea calculatoarelor se definete ca meserie ce folosete resurse, iar eficiena sa este cu att mai mare cu ct procesul de alocare i nivelare a acestor resurse este mai aproape de nivelul optim. Limbajele, subprogramele, instruciunile, tehnicile de programare sunt resurse, nivelarea i alocarea lor o efectueaz programatorul.
22
nalt. n cazul aplicaiilor care opereaz cu seturi foarte mari de date, programatorii acumuleaz experien n a lucra cu sisteme complexe de gestiune a bazelor de date. n contextul creterii complexitii aplicaiilor informatice s-au cristalizat urmtoarele direcii: activitatea de programare este o activitate care se deruleaz n cadrul unei echipe, n care fiecare membru are definite inputurile, respectiv outputurile, foarte clar; s-au adncit specializrile, nct rolul analistului de sistem, rolul designerului, rolul programatorului i rolul celor care se ocup de testare, implementare i mentenan a crescut foarte mult; deja exist profesiuni foarte bine conturate, iar managerul de proiecte IT este deja o profesie necesar i, mai ales, recunoscut n societatea informaional; necesitatea utilizrii de instrumente tehnice vzut ca necesitate a nzestrrii posturilor de munc, fie c e vorba de analiti, de designeri, de programatori, de testeri de software i de personal care administreaz aplicaia.
Activitatea de programare este privit, uneori, ca activitate pur de creaie, aspect exagerat, cum tot exagerat este a-l privi pe programator ca pe un simplu codificator. Programatorul trebuie s se instruiasc continuu, forma concret programul a ntregii activiti dintr-o companie de software este rezultatul muncii sale, chiar dac n spate se afl eforturile concertate i ale analitilor i designerilor. Apar numeroase ntrebri referitoare la: numrul limbajelor de programare pe care trebuie s le cunoasc un programator; dac programatorul stpnete foarte bine un limbaj de programare, are experien n a folosi facilitile respectivului limbaj, posed cunotine de algoritmi i tehnici de programare i dac stpnete elementele de baz ale unui sistem de operare, va dezvolta produse de bun calitate; atunci cnd un alt limbaj de programare devine dominant, se impune ca programatorul s treac la elaborarea de software folosind resursele noului limbaj dominant; nseamn c activitatea de programare presupune un continuu efort de instruire, dar, mai ales, autoinstruire;
24
traiectoria pe care o urmeaz un programator; dac acesta e o persoan perseverent adncete cunotinele dintr-un domeniu sau extinde aria de cuprindere spre alte limbaje sau tehnici de programare; o traiectorie normal este aceea n care programatorul se ndreapt spre activitatea de proiectare, iar dup ce capt o mai mare experien se orienteaz spre analiza de sistem i spre a deveni manager de proiect; caracterul industrial al activitii de programare; conceptul de fabric de software este privit ca o translatare forat a unor laturi cu caracter repetat, reproductibil i responsabilizat din producia industrial spre o zon unde imaterialitatea produsului imprim particulariti mai ales legate de absena uzurii fizice i de costul nul al operaiei de copiere; necesitatea de a compara produsele software prin definirea de metrici ale calitii, prin utilizarea unor coeficieni de transformare; apariia pieii de software ca principal prghie de reglare a tendinelor centrifuge din domeniul produciei de aplicaii informatice cu grad de generalitate deosebit de ridicat.
Activitatea de programare a calculatoarelor presupune caliti remarcabile din partea celor care doresc s profeseze meseria de programator. Piaa de joburi n evoluia sa a impus meserii deosebit de diverse, cum sunt: programator, dezvoltator, inginer software, inginer de sistem etc. Pentru analizarea avantajelor i dezavantajelor tehnicilor de programare prezentate n aceast carte, a fost aleas o problem comun, care este dezvoltat folosind tehnicile prezentate. Se presupune o matrice cu m linii i n coloane, m i n numere impare. Pentru aceasta se determin valorile maxime i minime, se testeaz simetria matricei, iar dac matricea este simetric, atunci se numr elementele pozitive, negative i nule ale acesteia. Dimensiunile i elementele matricei sunt informaii ce vor fi citite de la tastatur, n programele de test.
25
are un numr de zece linii surs. n cazul n care un alt programator rescrie acelai program sub forma:
int main () { int a, b, c, d, e, f; a = b = c = 7; d = 89; e = -1; if ( e + d * a ) f = e + a; else f = e - a;}
se obine un program cu doar ase linii surs, ns ambele au acelai numr de instruciuni. Se impune stabilirea unor reguli foarte precise pentru dispunerea instruciunilor i separatorilor de blocuri pentru a se obine construcii comparabile ca efort de realizare i claritate a codului. n graful asociat unui program, fiecrei instruciuni i se asociaz un nod, legarea acesteia de instruciunea ce urmeaz a fi executat realiznduse cu ajutorul unui arc orientat.
26
Textul surs al programului pentru alegerea minimului dintre trei elemente este urmtorul:
int main () { int a, b, c, min; printf( "Introduceti a = " ); scanf("%d", &a); printf( "Introduceti b = " ); scanf("%d", &b); printf( "Introduceti c = " ); scanf("%d", &c); if ( a > b ) { if ( b > c min } else { min } } else { if ( a > c min } else { min } } I1 I2 I3 I4 I5 I6 I7 I8 I9 I10 I11 I12 I13 I14 I15 I16 I17
) { = c; = b;
) { = c; = a;
I18
27
Secvena de program destinat numrrii elementelor pozitive, negative i nule ale unei matrice este urmtoarea:
int splus = 0; int snegativ = 0; int snul = 0; for ( i = 0;i < m; i++ ) { for ( j = 0; j < n; j++ ) { if ( a[i][j] > 0 ) splus++; if ( a[i][j] < 0 ) snegativ++; if ( a[i][j] == 0 ) snul++; } } printf("\nNumarul de elemente pozitive este %d", splus ); printf("\nNumarul de elemente negative este %d", snegativ ); printf("\nNumarul de elemente nule este %d\n", snul ); I1 I2 I3 I4 I5 I6 I7 I8
I9 I10 I11
28
Graful asociat unui program permite evidenierea modului n care este construit programul. Pentru programul:
int splus int snegativ = 0; int snul = 0; = 0; I1 I2 I3 I4 I5 I6 I7 I8 I9 I10 I11 I12
for ( i = 0;i < m; i++ ) { for ( j = 0; j < n; j++ ) { if ( a[i][j] > 0 ) { splus++; } else { if ( a[i][j] < 0 ) { snegativ++; } else { snul++; } } } }
29
printf("\nNumarul de elemente pozitive este %d", splus ); printf("\nNumarul de elemente negative este %d", snegativ ); printf("\nNumarul de elemente nule este %d\n", snul );
Abordarea structurii graf permite dezvoltarea de operaii de transformare care s conduc la construcii echivalente, ns optimizate. Numrul de arce i orientarea acestora deosebete tehnicile de programare.
30
31
Complexitatea ciclomatic Tabelul 2.1 Complexitate ciclomatic 1 10 11 20 21 50 mai mare de 50 Evaluarea riscului un modul/program simplu, fr risc mare un modul/program mai complex, cu risc mediu un modul/program foarte complex, cu risc ridicat un modul/program netestabil, cu un risc extrem de ridicat
Dac un program este format din instruciunile I1, I2, , IK, crora li se asociaz graful dat n figura 2.4.
Complexitatea CM = 1 pentru c: na nn = K1 = K
Pentru programul cu structura de graf dat n figura 2.5, complexitatea CM = 14 10 + 3 = 6, pentru c numrul de arce este 14, iar numrul de noduri este 10.
32
Pentru programul de alegere a elementului minim i a elementului maxim dintr-un ir dat prin textul surs:
int x[N], i, n, minim, maxim; n = citeste(); //functie de pentru numarul de elemente citeste(n, x); // functie de citire pentru elemente maxim = minim = x[0]; for ( i = 0; i < n; i++ ) { if ( minim > x[i] ) { minim = x[i]; } if ( maxim < x[i] ) { maxim = x[i]; } } printf( "Maximul este %d\n", maxim ); printf( "Minimul este %d\n", minim );
33
Se observ c de la o tehnic de programare la alta difer nivelul complexitii n sens McCabe. O alt metric a complexitii software o reprezint complexitatea Halstead. Ea a fost dezvoltat n special pentru a msura complexitatea unui modul de program pe baza codului surs, cu accent pe complexitatea de calcul. Metricile Halstead se bazeaz pe patru valori numerice, rezultate din codul surs: n1 n2 - numrul de operatori distinci - numrul de operanzi distinci
Din aceste numere, rezult cinci metrici: Metric Lungimea programului Vocabularul programului Volumul Dificultatea Efort Simbol N n V D E Formula N = N1 + N2 n = n1 + n 2 V = N * log2n D = (n1/2)*(N2/n2) E=D * V
Volumul modulului, V, reprezint coninutul informaional al modulului. El descrie dimensiunea implementrii unui algoritm. Calculul lui V este bazat pe numrul de operaii realizate i de numrul de operanzi cu care lucreaz algoritmul. Astfel determinat, volumul unei funcii trebuie s rezulte ntre valorile 20 i 1000. Un volum mai mare de 1000 reprezint o indicaie c funcia respectiv conine mult prea multe prelucrri. Volumul unui fiier cu cod surs trebuie s se gseasc ntre 100 i 8000. Complexitatea n sens Halstead CH este dat de relaia: CH = N1 log2N1 + N2 log2 N2 Astfel, pentru secvena:
a b c d = = = = 7; 8; a + b; a * c + b * ( a 1 )
prin numrare operanzi i operatori N1 = 10, N2 = 13. CH = 10 log2 10 + 13 log2 13 deoarece cele dou paranteze sunt un singur operand. n secvena:
a += b++ + c++; a*=--b - --c;
numrul operanzilor N2 = 6, iar numrul operatorilor N1 = 10, ceea ce conduce la complexitatea CH = 10 log2 10 + 6 log2 6
35
Pentru programe de mari dimensiuni este preferabil s se utilizeze instrumente care numr operanzii, respectiv operatorii, permind automatizarea procesului de msurare a complexitii n sens Halstead.
Etapa de proiectare const n elaborarea soluiei, corelnd cerinele care rezult din definirea problemei, cu resursele pe care le ofer limbajele de programare, tehnologiile software existente, precum i sistemele de gestiune a bazelor de date. Aceast faz asigur programatorului suficiente informaii pentru ca acesta s scrie rutine software care realizeaz aciuni bine definite n cadrul aplicaiei. Soluia elaborat este documentat i prezentat clientului spre aprobare. Ea se adreseaz urmtoarelor arii de interes n cadrul dezvoltrii produsului software: proiectarea interfeei cu utilizatorul, ce reprezint imaginea sistemului pentru utilizator; proiectarea bazei de date, prin care se stabilete cum vor fi stocate i structurate datele utilizate de produs; stabilirea aciunilor realizate de aplicaie sub aspectul datelor ce sunt prelucrate i a algoritmilor folosii pentru aceasta; identificarea i definirea interfeelor externe, prin care sistemul se conecteaz la alte sisteme existente (automat sau manual); stabilirea formatelor de fiiere folosite pentru exportul i importul de date; conversiile de date, ce se refer la posibilitatea ca, n cazul n care datele existente sunt ncrcate ntr-o aplicaie nou, anumite conversii i reformatri s fie necesare.
Pe lng cele menionate mai sus, designul unui produs software precizeaz arhitectura general a aplicaiei, modul n care modulele componente sunt combinate pentru a oferi funcionalitatea dorit, nivelul planificat pentru caracteristicile de calitate. Dac etapa de analiz a problemei spune ce trebuie s se fac, etapa de proiectare stabilete cum trebuie s se fac. Documentul de proiectare este folosit de programatori i de testeri, n etapele urmtoare, pentru a dezvolta produsul i pentru a testa dac funcionalitatea rezultat n urma codificrii corespunde funcionalitii proiectate. Etapa de implementare a codului const n scrierea efectiv a produsului software. Software trebuie scris ca s respecte cerinele specificate n faza de analiz i testate n timpul dezvoltrii. De regul exist standarde de programare i recomandri de soluii pentru problemele comune care sunt aplicate n timpul dezvoltrii produsului pentru a spori
37
calitatea acestuia. Principalele rezultate ale acestei faze sunt aplicaia n sine, documentaia i rezultatele testelor. Dintre activitile de asigurare i control al calitii, cele mai prezente n timpul acestei faze sunt cele de revizuire a codului surs i inspeciile de cod i de documente. Etapa de testare are ca obiectiv s stabileasc dac sistemul este robust din punct de vedere tehnic, fiabil i corespunde cerinelor pentru care a fost dezvoltat. Caracteristicile principale ale testrii sunt: posibilitatea de a identifica ulterior testele realizate; posibilitatea de a repeta execuia unui test; documentarea pailor urmai n testarea unei funcionaliti i a rezultatelor obinute. testarea fiecrei rutine software n parte; testare de integrare a modulelor asamblate n aplicaia final sau ntr-un subsistem; testare de sistem a ntregii aplicaii pe aceeai platform ca cea pe care clientul o va folosi; testele de instalare prin care se verific c procedurile i instruciunile de instalare sunt corecte; testele de regresie, realizate dup upgrade-uri de software (adugri de funcionalitate nou, corectri de defecte); teste de acceptan, care sunt parte din faza de instalare, i prin care clientul verific dac ceea ce i se livreaz corespunde cu ceea ce a cerut.
Testarea reprezint o arie extrem de complex creia, de cele mai multe ori, i se acord insuficient atenie. Trebuie neles c orict de mult sar testa un produs, nu se poate garanta c aplicaia este complet lipsit de defecte. Costul testrii trebuie analizat prin prisma costului potenial al funcionrii proaste, prin prisma defectelor software. Pentru aplicaiile necritice, este acceptabil de exemplu, lansarea unei versiuni beta pentru ca utilizatorii s o testeze n producie. O astfel de abordare nu este acceptat n cazul sistemelor software critice.
38
Etapa de implementare reprezint predarea oficial a produsului mpreun cu documentele asociate clientului. Procesul efectiv de instalare presupune: instalarea software pe sistemele de calcul ale clientului; executarea testelor de acceptan mpreun cu clientul; semnarea unor documente prin care se accept produsul de ctre client; pregtirea viitorilor utilizatori ai sistemului pentru folosirea acestuia.
Etapa de mentenan, dei nu face parte din ciclul de dezvoltare software, este o etap extrem de important a ciclului de via, pe care nici o companie productoare de software nu trebuie s o neglijeze. Instalarea produsului i predarea lui ctre client nu nseamn c din acel moment, productorul software nu mai are nici o legtur cu produsul. Mentenana include urmtoarele activiti: rezolvarea problemelor care apar n timpul rulrii aplicaiei n mediul de producie detectarea, analiza i corectarea defectelor software; modificarea interfeelor determinat de adugri sau schimbri la platforma hardware pe care ruleaz software; extinderea funcionalitii sau mbuntirea performanelor clientul solicit modificri dup ce livrarea iniial i instalarea au fost realizate.
Perioada de mentenan care urmeaz imediat instalrii este inclus ca parte a unui contract de garanie furnizat de productorul software. Mentenana pe termen lung are un alt statut n cadrul relaiei client productor software. Pentru ca un proiect de dezvoltare s fie un succes, o abordare sistematic este necesar. Proiectul trebuie mprit n faze, fiecare cu un obiectiv clar. Munca efectuat n cadrul fiecrei faze furnizeaz dezvoltatorului informaii necesare pentru a estima efortul din faza urmtoare. Costul total al proiectului este, astfel, mai sczut dect estimarea realizat la nceputul proiectului. Nu toate proiectele urmeaz acest ciclu de dezvoltare. Dimensiunea proiectului, n termeni de cost i efort necesar, i natura proiectului
39
determin ce faze sunt relevante i ce efort suplimentar trebuie alocat pentru fiecare faz. Efectele pe care le genereaz un produs sunt multiplicate cu numrul de utilizatori simultani ai produsului. Este necesar ca activitatea de dezvoltare software s aib la baz procese de optimizare dinamic, adic de alegere din aproape n aproape a acelor resurse ce n final asigur realizarea de produse de foarte bun calitate.
40
datele de ieire: xm nivelul mediei aritmetice. domeniile de variaie: n {5,6,....,1000} x i [1000;1000] f i [0;100] N x m [10000;10000]
formule de calcul:
xm =
f
i =1 n
* xi
i
f
i =1
Pentru a analiza calitatea specificaiilor se construiesc tabelele 3.1 i 3.2 n care sunt stabilite relaiile ntre elementele definite.
Dependena ntre variabila endogen i variabilele exogene
Tabelul 3.1
Variabila endogen Variabilele exogene
n fi xi * * *
xm
Tabelul 3.2
Variabile Formula xm n fi xi
xm =
f
i =1 n
* xi
i
f
i =1
42
Abordarea clasic
Avnd n vedere c n tabelele 3.1 i 3.2 nu exist linii sau coloane fr asteriscuri, rezult c definirile legturilor dintre date de intrare i rezultate sunt complete. Asteriscurile marcheaz prezena unei variabile n formul sau legtura dintre variabilele exogene i variabila endogen, aa cum rezult din formul. Specificaiile trateaz i cazurile de excepie. Pentru f1 = f2 = ..... = fn = 0, xm = 0. n specificaii sunt prezentate date de test, pentru seturile din tabelele 3.3, 3.4 i 3.5 i nivelurile variabilei xm.
Set de date identice
Tabelul 3.3
Nr. crt
1 2 ..... 15 xm = 10 10 10 10
fi
10 10 10
xi
Tabelul 3.4
Nr. crt
1 2 3 4 5 6 7 8 9 10 xm = 0 10 8 6 4 2 2 4 6 8 10
fi
-15 -13 -11 -9 -7 +7 +9 +11 +13 +15
xi
43
Tabelul 3.5
Nr. crt
1 2 3 4 1 3 5 7
fi = fi-1 + 2
3 11 19 27
xi = 4*fi - 1
Datele din tabele se construiesc aa fel nct s existe o formul de calcul pentru ele ca verificarea corectitudinii prelucrrilor realizate de program s fie mult mai uoar, ca n exemplul datelor din tabelul 3.6
Serii de date generate
Tabelul 3.6
Nr. crt
1 2 3 N 3 5 7 2*n + 1
fi = 2*i + 1
2 5 10
xi = i 2 + 1
n2 + 1
Aplicnd relaia:
xm =
f
i =1 n
* xi
i
f
i =1 n
xm =
(2i + 1)(i + 1)
i =1
(2i + 1)
i =1
xm =
n 2 + 6n + 8 3(n + 2)
44
Abordarea clasic
Se construiete o procedur care genereaz seriile fi, xi i calculeaz xm. Prin apelul programului de calcul pentru media aritmetic cu aceste date generate se obine un rezultat x`m. Dac x`m = xm, rezult c programul scris pentru calculul mediei aritmetice este corect n raport cu setul de date pentru care a fost testat. Specificaiile de programare sunt construcii care sistematizeaz informaii extrem de variate. Cei care elaboreaz specificaiile au rol hotrtor n definirea contextului prin restriciile asupra variabilelor, prin formulele construite i prin informaiile care nsoesc algoritmii de prelucrare. Absena unor restricii, definirea incorect a domeniilor, construirea de formule incomplete genereaz probleme serioase procesului de elaborare a programelor. n contextul actual sunt soluionate multe dintre problemele legate de elaborarea de specificaii. n contextul primelor abordri, n ceea ce privete scrierea de programe, programatorul era singurul care i definea datele de intrare, definea rezultatele i crea sau prelua algoritmi. Fr o gndire sistematic, fr instrumente adecvate, specificaiile includeau o serie de inexactiti sau eliminau o serie de aspecte, ceea ce reducea aria de cuprindere a programelor. Arta elaborrii de programe i avea izvorul n arta de a elabora specificaii.
Programatorii preiau algoritmii deja analizai din punct de vedere a calitii soluiei sau construiesc algoritmi proprii. Pentru verificarea unui ir x1, x2, ...., xn care are componentele ordonate cresctor se construiesc de ctre programator doi algoritmi: Algoritmul A1: pasul 1 : se iniializeaz variabila iv cu 0; pasul 2 : se traverseaz irul; pasul 3 : se compar xi cu xi+1; pasul 4 : dac xi > xi+1, se incrementeaz variabila iv; pasul 5 : dup ncheierea procesului de comparare se testeaz variabila iv; pasul 6 : dac iv = 0, se afieaz mesajul irul este ordonat, n caz contrar se afieaz mesajul irul nu este ordonat. pasul 1 : se traverseaz irul; pasul 2 : se compar xi cu xi+1; pasul 3 : dac xi > xi+1 se ntrerupe traversarea i se afieaz mesajul irul nu este ordonat; pasul 4 : dac traversarea se ncheie normal, se afieaz mesajul irul este ordonat.
Algoritmul A2:
Este important s se construiasc algoritmi complei i coreci pentru problemele de rezolvat i abia dup aceea s se urmreasc mbuntirea lor, fr a accepta conceptul de algoritm optim. Preluarea algoritmilor este o necesitate i modul concret prin care se realizeaz const n: asimilarea unei mulimi de algoritmi de baz, pentru operaii de prelucrare bine definite; alegerea algoritmilor care se potrivesc clasei de probleme n raport cu structurile de date utilizate; ntr-un fel sunt concepui algoritmii care lucreaz cu fiiere, altfel sunt construii algoritmii care opereaz pe masive; combinarea de algoritmi pentru a obine fluxurile de prelucrare specifice soluionrii unei probleme.
46
Abordarea clasic
Exist mai multe moduri de descriere a algoritmilor, dintre care sunt prezentate n continuare urmtoarele: forma text, n care operaiile i operanzii sunt dai sub form de fraze precum: se iniializeaz de la tastatura...; rnd pe rnd se adun fiecare element; se compar element cu element....; primul element se interschimb cu ultimul, al doilea se interschimb cu penultimul, procedeul continu n acelai fel pn cnd.... limbaj de descriere a operaiilor n succesiunea efecturii pseudo-cod : sau
x1 = 2 xi = xi-1, i = 2, 3, ...., n x1 = 0 x2 = 1 xi = xi
sau
+ xi-2, i = 2, 3, ....., n
schemele logice, care reprezint o transcriere grafic a etapelor (pailor) unui algoritm.
Limbajul utilizat n descrierea algoritmului trebuie s conduc la elaborarea de programe, fr confuzii, care prin testare s evidenieze c pentru date de intrare complete i corecte se obine rezultatul corect indicat.
47
i arce orientate Nivelul de detaliere depinde de modul de nelegere a algoritmului, de rolul pe care l are cel care elaboreaz schema logic i persoana care elaboreaz textul surs. ntre schema logic i program trebuie s existe o coresponden clar n ceea ce privete numele variabilelor, etichetelor ntrebuinate i formulelor de calcul. Pentru aflarea elementului minim dintr-un ir i a poziiei acestuia se utilizeaz variabilele: n x[i] xmin pozmin i - numrul de componente al vectorului x[]; - componenta i a vectorului; - valoarea elementului minim; - poziia elementului minim; - variabil de control.
48
Abordarea clasic
49
Pentru a verifica dac o matrice este simetric se construiete schema logic din figura 3.3.
Abordarea clasic
Pentru numrarea componentelor nule, negative i pozitive ale unui masiv a[m][n] bidimensional se elaboreaz schema logic din figura 3.4.
START
Citeste m,n
Citeste a[i][j], i=1, , m j=1,..,n
e1
j=0
e2
a[i][j] > 0
Da
e3
nrpoz = nrpoz + 1
e5
a[i][j] < 0
Da
e4
nrneg = nrneg + 1
e5
nrnul = nrnul + 1
51
Figura 3.4 Schema logic pentru numrarea elementelor negative, pozitive i nule
Schemele logice clasice corespund unui stadiu incipient al dezvoltrii limbajelor de programare. De exemplu, limbajul FORTRAN este nzestrat cu instruciunea IF cu sintaxa:
IF ( expresie ) e1, e2, e3
unde: e1 e2 e3 - eticheta instruciunii care se execut dac expresia este negativ; - eticheta instruciunii care se execut dac expresia este nul; - eticheta instruciunii care se execut dac expresia este pozitiv.
52
Abordarea clasic
O astfel de construcie impune utilizarea de instruciuni cu etichete i continuarea prelucrrii prin efectuarea unui salt necondiionat la secvena cu eticheta e4, figura 3.5.
53
Pentru numrarea elementelor nule, pozitive i negative ale unui masiv bidimensional se construiete secvena de schem logic dat n figura 3.6.
Abordarea clasic
Pe msur ce se dezvolt limbajele de programare, n schemele logice apar modificri. Programul scris n C++ dup schema logic din figura 3.4 este:
#include <stdio.h> #define M #define N 10 10
int main () { int i, j, m, n, a[M][N]; int nrplus, nrnegativ, nrnul; nrplus = nrnegativ = nrnul = 0; printf( "Introduce numarul de linii (<%d) = ", M ); scanf( "%d", &m ); printf( "Introduce numarul de coloane (%d) = ", N ); scanf( "%d", &n ); i = 0; et11: if ( i < m ) { j = 0; et12: if ( j < n ) { printf("a[%d][%d] = ", i, j ); scanf("%d", &a[i][j] ); j++; goto et12; } i++; goto et11; } i = 0; et1: if ( i < m ) { j = 0; et2: if ( j < n ) { if ( a[i][j] > 0 ) { nrplus++; } else { if ( a[i][j] < 0 ) { nrnegativ++; } else { nrnul++; } } j++; goto et2;
55
Analiza comparat a complexitii entitilor text generate prin tehnici de programare } i++; goto et1; } printf( "Numarul de elemente pozitive este %d\n", nrplus ); printf( "Numarul de elemente negative este %d\n", nrnegativ ); printf( "Numarul de elemente nule este %d\n", nrnul ); }
Datorit caracterului su artizanal, tehnica nu se baza pe reutilizarea codului. Programatorul rescria funcionaliti ce altfel se regseau n biblioteci scrise deja. Prin aceasta dimensiunea codului surs cretea foarte mult i odat cu ea i complexitatea software. De asemenea, frecvente erau i programele monolit, care conineau toat funcionalitatea n programul principal, neexistnd apeluri ctre subrutine. Pentru aplicarea formulei complexitii ciclomatice, se identific urmtoarele valori pentru parametrii acesteia: na nn = 36 = 31
Rezult c valoarea complexitii ciclomatice este: CM = 36 31 + 2 = 7 Valoarea obinut este relative mare, avnd n vedere c complexitatea problemei este mic, ns ea se datoreaz n special faptului c pe de o parte au fost folosite instruciuni de salt necondiionat, care au mrit numrul de arce din cadrul grafului asociat, dar i deoarece nici varianta de algoritm implementat nu este optim. Conform setului de valori-prag, acest program se ncadreaz n categoria de risc minim. Pentru aplicarea formulelor Halstead, se identific urmtoarele valori pentru parametrii acestora: n1 n2 = 5 = 8
N1 = 22 N2 = 36
56
Abordarea clasic
Pentru aceste valori, aplicnd formulele de calcul pentru metricile Halstead, se obine:
Metric
Lungimea programului Vocabularul programului Volumul Dificultatea Efort = = = = =
Valoare
64 14 384 14,25 5472
Dac se raporteaz valorile obinute la valorile-prag, se constat c ele se ncadreaz n intervalele acceptate. Trebuie menionat c, dei limbajul C++, folosit pentru a implementa rezolvrile la problema aleas, ofer implementri pentru structuri repetitive (for, while), s-a ales varianta cu instruciuni de salt necondiionat specific pentru perioada n care aceast tehnic de programare era folosit. Exist limbaje, cum este i cel de asamblare, care nu au implementri de structuri repetitive, i prin urmare ele trebuie scrise de ctre programator. Programarea clasic are o serie de imperfeciuni pe care programatorii de azi nu le accept sau pentru care au gsit soluii mai ales prin elaborarea de expresii condiionale compuse. Acum, pentru a verifica dac o matrice este sau nu simetric se scrie secvena:
. k = 1; for ( i = 0; i < m && k == 1; i++ ) { for ( j = 0; j < n && k == 1; j++ ) { if ( a[i][j] != a[j][i] ) k = 0; } } if ( k == 1 ) printf (matrice simetrica); else printf (matrice nesimetrica); .
secvena devenind:
k = 1; for ( i = 0; i < m && k == 1; i++ ) { for ( j = 0; j < n && k == 1; j++ ) { k = a[i][j] == a[j][i]; } } printf( k == 1 ? matrice simetrica : matrice nesimetrica); .
57
Programarea clasic are un caracter direct, avnd de evaluat expresii simple i efectund prelucrri cu pai mruni, din aproape n aproape.
58
Abordarea clasic
Problema ordonrii ntreprinderilor dup cifra de afaceri, i corespunde azi textul surs:
#define N #define MAX_STRING int main() { int n, i, j, min; char* sc[N]; float cf[N]; memset(sc, 0, N); printf( "Introduce numarul de societati comerciale (<=%d)", N ); scanf( "%d", &n); for ( i = 0; i < n; i++ ) { sc[i] = (char*)malloc(MAX_STRING); printf("Numele firmei = "); scanf("%s", sc[i]); printf("Cifra de afaceri = "); scanf("%f", &cf[i]); 10 255
for ( i = 0; i < n - 1; i++ ) { min = i; for ( j = i+1; j < n; j++ ) { if ( cf[j] < cf[i] ) { min = j; } } //muta elementul minim pe pozitia i char* temp = sc[i]; sc[i] = sc[min]; sc[min] = temp; float f = cf[i]; cf[i] = cf[min]; cf[min] = f; } for ( i = 0; i < n; i++ ) { printf(" Societatea %s are CF %f\n", sc[i], cf[i]); } }
Programarea clasic soluiona problema prin codificarea ntreprinderilor i operarea cu doi vectori, unul de tip ntreg pentru codurile ntreprinderilor i unul de tip real pentru valorile cifrei de afaceri, obinnd
59
programul care, la acel moment, din cauza imposibilitii de a construi blocuri i datorit absenei instruciunii while, era scris folosind instruciunile if i goto care aveau menirea de a selecta situaiile diferite, cu efecte negative asupra fluenei ntregului demers. Se obine textul surs:
i = 0; et1: if ( i < n - 1 ) { min = i; j = i; et2: if ( j < n ) { if ( cf[j] < cf[i] ) { min = j; } j++; goto et2; } //interschimba pozitia i cu pozitia min int temp1 = sc[i]; sc[i] = sc[min]; sc[min] = temp1; float temp2 = cf[i]; cf[i] = cf[min]; cf[min] = temp2; i++; goto et1; } i = 0; et3: if ( i < n ) { printf(" Societatea %d are CF %f\n", sc[i], cf[i]); i++; goto et3; }
Comparnd textele surs, devine foarte clar necesitatea acceptrii faptului c evoluia coninutului unui program a depins strict de resursele care au fost la dispoziia programatorilor. Muli dintre programatorii cu prul alb sunt cei care au scris programe foarte bune n limbajul FORTRAN 4 acum 35 de ani, tot ei fiind cei care lucreaz n C# sau Java cu aceeai uurin.
60
Abordarea clasic
De exemplu, programul pentru nsumarea elementelor unui ir se procedeaz astfel: se iniializeaz irul cu valorile 1,2, 3, .., n;
61
se afieaz valoarea variabilei de control a irului, valorile irului i sumele intermediare; se afieaz mesaje care arat c au fost parcurse etapele programului.
Abordarea clasic
Din analiza acestor informaii se verific faptul c programul efectueaz corect prelucrrile. n cazul n care se testeaz procedura:
int expresie( int a, int i, int k) { int result = 0; if ( i % 2 ) { if ( !(i % k) ) { printf("Numar divizibil cu %d\n", k); result = a * a; } else { printf("Numar nedivizibil cu %d\n", k); result = a; } } else { printf("Numar par\n"); result = a * a * a; } return result; }
63
int main() { int a, i, k, n, j; printf( "Introduce numarul de seturi de date i = " ); scanf( "%d", &n ); printf( "Introduce numarul de variabile de control k = " ); scanf( "%d", &k );
printf("i\ti*i\ti*i*i\tk\ta\texpresie\n"); for ( j = 1; j <= k; j++ ) { for ( i = 0; i < n; i++ ) { printf("%d\t%d\t%d\t%d\t%d\t%d\n", i, i*i, i*i*i, j, i, expresie(i, i, j)); } } }
Analiznd rezultatele intermediare, rezult c procedura efectueaz prelucrri corecte. Pentru programele cu grad de complexitate ridicat, procedurile de testare sunt mai elaborate. Cei care elaboreaz specificaiile aveau obligaia s furnizeze situaiile care trebuie testate, cu luarea n considerare a situaiilor care n practic ofereau cazuri greu de controlat,
64
Abordarea clasic
ateptndu-se ca prin implementarea produsului software, tocmai acele erori de prelucrare manual s fie nlturate.
Trecerea spre meteug s-a impus cnd au trebuit scrise multe programe pentru a dezvolta sisteme informatice complexe. Deja exist un responsabil de proiect, deja trebuia s existe un mod de comunicare prin specificarea structurilor de articole, dimensiunilor de matrice. Trebuia pus ordine n modul n care sunt prelucrate datele pentru ca un program s nu distrug informaii pe care un alt program trebuie s le preia. Discuiile dintre analiti, discuiile dintre programatori au cristalizat tehnici de analiz i stiluri de programare.
65
A nceput perioada definirii unor reguli care s conduc la creterea calitii programelor. De la caracterul meteugresc spre caracterul industrial era doar un pas. Exagerrile legate de transferul mecanic a unor elemente de structur i de management din industria prelucrtoare spre producia de software a avut un singur efect: compromiterea ideii de industrie de software. Lucrurile apar acum cu totul sub alt lumin n contextul organizaiei virtuale de producie software pentru c: posturile de lucru sunt legate prin reea; accesul la resurse este dat de facilitile internetului; instrumentele de dezvoltare a aplicaiilor distribuite sunt o realitate; concepia dezvoltrii de aplicaii complete care s aduc acoperire avantajoas pentru cetean n planul fluxurilor materiale i a fluxurilor bneti este deja o realitate.
Aa cum societatea uman a trecut prin comuna primitiv, epoca bronzului, epoca fierului, prin sclavagism, feudalism, capitalism, comunism i retur, programarea calculatoarelor a reflectat salturile spectaculoase din domeniul hardware. n software nu exist penurie de idei. Exist numai restricii n plan tehnic. De la o etap la alta, pe msur ce barierele sunt doborte, se obin i salturi calitative n producia de software; iar ceea ce se ntmpl an de an este o evoluie normal i necesar care ajut dezvoltarea societii informaionale.
66
Regulile de construire a bibliotecilor de programe sunt stricte pentru a se obine programe de foarte bun calitate. Prima regul const n acceptarea unui stil de programare caracterizat prin: modul de alegere a numelor de variabile; modul n care sunt iniializate variabilele; construirea etichetelor i dispunerea lor, de exemplu, n ordine cresctoare e10, e20, ... , e300; utilizarea instruciunilor pentru a asigura un mod unitar de interpretare; astfel, dac se adopt un mod de parcurgere a unui masiv unidimensional cu ajutorul instruciunii for, atunci aceasta este aplicat n toate cazurile din cadrul programului, evitndu-se introducerea unor parcurgeri cu while sau chiar instruciuni de salt necondiionat, goto; separarea instruciunilor de citire i scriere de celelalte subprograme pentru a nu influena procesul de mentenan atunci cnd se dorete efectuarea de modificri n program, dar i pentru a obine creterea flexibilitii n ceea ce privete iniializarea variabilelor; stabilirea structurilor de date utilizate pentru a obine maximizarea gradului de utilizare a memoriei alocate; de la un program la altul, pentru aceleai prelucrri se folosesc aceleai structuri de date, denumirile lor nefiind diferite, mai ales, n ceea ce privete pointerii cu care se refer componentele; asigurarea continuitii att n ceea ce privete interfeele, ct i n ceea ce privete modul de elaborare a programelor. Biblioteca de programe trebuie s apar ca un ntreg, format din componente omogene programele.
Pe msur ce s-au impus bibliotecile de programe, dezvoltatorii de software au dezvoltat i biblioteci de subprograme, cele mai multe subprograme dezvoltnd funcii de prelucrare fundamentale n raport cu asigurarea calitii i mai ales cu creterea generalitii programelor. i subprogramele de bibliotec sunt elaborate pe baza unor reguli, din care mai importante sunt: construirea numelui de subprogram n aa fel nct s se rein cu uurin i s se intuiasc semnificaia prelucrrii; de exemplu, invmat() este subprogramul pentru inversarea de
68
Programarea standard
matrice, sortstring() este subprogramul pentru sortarea unui ir, sortfile() este subprogramul pentru sortarea unui fiier, prodpol() este subprogramul pentru produsul a dou polinoame; pentru tipul rezultatului returnat se indic de fiecare dat semnificaia; nivelul de autodocumentare este foarte ridicat, fiind incluse comentarii legate de semnificaia parametrilor, a secvenelor coninnd elemente de identificare a programatorului i a algoritmului utilizat; listele de parametrii au elemente comune care s fie uor interpretate i mai ales s fie uor de construit i de iniializat pentru a efectua un apel corect pentru fiecare subprogram; se stabilete regula privind modul de revenire n programul principal; ntr-un fel este privit programul ce conine o singur instruciune return, fa de programul care are mai multe instruciuni return.
eful de proiect al unei biblioteci de programe sau de subprograme, cnd constituie echipa de analiti i programatori prezint reguli, care sunt adoptate, chiar cu unele modificri. Dup ce regulile sunt adoptate, devin obligatorii i toate programele sau subprogramele sunt elaborate cu utilizarea acestor reguli. Prin respectarea regulilor, biblioteca de programe este o construcie unitar, operaional i, mai ales, care este utilizat de toi cei care dezvolt software, datorit economiei de efort pe care o genereaz.
4.2 Subprogramul
Programarea standard are la baz lucru cu subprograme performante, stocate n biblioteca de subprograme. Programatorul are menirea de a selecta cele mai potrivite subprograme pentru a soluiona fiecare problem n modul cel mai eficient. Pentru problema definit n capitolul Ciclul de dezvoltare software se consider o bibliotec de subprograme care opereaz cu masive unidimensionale. Regulile dup care se construiesc subprogramele sunt urmtoarele: numrul de componente ale irului este n; elementele irului sunt x[0], x[1], , x[n-1];
69
denumirea subprogramului arat operaia i operandul de baz; variabila i permite referirea elementelor unui ir sau refer liniile unui masiv bidimensional; variabila j refer elementele unei coloane din masivul bidimensional; masivul bidimensional este referit prin numele a[M][N].
Biblioteca de subprograme BVECTMAT se organizeaz pe dou niveluri: nivelul de baz conine proceduri de lucru cu masive unidimensionale (vectori); nivelul derivat conine proceduri de lucru cu masive bidimensionale vzute ca vectori de vectori, folosind subprogramele de lucru cu vectori.
Pentru copierea elementelor unei linii k dintr-un masiv bidimensional a[M][N] ntr-un masiv unidimensional x[N], se folosete subprogramul:
void copylinvect( int a[][N], int x[], int n, int k ) { for ( int j = 0; j < n; j++ ) { x[j] = a[k][j]; } }
Pentru copierea unei coloane k a matricei a[M][N] n vectorul x[M] se folosete subprogramul:
void copycolvect( int a[][N], int x[], int m, int k ) { for ( int i = 0; i < m; i++ ) { x[i] = a[i][k]; } }
70
Programarea standard
Pentru copierea unui vector x[N] pe linia k a unei matrice a[M][N] se utilizeaz subprogramul:
void copyvectlin( int a[][N], int x[], int n, int k ) { for ( int j = 0; j < n; j++ ) { a[k][j] = x[j]; } }
Pentru numrarea elementelor unui ir x[] mai mari dect valoarea k, se folosete subprogramul:
int contorgreater ( int x[], int n, int k ) { int contor = 0; for ( int i = 0; i < n; i++ ) { if ( x[i] > k ) { contor++; } } return contor; }
71
Pentru a contoriza elementele irului mai mici dect o valoare k, se scrie subprogramul:
int contorless ( int x[], int n, int k ) { int contor = 0; for ( int i = 0; i < n; i++ ) { if ( x[i] < k ) { contor++; } } return contor; }
Iniializarea prin atribuire a elementelor vectorului x[] cu o valoare k se realizeaz prin procedura:
void initvect ( int x[], int n, int k ) { for ( int i = 0; i < n; i ++ ) { x[i] = k; } }
Pentru iniializarea componentei de pe poziia poz a irului x[] cu o valoare k se utilizeaz procedura:
void initpoz ( int x[], int poz, int k ) { x[poz] = k; }
Pentru realizarea procedurilor derivate se utilizeaz procedeul de lucru cu masive unidimensionale. Astfel, generarea matricei unitate linie de linie presupune conturarea unei structuri repetitive de tip for dat n figura 4.1.
72
Programarea standard
} }
Figura 4.1 Funcia de generare a matricei unitate folosind funcii de lucru cu vectori
73
Subprogramul scadvect() care evalueaz expresiile z[i] = x[i] y[i], are textul surs:
void scadvect(int x[], int y[], int z[], int n) { int i; for ( i = 0; i < n; i++ ) { z[i] = x[i] - y[i]; } }
Subprogramul adunvect() care evalueaz expresiile z[i] = x[i] + y[i], are textul surs:
void adunvect(int x[], int y[], int z[], int n) { for ( int i = 0; i < n; i++ ) { z[i] = x[i] + y[i]; } }
Dac trebuie scris subprogramul pentru transpunerea unei matrice a[][N], obinndu-se matricea b[][M], procedura corespunztoare este:
void transpune ( int a[][N], int b[][N], int m, int n) { int j; int x[N]; for ( j = 0; j < n; j++ ) { copycolvect(a, x, m, j); copyvectlin(b, x, m, j); } }
74
Programarea standard
75
Procedura pentru numrarea elementelor pozitive, nule respectiv negative ale unei matrice se realizeaz cu ajutorul procedurilor:
int contorplus (int a[][N], int m, int n) { int i, x[N]; int contor = 0; for ( i = 0; i < m; i++ ) { copylinvect(a, x, n, i); contor += contorgreater(x, n, 0); } return contor; }
int contorminus (int a[][N], int m, int n) { int i, x[N]; int contor = 0; for ( i = 0; i < m; i++ ) { copylinvect(a, x, n, i); contor += contorless(x, n, 0); } return contor; } int contorzero (int a[][N], int m, int n) { int i, x[N]; int contor = 0; for ( i = 0; i < m; i++ ) { copylinvect(a, x, n, i); contor += contorequal(x, n, 0); } return contor; }
Pentru rezolvarea problemei PROB formulate n capitolul Ciclul de dezvoltare software, programul apelator va conine: apelul subprogramului citmat() pentru iniializarea de la tastatur; apelul subprogramului contorplus() elemente mai mari dect k = 0; pentru contorizare
76
Programarea standard
pentru
numrarea
apelul unui subprogram pentru afiarea valorilor nrplus, nrminus i nrzero cum este urmtoarea:
Pentru aplicarea formulei complexitii ciclomatice pentru programul apelator (funcia main) se identific urmtoarele valori pentru parametri: na nn = 11 = 12
77
O astfel de valoare are mai multe semnificaii: datorit atomicizrii aciunilor, i gruprii lor n biblioteci de programe i subprograme, construirea unui produs software este mult simplificat, ajungndu-se la situaia ideal de a avea de scris doar o succesiune de apeluri de rutine pentru implementarea unei funcionaliti; din acest punct de vedere complexitatea produsului este redus, mai ales n condiiile unei bune documentri a rutinelor folosite; dac bibliotecile sunt concepute n cadrul aceluiai proiect ca i produsul software, atunci valoarea complexitii este neltoare, pentru c ea nu exprim i complexitatea ascuns a rutinelor folosite. n cazul n care se calculeaz i complexitile ciclomatice ale rutinelor apelate din cadrul programului apelator, se obin urmtoarele rezultate pentru parametrii formulei i respectiv pentru complexitate: Rutin
main citmat contorplus contorminus contorzero printint
na
11 5 7 7 7 0
nn
12 4 6 6 6 1
Complexitate
1 3 3 3 3 1
Complexitatea total este 14, cu urmtoarele ipoteze: instruciunea for este considerat o singur instruciune i nu trei; nu s-a mers mai departe de primul nivel de derivare, adic nu a fost dimensionat i complexitatea rutinelor de pe nivelul de baz al bibliotecii de subprograme. Se observ, doar n condiiile n care se iau n considerare i complexitile rutinelor apelate, valoarea complexitii ciclomatice totale se apropie de valoare obinut pentru abordarea clasic. ns, n mod firesc, are sens calculul complexitii doar pe baza codului din programul apelator, fie i din simplul motiv c biblioteca de subprograme este deja compilat i prin urmare nu exist un acces la codul-surs.
78
Programarea standard
n condiiile n care se consider semnificativ, analiza doar asupra programului apelator, pentru aplicarea formulelor Halstead se identific urmtoarele valori pentru parametrii: n1 n2 N1 N2 = = = = 2 6 5 20
Numrul total de operanzi este semnificativ mai mare dect celelalte valori datorit utilizrii unora dintre acetia n apeluri repetate ctre subrutine pentru a implementa funcionaliti distincte. Valorile pentru metricile Halstead, n aceste condiii sunt: Metric
Lungimea programului Vocabularul programului Volumul Dificultatea Efort = = = = =
Valoare
25 8 25*log28 3,3 75,75*log28
Comparativ cu valorile obinute pentru abordarea clasic, acestea sunt mult inferioare, ceea ce dovedete o mai bun implementare a rezolvrii problemei, cu o complexitate i un efort mult diminuat prin reutilizarea subrutinelor din biblioteca de subprograme. Programarea standard se caracterizeaz, n principal, printr-o productivitate mult crescut.
79
Dup aceea se construiete procedura pentru calculul mediei aritmetice ponderate, cu textul surs:
float mediapond ( float x[], int f[], int n) { int i, sumf = 0; float sumxf = 0, xmed; for ( i = 0; i < n; i++ ) { sumf += f[i]; sumxf += x[i] * f[i]; } xmed = sumxf / sumf; return xmed; }
Dac se dorete creterea generalitii unui subprogram n sensul c acesta s permit fie calculul mediei aritmetice simple, fie calculul mediei aritmetice ponderate, se procedeaz la elaborarea textului surs:
float media ( float x[], int f[], int n, int k) { int i, sumf = 0; float sumxf = 0, xmed; if ( k == 0 ) { for ( i = 0; i < n; i++ ) { f[i] = 1; } } xmed = mediapond(x, f, n); return xmed; }
Utilizatorul iniializeaz variabila k pe zero dac dorete s calculeze o medie aritmetic simpl. Pentru a calcula media aritmetic ponderat variabila k se iniializeaz cu o valoare diferit de zero. Mai mult, dac se dorete calculul altor tipuri de medii (media geometric, media armonic) n acelai subprogram, se impune efectuarea de modificri adecvate. n al doilea rnd, generalitatea trebuie privit prin prisma acceptrii cazurilor particulare. Trebuie tratate distinct cazurile particulare. De exemplu, subprogramul care trateaz situaia n care suma frecvenelor din relaia este nul.
x=
x
i =1 n i =1
fi
i
80
Programarea standard
} ...............
}
Proiectantul bibliotecii trebuie s stabileasc outputurile subprogramelor pentru situaiile particulare; de cele mai multe ori subprogramul returneaz coduri asociate poziiilor unei liste de mesaje care trebuie afiate. Codurile trebuie testate pentru a determina fluxuri de prelucrare difereniate funcie de outputurile subprogramelor. n al treilea rnd, subprogramele conin teste suficient de puternice pentru a permite efectuarea de calcule. Expresia:
x=
n
x
i =1 n i =1
fi
i
pentru a fi evaluat corect trebuie ca fi >= 0, i = 1, 2, ..., n i n > 0. Aceste condiii trebuie avute n vedere de designerii de subprograme. Se ia n considerare atitudinea acestora fie de a lsa programatorul care dezvolt aplicaii folosind subprograme de bibliotec pentru a face validrile, fie de a le ncorpora n subprograme i de a furniza mesaje referitoare la modul n care s-a derulat prelucrarea. Astfel, se d subprogramul:
int mediapondt ( float x[], int f[], int n, float &xmed ) { int rezultat = 0; if ( n < 1 ) { rezultat = -1; } if ( contorless (f, n, 0) ) { rezultat = -2; }
81
Subprogramul returneaz 0 dac prelucrarea s-a efectuat complet i corect. Subprogramul returneaz -2 dac exist frecvene negative i returneaz -1 dac numrul de componente ale irului nu conine cel puin un element. n al patrulea rnd, generalitatea problemei crete atunci cnd subprogramele sunt construite pentru a accepta diferite tipuri de operanzi. Pentru produsul scalar a doi vectori de tip ntreg, se construiete subprogramul:
long int prodscalar( int x[], int y[], int n) { long int rezultat = 0; for ( int i = 0; i < n ; i++ ) { rezultat += x[i] * y[i]; } return rezultat; }
n programarea orientat obiect aceast problem de multiplicare a codului funcie de tipul operanzilor este soluionat folosind descriptorul template. Biblioteca de subprograme este construit sub form de componente interdependente. Dac se dorete soluionarea unei probleme, mai nti se analizeaz dac exist deja subprogram n bibliotec. n caz contrar, se identific subprogramele apte s rezolve problemele fiecrui pas al algoritmului. Dac acest aspect impune dezvoltarea de subprograme de
82
Programarea standard
baz, evident, acestea se elaboreaz. Din aproape n aproape se dezvolt o bibliotec de subprograme care, n al cincilea rnd, acoper prin diversitatea de prelucrri de baz, toate cerinele pentru a soluiona orice problem, generalitatea bibliotecii fiind apreciat n raport cu diversitatea de probleme.
Semnificaie
Prelucrare complet care a condus la rezultate corecte ntreruperea prelucrrilor nainte de a efectua o mprire la zero Un masiv are numr de componente negativ O variabil are valori n afara unui interval impus Argumentul unui logaritm sau al unui radical este negativ Parametrul are o valoare n afara elementelor enumerate Numele fiierului este necunoscut Elementul cu cheia specificat nu exist
83
Dac toate subprogramele sunt nzestrate cu variabila de stare ik n lista de parametrii, n programul apelator sau n subprograme se introduce o secven de program obligatorie de testare a ei. Continuarea prelucrrii se realizeaz dac i numai dac, variabila ik are la ieirea din procedur valoarea zero, aa cum se arat n figura 4.2.
84
Programarea standard
Listele de parametrii pentru subprogramele dintr-o bibliotec sunt extrase dintr-o list comun de nume, utilizatorii bibliotecii trebuind obligatoriu s cunoasc aceast list comun de nume. Mai mult, pentru subprograme care au prelucrri ce se refer la aceleai variabile, se impune ca listele de parametrii s fie identice. De exemplu, n tabelul 4.2 sunt date proceduri care au liste de parametrii identice ca structur i ca poziie a elementelor i ca nume pentru parametri. Proceduri cu liste identice de parametri Tabelul 4.2 Prototipul procedurii addmat (a, b, m, n, c) scadmat (a, b, m, n, c) prodmat (a, b, m, n, c) compartmat (a, b, m, n, c) Semnificaia adun matricele a, b, rezultatul este matricea c evalueaz expresia c = a b, unde a, b, c sunt matrice cu m linii i n coloane calculeaz elementele unei matrice c dup relaia cij = aij * bij compar elementele a dou matrice cij = 0 dac aij = bij cij = 1 dac aij > bij cij = -1 dac aij < bij Programarea standard i dovedete eficiena dac subprogramele sunt bine construite, avnd un grad de omogenitate maxim, nct programatorii s se recunoasc pe ei nii prin nivelul ridicat al performanei ncorporate n secvenele de texte surs. n cazul n care un programator identific secvene care prin nlocuirea cu altele mult mai performante schimb calitatea unei proceduri, ncrederea n bibliotec scade, programarea standard fiind periclitat.
85
reutilizare. Este important s fie cunoscute att bibliotecile, ct i componentele lor, adic subprogramele incluse n ele. Dac exist o problem P de rezolvat, n faza de analiz se stabilesc datele de intrare DI1, DI2, ...., DIn i rezultatele care trebuie obinute RI1, RI2, ...., RIm. n funcie de volum, de tip, de modul de regrupare, datele de intrare i rezultatele sunt puse n coresponden cu unele dintre structurile de date cunoscute. Rezult automat tipologia de probleme n raport cu operanzii de prelucrat. Dac sunt luate n considerare urmtoarele structuri de date: VE M1 M2 FS LS LD ST AB GR BB MR - variabile elementare - masiv unidimensional - masiv bidimensional - fiier secvenial - list simpl - list dubl - stiv - arbore binar - graf - arbore B - matrice rar
modulele sau subprogramele, n faza de proiectare sunt cu un grad de omogenitate ridicat, n primul rnd, dac includ operanzi de acelai tip, n proporie covritoare. De exemplu, n figura 4.3 e dat o structur de program apelator care include apel de subprograme cu grad maxim de omogenitate a structurilor de date utilizate.
86
Figura 4.3 Program apelator omogen din punct de vedere al structurilor de date
Stabilirea structurilor de date din lista de parametrii ai subprogramului determin care este biblioteca utilizabil, care sunt subprogramele care se reutilizeaz de ctre programator pentru a-i soluiona problema. Problema P se descompune n subproblemele P1, P2, ....., Pr. Problemei P i va corespunde programul apelator PA, figura 4.4. Fiecrei subprobleme i corespunde apelarea unui subprogram SP1(), SP2(), ...SPk(). Descompunerea subproblemei Pi n alte subprobleme Pi1, Pi2, ....., Piki impune includerea n subprogramul SPi() a apelurilor de subprograme SPi1(), SPi2(), ....SPiki(). Programarea standard impune ca structura arborescent s includ un numr de niveluri rezonabil, iar frunzele arborescenei sunt componente ale unei biblioteci de subprograme. Rezult c programatorul care stpnete programarea standard, trebuie s dezvolte o structur arborescent, trebuie s tie care sunt componentele bibliotecii, tehnologia bottom-up fiind cea mai nimerit de a construi soluia ca program a problemei. Astfel, pentru o problem P dat n figura 4.4 se construiete programul dat n figura 4.5.
87
Toate componentele de pe nivelul al doilea sunt subprograme de bibliotec. Programarea standard este programarea reutilizrii de subprograme. Activitatea de programare este reorientat spre a dezvolta subprograme reutilizabile, iar comunicarea ntre programatori este esenial. n momentul n care activitatea unui programator este analizat calculnd raportul dintre secvenele originale de instruciuni i lungimea programului, toat problematica efortului de programare, se rezolv de la sine. Secvenele originale se obin dintr-un program, dup ce se elimin toate secvenele pentru care exist subprograme de bibliotec. La un moment dat, programarea standard s-a constituit n factor progres, impunnd o component de baz n ingineria software, partea dedicat reutilizrii de subprograme.
88
ntr-un program, structura liniar apare sub forma unor instruciuni dispuse una dup cealalt; de exemplu secvena:
s = 0; i++; s = x[i] + x[i] * x[i];
formeaz o structur liniar; structura alternativ, care presupune o expresie condiional C1 i dou secvene S1 i S2; dac, dup evaluarea expresiei condiionale C1, se obine valoarea logic adevrat, se execut secvena S1, n caz contrar se execut secvena S2, figura 5.2:
89
90
Abordarea structurat
structura repetitiv sau de ciclare, care presupune o expresie condiional C i o secven S de program care se execut de un numr finit de ori, pn cnd condiia i schimb valoarea. O astfel de structur are una din formele: repetitiv cu test iniial, n care secvena S se repet atta timp ct condiia C este adevrat, apoi se execut secvena S`;
repetitiv cu test final, n care se execut secvena S, pn cnd condiia C devine adevrat, apoi se execut secvena S`;
repetitiv cu contor, care presupune existena unei variabile de control i, a unei valori iniiale a acesteia, vinit, a unei valori finale vfin si a unei raii r, precum i o secven S care se execut, iar variabilele incluse n ea depind de variabila de control i; schema logic corespunztoare este prezentat n figura 5.6;
91
Pentru problema de determinare a numrului de elemente pozitive, negative i respectiv nule dintr-o matrice, prezentat n capitolul al doilea, schema logic corespunztoare utilizrii n exclusivitate a celor trei tipuri de structuri este dat n figura 5.7.
92
Abordarea structurat
Figura 5.7 Schema logic pentru numrarea elementelor negative, pozitive i nule
Programarea structurat determin o ordonare a dezvoltrii succesiunii operaiilor, obligndu-i pe programatori s realizeze construcii ct mai clare.
93
Figura 5.8 Graf asociat unui program cu salturi napoi (A), cu salturi nainte (B) i cu salturi napoi i nainte (C) ntretiate
O utilizare frecvent a salturilor necondiionate este pentru implementarea structurilor repetitive, n lipsa unor construcii oferite de limbajul de programare. De asemenea, o alt utilizare apare n situaia n care se dorete creterea vitezei de execuie. Secvenele de cod cu salturi necondiionate sunt mai rapide, ns mai greu controlabile sub aspectul fluxului de execuie. Limbajele de nivel nalt doar includ printre cuvintele lor rezervate aceast instruciune, ns nu furnizeaz nici o implementare. Salturile necondiionate rmn apanajul limbajelor de asamblare i ale limbajelor de nivel mediu (C/C++), ns i la acestea din urm, utilizarea lor nu este recomandat, preferndu-se abordrile structurate. Programarea structurat este o tehnic n care programatorul nu trebuie s foloseasc instruciunea de salt GOTO. Cele trei tipuri fundamentale de structuri permit implementarea acestei cerine. Dac se consider limbajul de asamblare i se analizeaz procesul de compilare, se observ c instruciunea de salt necondiionat jmp este inevitabil.
94
Abordarea structurat
Structurii alternative din figura 5.2 i se asociaz secvena din figura 5.9.
Figura 5.9 Construcia secvenial asociat structurii alternative pentru limbajul de asamblare
Implementarea unei structuri alternative presupune operaii anterioare testrii indicatorilor de condiie, iar n cazul unui rezultat negativ al evalurii se execut un salt necondiionat la secvena ce trebuie executat n acest caz (corespondentul ntr-un limbaj de programare evoluat este varianta else a structurii if). i n cazul structurii repetitive, instruciunea jmp este inevitabil, figura 5.10.
95
Figura 5.10 Construcie secvenial asociat structurii repetitive pentru limbajul de asamblare
Limbajele de programare evoluate includ apeluri de funcii de bibliotec. De exemplu, n programul din figura 5.11, programul principal PROG1 apeleaz subprogramele calcul1, calcul2 i calcul3.
96
Abordarea structurat
Instruciunile call i ret conin i salturi necondiionate care definesc modificri ale registrului IP cu valori mai mari de 6 baii. Rezult c programarea fr GOTO este un deziderat i limbajele evoluate reuesc s mascheze apariiile de baz ale salturilor necondiionate.
97
Pentru a o transforma ntr-o structur n care s se regseasc doar structuri fundamentale, se duplic codul, obinndu-se urmtoarea schem:
n practic ns nu se opteaz pentru scrierea structurii de tip if-then folosind doar structuri fundamentale, pentru c, aa cum se observ n figur, cantitatea de cod duplicat este semnificativ, iar introducerea unei
98
Abordarea structurat
noi testri a condiiei C1 pe lng faptul c afecteaz performana codului (este tiut faptul c operaiile de comparare sunt costisitoare, din punct de vedere ale procesorului), afecteaz i lizibilitatea algoritmului. n cazul structurii repetitive condiionate posterior, se realizeaz tot o duplicare de cod, ns impactul asupra performanei i lizibilitii codului este mult mai mic. Considerndu-se schema din figura 5.5, ea se rescrie folosind doar structuri fundamentale n figura 5.14:
Se observ c se duplic doar blocul funcional S, ceea ce nu afecteaz deloc performana codului i ntr-o msur foarte mic lizibilitatea lui. Probleme sunt atunci cnd blocul S trebuie modificat, fiindc modificarea trebuie realizat n dou locuri. Atunci cnd un programator este format s lucreze conform cerinelor programrii structurate, construirea algoritmilor, construirea secvenelor de blocuri n schema logic se realizeaz din start pentru cele trei tipuri de structuri fundamentale, nefiind necesar conversia. n cazul n care programatorul are la dispoziie software mai vechi n care apare frecvent instruciunea GOTO trebuie s realizeze conversia de programe, adic s dezvolte astfel de construcii nct s fie eliminat instruciunea GOTO din program. Structurile de program ce conin instruciuni GOTO sunt greu de analizat, modificat, restructurat i testat. De aici i nevoia de a transforma aceste structuri de program n unele din care instruciunile GOTO s lipseasc.
99
Pentru transformarea programelor ce conin instruciuni GOTO n programe structurate, cea mai utilizat tehnic este cea a introducerii unei variabile de control. Se consider urmtoare structur posibil n cadrul unei aplicaii:
C1
Da
S1
C2
Da S2
C3
Da S3
Pentru structurarea secvenei de program din figura 5.15 se introduce variabila de control vb de tip boolean, care va fi iniializat cu valoarea true. Schema rezultat este prezentat n figura 5.16:
100
Abordarea structurat
Nu toate programele nestructurate au corespondent sub form de program structurat. Se observ c exist situaii, cum este cazul structurilor de tip if-then sau repetitive condiionate posterior, n care rescrierea lor folosind doar structuri fundamentale nu i are rostul. Este i motivul pentru care toate limbajele de programare accept construcii de acest fel, existnd chiar cuvinte cheie speciale, de exemplu, do-while. Nu acelai lucru se spune despre secvenele ce folosesc instruciuni GOTO, care trebuiesc eliminate din cod. Pentru programele deja scrise folosind GOTO, transformarea lor manual reprezint un efort semnificativ, motiv pentru care au fost
101
dezvoltate o serie de instrumente software cu ajutorul crora aceast transformare este automatizat.
102
Abordarea structurat
Rezult c pentru schema logic dat n figura 5.7 textul surs C++ este dat n figura 5.17.
#include <stdio.h> #define M #define N 10 10
int m, n, i, j, a[M][N]; int main() { printf("\nIntroducetie numarul de linii: "); scanf("%d", &m); printf("\nIntroduceti numarul de coloane: "); scanf("%d", &n); for ( i = 0; i < m; i++ ) { for ( j = 0; j < n; j++ ) { printf("\na[%d][%d] = ", i, j); scanf("%d", &a[i][j]); } } int nrplus int nminus int nrzero = 0; = 0; = 0;
for ( i = 0;i < m; i++ ) { for ( j = 0; j < n; j++ ) { if ( a[i][j] == 0 ) nrzero++; if ( a[i][j] > 0 ) nrplus++; if ( a[i][j] < 0 ) nrminus++; } } printf("\nNumarul de elemente pozitive este %d",nrplus); printf("\nNumarul de elemente negative este%d",nrminus); printf("\nNumarul de elemente nule este %d\n", nrzero); return 1; }
Figura 5.17 Codul surs n C++ pe baza schemei logice din figura 5.7
Aplicnd metricile de complexitate definite n capitolul Ciclul de dezvoltare software, se obine urmtoarele valori pentru complexitatea ciclomatic: CM =35 25 + 2 = 12
103
Analiznd variaia complexitii fa de abordarea clasic, unde implementarea a fost tot monolitic, se observ o cretere a complexitii pe fondul diminurii numrului de noduri din graf. Aceasta deoarece au fost folosite structuri repetitive ale limbajului i elaborate de programator. Prin urmare, mai multe instruciuni din programarea clasic au fost grupate ntr-una singur. Acest lucru ns nu a diminuat fluxul de execuie care rmne acelai, dovad numrul aproape identic de arce. Metricile Halstead nu difer foarte mult de valorile nregistrate pentru abordarea clasic, lucru normal, avnd n vedere se efectueaz aceleai operaii logice, pe aceiai parametrii. Comparnd cu abordarea standard, lucrurile stau total diferit. Datorit organizrii mult mai riguroase a codului, n librrii de subprograme, complexitatea programului scade foarte mult, el devenind o succesiune de apeluri de funcii din librrie. n situaia n care se iau n considerare i complexitile funciilor se observ o apropiere a valorilor, n condiiile n care nu s-a mers dect pe primul nivel de derivare, n cadrul bibliotecii de subprograme. Dac ns se realizeaz o implementare i n variant structurat, cu ajutorul mai multor subprograme, se constat o cretere a complexitii. Se construiete un subprogram care realizeaz citirea elementelor matricei de la tastatur i un subprogram care numr elementele din matrice pe baza rezultatului returnat de o funcie transmis ca parametru subprogramului de numrare. Aceast funcie presupune trei implementri pentru comparaiile de tip mai mare, egal i mai mic. Rezultatele obinute pentru metricile de complexitate devin n acest caz: CM =15 respectiv:
Metric Lungimea programului Vocabularul programului Volumul Dificultatea Efort = = = = = Valoare 83 40 356,5 20,08 2116,73
104
Abordarea structurat
Trebuie menionat c evaluarea a fost fcut prin cumularea valorilor obinute i la nivelul subprogramelor, la fel ca n cazul programrii standard. Rezult c, n general, introducerea de subprograme crete complexitatea programului. Avantajul principal este legat de reutilizare. n plus, dac subprogramele nu sunt dezvoltate n cadrul aceluiai proiect, ele nu particip la calculul complexitii, i atunci valoarea acesteia este foarte mic. Programarea structurat reprezint o tehnic de programare care ajut la dezvoltarea de software cu un design i un flux de execuie clare i prin care se asigur un grad ridicat de modularitate a produsului. Eliminarea instruciunii GOTO din programe folosind diverse metode, unele prezentate i n cadrul acestui capitol, ajut la eliminarea unor categorii de erori de programare foarte grave i la mbuntirea calitii codului. Trebuie menionat c aceast tehnic de programare este fundamentat tiinific, prin teoremele de structur, spre deosebire de alte tehnici anterioare ei, care nu posed un astfel de fundament.
105
106
Programarea modular
n notaia din figura 6.1, indicele inferior arat numrul modulului, iar indicele superior arat nivelul pe care se afl modulul n structura arborescent asociat produsului software. Datele de ieire ale unui modul de pe nivelul j, reprezint datele de intrare pentru unul sau mai multe module de pe nivelul j-1. Proiectantul sistemului software abordeaz ntreaga problematic de la elementele concrete spre elementul de sintez, care se afl la nivelul superior. Se construiete soluia sub form de structur arborescent pornind de la ultimul nivel de descompunere frunzele arborelui, ctre rdcin. Asamblarea se efectueaz de la nivelul j+1 spre nivelul j, continundu-se de la nivelul j spre nivelul j1 pn la nivelul 0 ce corespunde rdcinii. O astfel de abordare permite construcia produsului pornind de la funciile primitive, specifice domeniului problemei, spre funciile complexe ce implementeaz logica aplicaiei; tehnica este potrivit pentru situaii n care specificaiile nu sunt clare, i de asemenea, asigur o mai bun organizare a codului surs. Modulele de pe nivelurile inferioare sunt asamblate n unul sau mai multe module de pe nivelurile superioare. De exemplu, un set de funcii pentru accesul la fiiere este folosit att ntr-un modul de salvare a datelor manipulate de aplicaia software, ct i ntr-un modul de prelucrare care necesit lucrul cu fiiere temporare.
P=M10
M11
M21
M3 1
..
M1p-1
M2p-1
...
Mmp
M 1p
M2p
M3p
...
Mnp
De exemplu, pentru problema de calcul matriceal aleas pentru ilustrarea particularitilor tehnicilor de programare, soluia prin tehnologia bottom-up presupune: cunoaterea exact a rezultatelor obinute: numrul componentelor negative, numrul componentelor pozitive i numrul componentelor nule; cunoaterea exact a condiiilor pe care matricea trebuie s le ndeplineasc: matricea este simetric. Printr-o abordare bottom-up, programatorul identific, n primul rnd, funciile primitive: citirea informaiilor de la tastatur, afiarea informaiilor pe ecran, compararea a dou numere pentru care rezultatul are valoarea adevrat sau fals. Aceste funcii se vor gsi pe nivelul de baz a structurii. Pornind de la aceste primitive, se construiesc pe nivelul urmtor, modulul de citire a dimensiunilor i elementelor matricei, modulul de afiare a rezultatelor i a unor mesaje de informare sau de eroare dintr-o list asociat. Pe nivelul al treilea de la baz, apar modulele de stabilire a simetriei matricei, de determinare a minimului i a maximului, precum i de numrare a elementelor pozitive, negative i nule din matrice. Aceste module se bazeaz pe funciile de comparare a dou numere, de afiare de rezultate i afiare de mesaje. Pe nivelul rdcin, se apeleaz aceste module conform fluxului cerut de problem. nseamn c structura arborescent are 4 niveluri, (figura 6.3).
Programarea modular
Se impun urmtoarele observaii referitoare la figura 6.3: un modul de pe un nivel inferior, cum este cazul modului de Afiare mesaje din lista de mesaje s fie implicat n construirea mai multor module de pe nivelurile superioare; un modul de pe un nivel inferior, cum este cazul modulului Compararea a dou numere particip la construcia unui alt modul care nu se afl neaprat pe urmtorul nivel n ierarhie. Rezult c, structura nu este arborescent n proporie de 100%, ea permind ca un nod copil s aib mai mult de un nod printe. ns caracterul ierarhic se menine, prin faptul c modulele sunt dezvoltate ncepnd cu cele primitive, de baz, i terminnd cu cele complexe, respectiv cu produsul final ca ultim modul, iar acestea din urm au n construcia lor apeluri ctre modulele de baz. Se construiete tabelul 6.1 care evideniaz faptul c datele de intrare ale unui modul de pe nivelul k sunt ieiri ale modulului de pe nivelul k 1. Corelaia nivel mod de utilizare variabile Tabelul 6.1
Nivel Modul c1 c2 c3 c4 c5 c6 c7
3 3 3 2 2 2 1 1 1 0
citire informaii afiare informaii comparare numere citire matrice afiare rezultate afiare mesaje contorizare maxim/minim simetrie main()
I E I I I I/E
E I I I I/E
E I/E
Semnificaia coloanelor c1 c7 este prezentat mai jos. c1 c2 c3 c4 c5 c6 c7 nrplus nrnegativ nrnull text matricea
dimensiuni matrice
max/min
109
Avantajul acestei abordri este dat de faptul c programatorul rezolv pri ale problemei, ofer rezultate intermediare i pe msur ce poate asambla module, obine n final produsul finit. Dezvoltarea bottom-up corespunde proceselor industriale care realizeaz repere, prin asamblare se obin subansambluri, iar la cel mai nalt nivel se obine produsul finit.
SP1 reprezint subproblema corespunztoare iniializrii datelor; SP2 reprezint subproblema de prelucrare a datelor; SP3 reprezint subproblema de afiare a rezultatelor.
110
Programarea modular
Dezvoltarea top-down presupune efectuarea unor detalieri pentru fiecare subproblem, dup cum urmeaz: S-SP11 corespunde iniializrii dimensiunilor matricei, care este subproblem a lui SP1; S-SP12 corespunde iniializrii matricei, care este subproblem a lui SP1; S-SP13 corespunde alegerii minimului dintre numrul de linii i numrul de coloane ale matricei pentru a stabili dimensiunea matricei pentru care se analizeaz simetria.
6.5.
Pentru efectuarea prelucrrilor, subproblema SP2 este descompus n subprobleme dup cum urmeaz: S-SP21 corespunde testrii caracterului simetric dup diagonala principal; S-SP22 corespunde testrii caracterului simetric dup diagonala secundar; S-SP23 corespunde testrii simetriei dup coloana din mijloc; S-SP24 corespunde testrii simetriei dup linia din mijloc; S-SP25 corespunde aflrii elementului minim din matricea simetric; S-SP26 corespunde aflrii elementului maxim din matricea simetric S-SP27 corespunde contorizrii.
111
Afiarea rezultatelor impune descompunerea subproblemei SP3 astfel: S-SP31 corespunde afirii elementului minim; S-SP32 corespunde afirii elementului maxim; S-SP33 corespunde afirii numrului de elemente nule, pozitive, respectiv negative; S-SP34 corespunde afirii de mesaje.
S-SP31
S-SP32
S-SP33
S-SP34
Aceast descompunere corespunde situaiei n care problema este foarte bine neleas, experiena analitilor i designerilor permit asocierea unei structuri arborescente clare, iar duplicarea de cod este controlat. Pentru aceast abordare se procedeaz astfel: se scrie programul apelator SP0, definind variabilele de baz i apelurile la procedurile corespunztoare lui SP1, SP2, SP3; n faza de analiz sunt clarificate numrul de niveluri, inputurile corespunztoare fiecrui nivel;
112
Programarea modular
se scriu procedurile SP1, SP2, SP3, n fiecare dintre ele, incluzndu-se apelurile de proceduri de tipul S-SPij, i = 1,2,3 j = 1,2, ...., ni; se scriu procedurile corespunztoare frunzelor structurii arborescente.
Produsul se dezvolt de la ntreg spre parte, ceea ce confer designerului resurse suficiente pentru a obine un produs complet, prin adugri de proceduri la fiecare nivel, dac este cazul.
Complexitatea unui modul este rezultatul analizei contextului i filosofiei adoptate de ctre echipa de proiectani software. Dac se urmrete un nivel de complexitate ridicat pentru module, structura arborescent se organizeaz pe un numr sczut de niveluri. Modulele complexe se afl pe acelai nivel, funcie de raportul cu prinii, respectiv descendenii si. Dac se adopt ipoteza ca modulele s aib un nivel de complexitate redus, structura arborescent are un numr de niveluri ridicat. n acest context, pentru problema de calcul matriceal, n ipoteza un modul = un subprogram, lista subprogramelor este prezentat n continuare: citire_dim() - citete dimensiunile matricei de la tastatur; citire_mat() - citete elementele matricei de la tastatur; simetrie1() stabilete simetria matricei fa de diagonala principal; simetrie2() stabilete simetria matricei fa de diagonala secundar; contorplus() numr elementele pozitive dintr-o matrice; contorminus() numr elementele negative dintr-o matrice; contornul() numr elementele nule ale unei matrice; minim() identific minimul elementelor unei matrice; maxim() identific maximul elementelor unei matrice; afiseaza() afieaz rezultatele prelucrrilor; mesaj() afieaz mesaje specifice ale aplicaiei. Dac se procedeaz la regruparea ntr-un modul a mai multor subprograme, structura pentru aceeai problem este dat n figura 6.9.
Figura 6.9 Structura aplicaiei, dup gruparea mai multor subprograme ntr-un singur modul
114
Programarea modular
Se urmrete realizarea unui echilibru ntre numrul de module i dimensiunea aplicaiei. O aplicaie simpl, dar cu un numr mare de module nu face dect s creasc complexitate. Pe de alt parte, o aplicaie complex nu foarte bine delimitat pe module, afecteaz la rndul ei complexitatea, prin faptul c influeneaz alte caracteristici de calitate cum sunt mentenabilitatea sau reutilizabilitatea. Modulele din figura 6.9, sunt transpuse n codul surs, n limbajul C++, sub forma unor perechi de fiiere cu extensiile .h respectiv .cpp. Astfel, exist citire.h i citire.cpp, validare.h i validare.cpp, prelucrare.h i prelucrare.cpp, respectiv afisare.h i afiare.cpp. Codul surs pentru operaiile de citire este dat n figura 6.10 (citire.cpp).
void citire_dimensiuni(int *m, int *n) { printf("Introduceti numarul de linii ale matricei = "); scanf("%d", m); printf("\nIntroduceti numarul de coloane ale matricei = "); scanf("%d", n); } void citire_mat(int a[][N], int m, int n) { for (int i = 0; i < m; i++ ) { for (int j = 0; j < n; j++) { printf("a[%d][%d] = ", i, j); scanf("%d", &a[i][j]); } } }
for (int j = 0; result && j < n; j++ ) { if ( a[i][j] != a[m-i-1][n-j-1] ) { result = 0; } } } return result; }
Pentru modulul de prelucrri, implementare n C++ este dat n figura 6.12 (prelucrare.cpp).
int contor(int a[][N], int m, int n, int prag, int(*func)(int, int)) { int contor = 0; for ( int i = 0; i < m; i++ ) { for ( int j = 0; j < n; j++ ) { if ( func(a[i][j],prag) > 0) { contor++; } } } return contor; }
int minim (int a[][N], int m, int n) { int min = a[0][0]; for ( int i = 0; i < m; i++ ) { for ( int j = 0; j < n; j++ ) { if ( min > a[i][j] ) min = a[i][j]; } } return min; } int maxim (int a[][N], int m, int n) { int max = a[0][0]; for ( int i = 0; i < m; i++ ) { for ( int j = 0; j < n; j++ ) { if ( max < a[i][j] ) max = a[i][j]; } } return max; }
Programarea modular
n final, codul surs pentru modulul de afiare mesaje i rezultate este dat n figura 6.13 (afisare.cpp)
void afiseaza(char* mesaj, int result) { printf("%s %d\n", mesaj, result); } void mesaj(int cod) { switch(cod) { case 1: printf("Matricea este simetrica!\n"); break; case 2: printf("Matricea nu est simetrica!\n"); break; } }
n aceste condiii, programul care determin numrul de elemente pozitive, negative i nule din cadrul unei matrice citit de la tastatur, i care este scris folosind modulele descrise anterior, are urmtorul cod surs:
int maimare(int a, int b) { return a > b; } int maimic(int a, int b) { return a < b; } int egal( int a, int b) { return a == b; } int main() { int a[M][N], m, n; citire_dimensiuni(&m, &n); citire_mat(a, m, n); //numara elementele pozitive, negative si nule afiseaza("Numarul de elemente pozitive este", contor(a, m, n, 0, maimare)); afiseaza("Numarul de elemente negative este", contor(a, m, n, 0, maimic)); afiseaza("Numarul de elemente nule este", contor(a, m, n, 0, egal)); return 1; }
117
Cele trei funcii maimare, maimic, egal sunt transmise ca parametrii funciei de contorizare, n aa fel nct aceasta din urm capt un grad ridicat de generalitate, putnd numra orice tip de element din matrice care trebuie s respecte o condiie, condiie stabilit prin funcia transmis ca parametru. Programarea modular se bazeaz tot pe filozofia programrii structurate. Aceasta din urm permite divizarea programelor n module ce sunt implementate i testate pe calculator n mod independent. Abordarea modular, fie ca este bottom-up sau top-down, nu aduce un plus de complexitate, ci doar reorganizeaz acelai cod surs rezultat din programarea structurat. Prin urmare, pentru exemplul considerat, valorile complexitii obinute la abordarea structurat, pentru cazul folosirii de funcii n implementarea soluiei problemei i nu n abordarea tip monolit, rmn valabile i pentru programarea modular. Exist o component a complexitii care se modific, i anume, cea cognitiv, pentru c, prin descompunerea programului n module, cu funcionaliti clare, bine delimitate, crete inteligibilitatea codului i prin urmare el devine mai uor de neles de ctre programator, chiar dac, n esen este acelai cod. Aceast component ns nu se reflect n nici o msur cantitativ a complexitii.
6.4 Parametrizarea
La dezvoltarea programelor trebuie s se ia n considerare modificri ce vor apare n timp, la nivelul: datelor de intrare, n sensul introducerii de cmpuri, modificrii de domeniu, creterii dimensiunii i volumului de date, schimbrii criteriilor de cutare; formulelor de calcul, prin adugarea, eliminarea i nlocuirea de operatori i de operanzi, prin adugarea i eliminarea de formule; rezultatelor, prin adugarea de noi rezultate care trebuie obinute, prin schimbarea coninutului unor rezultate existente i prin eliminarea unora dintre acestea.
Dac structura produsului software este gndit rigid, orice modificare la nivel conceptual se reflect prin succesiuni de modificri n textul surs, modificri caracterizate prin efecte de antrenare multipl, imprevizibile. De
118
Programarea modular
aceea, trebuie ca programele s fie concepute astfel nct s preia toate modificrile prin introducerea unor parametri de ctre utilizatorul produsului software. De exemplu, n matricea pentru care se efectueaz prelucrri, trebuie terse unele linii, respectiv, unele coloane. Dac programul este construit rigid, trebuie definite dou proceduri, una de tergere linie din matrice i alta de tergere coloan, iar programatorul trebuie s le activeze n program, dup ce s-a fcut citirea matricei iniiale din fiier. O alt variant const n definirea nc de la nceputul elaborrii produsului software a doi vectori, unul pentru gestionarea liniilor, iar cellalt pentru gestionarea coloanelor, care vor avea valorile 1, dac linia, respective coloana este activ i 0 n caz contrar. Dac programul este conceput rigid, iar la un moment dat se impune ca operaiile pe matrice s nu ia n considerare una din simetrii, trebuie ca subprogramele de testare a simetriei s fie scoase, necesitnd operaii n cascad asupra produsului. Proiectarea flexibil va impune un parametru pe care l iniializeaz utilizatorul cu 1 dac se lucreaz pe matrice simetric i 0 dac se lucreaz pe toat matricea. n cazul unor evaluri de expresii, se introduc coeficieni, care prin iniializare cu -1 sau cu 1, vor determina evaluri de expresii diferite, iar ali coeficieni iniializai cu 1 sau 0 vor extinde sau vor restrnge formulele. De exemplu, pentru evaluarea expresiilor: E1 = a + b + c + d + h + g + p E2 = a + b - c + d - h + g - p E3 = a - b - c - d E4 = a + b + d + h + p E5 = a - b + c - d + h + g p se procedeaz astfel: variabilele a, b, c, d, h, g, p sunt memorate n componentele x[0], x[1], x[2], x[3], x[4], x[5], respectiv, x[6]; se construiete vectorul k[7], care se iniializeaz cu +1, dac termenii se adun sau cu -1, dac termenii se scad; se construiete vectorul s[7] care se iniializeaz cu 1 dac variabila particip la evaluarea expresiei, respectiv cu 0, n caz contrar;
119
Utilizatorul produsului software nu are altceva de fcut dect s iniializeze vectorii k[] i s[]. Tabelul 6.2 conine valorile celor doi vectori pentru cele cinci expresii. Iniializrile variabilelor de structur ale expresiei Tabelul 6.2
a b c d h g p a b c d h g P k[0] k[1] k[2] k[3] k[4] k[5] k[6] s[0] s[1] s[2] s[3] s[4] s[5] s[6] E1 +1 E2 +1 E3 +1 E4 +1 E5 +1 +1 +1 -1 +1 -1 +1 -1 -1 +1 +1 +1 +1 -1 +1 -1 +1 -1 +1 +1 +1 +1 +1 +1 +1 +1 +1 -1 +1 +1 -1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 0 +1 +1 +1 +1 +1 +1 +1 +1 0 +1 +1 +1 +1 0 0 +1 +1 +1 0 +1 +1
n cazul n care apar probleme de stabilire a apartenenei la intervale i prin program se definesc n instruciuni if limitele intervalelor, ori de cte ori se modific limitele, se modific numrul de intervale, trebuie operat n program. De exemplu, pentru evaluarea expresiei a, dac x < -1 2 a , dac x [1, 1] e= 3 a , dac x (1, 7] a 4 , n rest secvena de program este
if ( x < -1 ) { e = a; } else if ( x <= 1 ) { e = a * a; } else if ( x <= 7 ) { e = a * a * a; } else { e = a * a * a * a; } ............
120
Programarea modular
Se accept ideea introducerii de la tastatur a numrului K de intervale, iniializarea a doi vectori cu limitele inferioare i limitele superioare ale K intervale. Se stabilete o modalitate de preluare a expresiilor ce trebuie calculate. Ori de cte ori se modific numrul de intervale K i limitele intervalelor, utilizatorul modific parametrii. Nu se intervine asupra textului surs. Programarea modular presupune existena unor subprograme cu caracteristici de calitate excelente, destinate execuiei unor operaii de baz. Subprogramele acestea se ncorporeaz n module, permind creterea gradului de complexitate a prelucrrilor, meninndu-se nivelul ridicat al performanei.
121
profunde a problemei, comportamentul, adic ceea ce ateapt utilizatorul s fac aplicaia pentru el, este descris nc de la nceput, n termeni cu semnificaie att pentru programatori ct i pentru client. Prima aciune a echipei de proiectare software este de a rafina i clarifica specificaiile iniiale ale clientului, care, de cele mai multe ori sunt neclare i incomplete. De asemenea, trebuie avut n vedere posibilele modificri de specificaii ce apar ulterior i care afecteaz produsul n procesul de dezvoltare. De aceea, structura produsului software trebuie gndit n aa fel nct impactul modificrilor de specificaii s fie minim. Ingineria software este simplificat prin identificarea i dezvoltarea de componente software. O component este o entitate abstract care realizeaz anumite aciuni, adic ndeplinete anumite responsabiliti. n fazele iniiale ale procesului de dezvoltare nu este important s se cunoasc reprezentarea exact a unei componente sau cum realizeaz o anumit aciune. n final, o component se concretizeaz ntr-o funcie, structur sau clas, sau o colecie de alte componente. Sunt importante dou caracteristici: o component trebuie s aib un set redus bine definit de responsabiliti; interaciunea dintre componente trebuie s fie minim. Identificarea componentelor se realizeaz n momentul n care se imagineaz procesul de execuie a sistemului; asta nseamn stabilirea aciunilor efectuate i identificarea entitilor care le efectueaz. Orict de bine este condus procesul de specificare a cerinelor i de proiectare a produsului software, ntotdeauna este nevoie s se intervin ulterior n formularea cerinelor i n structura produsului, datorit unor noi cerine ale utilizatorului sau rafinrii unora deja existente. Programatorii i designerii software trebuie s anticipeze aceste situaii i s minimizeze impactul lor. Printre aciunile pe care trebuie s le ntreprind pentru aceasta, se numr: obiectivul principal este minimizarea numrului de componente afectate de o schimbare de cerine. Chiar i n cazul schimbrilor majore, acesta nu trebuie s afecteze prea multe seciuni de cod; identificarea nc de la nceput a codului surs cel mai predispus n viitor la schimbri i izolarea efectelor acestor schimbri; cele mai frecvente modificri in de interfee, de formatele de comunicare, de formatele rezultatelor;
123
reducerea cuplrii dintre componente, ceea ce reduce dependena dintre ele i crete posibilitatea modificrii uneia cu implicaii minime asupra celeilalte.
Pe lng comportament, componentele conin i informaii. O component reprezint o pereche constituit din comportament i stare. comportamentul unei componente este setul de aciuni pe care le realizeaz; descrierea complet a comportamentului unei componente se mai numete i protocol. starea unei componente reprezint toate informaiile memorate n interiorul ei. Nu este necesar ca toate componentele s menin informaii de stare, ns majoritatea componentelor vor fi o combinaie de comportament i stare. Dou concepte importante folosite n proiectarea componentelor software sunt cuplarea i coeziunea. Coeziunea reprezint msura n care responsabilitile unei singure componente formeaz un tot semnificativ. O coeziune ridicat este obinut prin asocierea aciunilor care sunt relaionate dintr-un anumit punct de vedere, ntr-o singur component. Cuplarea descrie relaia dintre componentele software. n general, este de dorit minimizarea dependenelor dintre componente, din moment ce acestea afecteaz reutilizarea codului, uurina n modificare i dezvoltare. n particular, cuplarea este crescut atunci cnd o component software trebuie s acceseze informaiile de stare ale altei componente. Aceste situaii trebuie evitate, o modalitate este ca aciunea respectiv s fie transferat ca responsabilitate componentei care memoreaz informaia de stare dorit. Atunci cnd o component dezvoltat de un programator este utilizat de un altul, este obligatoriu ca acesta din urm s tie cum s o foloseasc i mai puin s tie cum a fost implementat. Folosirea unei componente presupune cunoaterea responsabilitilor pe care le are, adic a aciunilor disponibile i nu cum aceste aciuni sunt implementate. Aceasta reprezint separarea dintre interfa i implementare. Separarea este necesar din mai multe motive: ascunderea detaliilor de implementare ine de o caracteristic fundamental a programrii orientate obiect, i anume ncapsularea, deoarece nu este relevant i nici nu ar trebui s intereseze pe un programator care folosete o component cum sunt implementate funcionalitile sale;
124
implementarea comportamentului unei componente se schimb; atta timp ct interfaa expus pentru celelalte componente nu se schimb, impactul modificrii implementrii este minim. De exemplu, exist o component folosit pentru salvarea unor informaii pe disc; o implementare iniial este salvarea ntr-un fiier binar; ulterior ns, se dorete salvarea ntr-o baz de date sau un fiier xml. Modificarea modului n care se salveaz datele nu trebuie s afecteze implementarea celorlalte componente care utilizeaz aceast funcionalitate.
Implementarea componentelor software se face prin intermediul construciilor de tip clas, puse la dispoziie de limbajele de programare. Nu toate limbajele de programare sunt 100% orientate obiect. De exemplu, limbajul C++ permite n continuare construcii din limbajul C, care nu sunt orientate obiect: funcii definite n afara claselor, variabile globale etc.
7.2 Clasele
Programarea orientat obiect aduce n plus fa de celelalte construcii ale altor tehnici de programare, un conglomerat numit clas. n [MSPCD97], clasa este definit ca o categorie generalizat ce descrie un grup de elemente particulare, numite obiecte, care fac parte din ea. O clas reprezint un instrument de descriere utilizat n programare pentru a defini o entitate sub cele dou aspecte: al caracteristicilor sale, definite prin atributele clasei i al comportamentului su, definit prin metodele clasei. Prin comportamentul unei entiti se nelege modul n care aceasta reacioneaz la interaciunea cu alte entiti. Definirea unei clase se realizeaz prin: precizarea operanzilor sau a atributelor; funciile membre; proprietile asociate componentelor pentru a stabili modaliti de referire. O clas este un tip de dat definit de utilizator printr-o construcie de forma:
class nume { element 1; element 2; . element n; }
125
Un element este fie variabil de tip fundamental sau de tip derivat, n cazul atributelor, fie respectiv o funcie. Rezult c o clas se descrie sub forma:
class nume { tip1 variabila1; tip2 variabila2; . tipn variabilan; tip1 functie1 ( lista parametrii 1 ); tip2 functie2 ( lista parametrii 2 ); . tipm functiem ( lista parametrii m ); }
Clasele difer de structurile de tip articol prin faptul c elementelor din componen li se asociaz restricii de referire cu ajutorul specificatorilor de control. Aa cum variabilele n programe sunt de tip global i local sau dup modul de alocare sunt statice i dinamice, tot astfel, pentru a defini o anumit disciplin n manipularea claselor se asociaz grupurilor de elemente din clas un specificator de control al crui cmp de aciune este anulat de un altul. Specificatorii de control sunt: public ceea ce nseamn c elementul respectiv este accesibil oricrei clase externe; private nseamn c numai elementele componente ale aceleai clase au dreptul de a accesa acest membru; protected prin care disponibilitatea elementului include disponibilitatea specificatorului private, i, n plus, asigur disponibilitatea elementului pentru elementele ce urmeaz a fi definite n clasele derivate din clasa curent.
Funciile membru ale clasei refer oricare dintre variabilele definite n clas. La proiectarea unei clase se au n vedere urmtoarele aspecte: funciile membru s acopere ntreaga gam de prelucrri; s fie definite ct mai multe forme de iniializare a operanzilor; ntre variabilele i funciile membre s fie o concordan perfect pentru a nu apare erori n execuia programelor ce folosesc clase.
126
Programarea orientat obiect este tehnica de programare fundamentat pe conceptul de reutilizare software. Pentru a atinge acest obiectiv trebuie ca: procedurile s efectueze prelucrri complete i corecte; referirea procedurilor s se efectueze rapid i uor, fr a fi nevoie de informaii inutile din moment ce funciile membre utilizeaz variabile definite n aceeai clas; manipularea claselor s conduc la obinerea de noi clase, iar gradul de generalitate s fie ct mai ridicat. De exemplu, dac se dorete implementarea calculului cu numere complexe, se definete clasa Complex care trebuie s aib o structur de tip articol pentru a grupa partea real i partea imaginar a numrului complex i s conin funcii membre pentru operaiile de adunare, scdere, nmulire, mprire, ridicare la putere i extragere de radical. Aceast clas se definete n secvena de surs:
class Complex { private: struct compl { float real; float img; } c; public: void aduna ( Complex& cpl ) { c.real = c.real + cpl.c.real; c.img = c.img + cpl.c.img; } void scade ( Complex& cpl ) { c.real = c.real - cpl.c.real; c.img = c.img - cpl.c.img; } void produs ( Complex& cpl ) { c.real = c.real * cpl.c.real - c.img * cpl.c.img; c.img = c.real * cpl.c.real + c.img * cpl.c.img; } void divide ( Complex& cpl ) { float temp; temp = cpl.c.real * cpl.c.real - cpl.c.img * cpl.c.img;
127
Analiza comparat a complexitii entitilor text generate prin tehnici de programare c.real = ( c.real * cpl.c.real - c.img * cpl.c.img ) / temp; c.img = ( c.real.*cpl.c.img - c.img * cpl.c.real ) / temp; } .. }
Aa cum sunt definite funciile membru ale clasei Complex, au asociat specificatorul de control public. Clasele sunt bine construite dac la elaborarea unui program principal apar numai referiri de funcii membre din clase.
Destructorii sunt metode ale clasei referii nainte de a se produce dealocarea zonei de memorie asociate elementelor clasei. O clas nu are dect un singur destructor. Destructorul este o procedur care se definete prin numele clasei, fr a avea list de parametrii, fiind precedat de operatorul ~; pentru clasa Complex destructorul are forma ~Complex().
128
Att constructorii, ct i destructorii nu au tipuri asociate. Forma constructorilor variaz de la o clas la alta, n funcie de specificul fiecreia, dar i de experiena i obiectivele proiectantului clasei. Exist o form de constructor, numit constructor de copiere, foarte util n special atunci cnd se transmit obiecte ca parametrii prin valoare. Constructorul de copiere primete ca unic parametru o referin la un alt obiect al aceleai clase. Implementarea cea mai frecvent presupune copierea efectiv a valorilor atributelor obiectului transmis ca parametru n atributele obiectului nou. De exemplu, pentru clasa Complex se identific urmtorii constructori pentru iniializarea: cu variabile corespunztoare membrilor structurii care stocheaz atributele numrului complex; cu un alt obiect din aceeai clas.
Complex ( float a, float b ) { c.real = a; c.img = b; } Complex ( Complex& cpl ) { c.real = cpl.c.real; c.img = cpl.c.img; }
7.4.1
ncapsularea
Este rezultatul regruprii operanzilor i funciilor membre ntr-un conglomerat numit clas. Operanzii i operatorii clasei interacioneaz, formnd un tot unitar. De exemplu, se consider clasa Matrice. ncapsularea revine la a defini operanzii care se refer la numrul de linii, numrul de coloane i elementele matricei n clas, precum i funciile care efectueaz operaiile de calcul, formnd un tot unitar. Funciile folosesc direct operanzii ntruct
129
prin ncapsulare este dreptul lor de a referi ceva definit pe un domeniu comun. n limbajul FORTRAN instruciunea COMMON avea menirea de a implementa germeni ai ncapsulrii asociind zone de memorie grupate unor operanzi, fr a mai fi nevoie de a-i nscrie n lista de parametrii exact ca n cazul ncapsulrii propriu-zise. Pentru efectuarea operaiilor de numrare elemente pozitive, negative i nule, precum i pentru testarea simetriei matricei lista parametrilor este nul, deoarece toate informaiile necesare sunt deja definite n cadrul clasei i folosite ca atare, nemaifiind nevoie s fie transmise ca parametrii, codul surs al clasei Matrice este:
class Matrice { protected: int nrCol; int nrLinii; int mat[M][N]; public: Matrice( int m, int n ) { nrCol = m; nrLinii = n; for ( int i = 0; i < m; i++ ) { for ( int j = 0; j < n; j++ ) { mat[i][j] = 0; } } } Matrice( int a[][N], int m, int n ) { nrCol = m; nrLinii = n; for ( int i = 0; i < nrLinii; i++ ) { for ( int j = 0; j < nrCol; j++ ) { mat[i][j] = a[i][j]; } } } Matrice( Matrice& m ) { nrCol = m.nrCol; nrLinii = m.nrLinii; for ( int i = 0; i < nrLinii; i++ ) { for ( int j = 0; j < nrCol; j++ ) { mat[i][j] = m.mat[i][j]; } } } bool simetrie1() { bool result = true; if ( nrCol != nrLinii ) {
130
} int contorminus() { int contor = 0; for ( int i = 0; i < nrLinii; i++ ) { for ( int j = 0; j < nrCol; j++ ) { if ( mat[i][j] < 0 ) { contor++; } } } return contor; } int contornul() { int contor = 0; for ( int i = 0; i < nrLinii; i++ ) { for ( int j = 0; j < nrCol; j++ ) { if ( mat[i][j] == 0 ) { contor++; } } } return contor; }
131
7.4.2 Motenirea
Este o proprietate deosebit de important, pe baza ei sunt construite clasele din aproape n aproape, organizndu-se pe niveluri de agregare. Clasele agregate de pe nivelul k preiau operanzii i funciile membre ale claselor de pe nivelul k-1, care intr prin derivare n componena lor, precum i proprietile acestora. De exemplu, dac se dorete mbogirea clasei Matrice cu funcii pentru determinarea minimului i maximului elementelor din cadrul matricei, se construiete o nou clas Matrice2, unde, pe lng funciile deja existente ale clasei se adaug noile metode pentru aflarea minimului, respectiv, maximului, clasa obinut avnd codul surs:
class Matrice2 : public Matrice { public: int minim () { int min = mat[0][0]; for ( int i = 0; i < nrLinii; i++ ) { for ( int j = 0; j < nrCol; j++ ) { if ( min > mat[i][j] ) { min = mat[i][j]; } } } return min; } int maxim () { int max = mat[0][0]; for ( int i = 0; i < nrLinii; i++ ) { for ( int j = 0; j < nrCol; j++ ) { if ( max < mat[i][j] ) { max = mat[i][j]; } } } return max; } };
Avantajele folosirii motenirii sunt: reutilizabilitatea cnd o funcionalitate este motenit din alt clas, codul respectiv nu trebuie rescris, el trebuie doar apelat n noul context; o alt implicaie este legat de fiabilitatea codului,
132
deoarece prin motenire, o anumit funcionalitate este scris doar la nivelul unei clase i apoi motenit i utilizat n toate clasele derivate; consistena interfeei cnd dou sau mai multe clase sunt derivate din aceeai clas printe, se asigur faptul c comportamentul motenit este acelai pentru toate clasele; componentele software motenirea d posibilitatea programatorilor s construiasc componente software reutilizabile i gruparea lor n biblioteci; n acest fel, efortul de dezvoltare al unui produs nou este diminuat prin utilizarea de librrii cu funcionalitate deja implementat; dezvoltarea rapid de prototipuri atunci cnd sistemul software este construit folosindu-se componente reutilizabile, timpul de dezvoltare este concentrat pe nelegerea elementelor specifice ale sistemului; astfel se construiesc versiuni de sistem, numite prototipuri, care pun accent pe aspectele critice ale sistemului. Un prototip este dezvoltat, utilizatorii l folosesc, iar a doua versiune a sistemului este realizat pe baza experienei acumulat cu prima i a feedbackului de la utilizatori.
Dei motenirea prezint foarte multe avantaje, exist i o serie de costuri de care trebuie s se in seama n momentul proiectrii ierarhiilor de clase: viteza de execuie este influenat prin prisma faptului c metodele motenite, care au un caracter mai general, sunt de regul mai lente dect codul specializat; ns afectarea vitezei de execuie este compensat cu creterea vitezei de dezvoltare; dimensiunea programelor este influenat n sensul c devine mai mare n cazul programelor care folosesc librrii de componente, dect programele care folosesc cod specializat, deoarece nu toate componentele dintr-o librrie sunt folosite n proiect, dar librria trebuie adugat n totalitatea sa; complexitatea programelor o ierarhie de clase introduce un anumit grad de complexitate n sistem; cu ct ierarhia este mai mare, nivelurile de abstractizare mai multe, cu att sistemul care folosete aceast ierarhie este mai complex.
133
7.4.3 Polimorfism
n limbajele de programare, un obiect polimorfic este orice entitate, cum ar fi o variabil sau argument de funcie, creia i este permis s stocheze valori de tipuri diferite pe durata execuiei programului. Funciile polimorfice sunt acelea care au argumente polimorfice. n programarea orientat-obiect, polimorfismul reflect principiul substituibilitii, i anume, o variabil polimorfic poate stoca o valoare a tipului su declarat sau a oricrui subtip al tipului declarat. Polimorfismul funciilor reprezint posibilitatea de a asocia acelai nume la diferite funcii. Evitarea ambiguitii n procesul de referire se obine prin: atribuirea de liste de parametrii de lungimi diferite; atribuirea de liste de parametrii cu aceeai lungime, dar parametrii corespunztori ca poziie au tipuri diferite; funciilor, polimorfismul poart i numele de
n cazul suprancrcare.
Pentru clasa Matrice se definete operaia de adunare a dou matrice. Implementarea operaiei se face prin dou metode, denumite aduna, difereniate prin lista de parametrii. Clasa Matrice devine:
class Matrice { . void aduna (Matrice& m ) { aduna(m.mat, m.nrLinii, m.nrCol ); } void aduna ( int a[][N], int m, int n ) { for ( int i = 0; i < m; i++ ) { for ( int j = 0; j <n ; j++ ) { mat[i][j] += a[i][j]; } } } };
Cele dou metode sunt polimorfice; au acelai nume, iar stabilirea apelului corect se face la execuie, n funcie de numrul, tipul i poziia parametrilor. O alt situaie n care se pune problema polimorfismului o reprezint suprascrierea metodelor. Prin motenire, clasa derivat preia metodele expuse de clasa printe, conform cu regulile de derivare. ns, ea are posibilitatea s furnizeze o alt implementare pentru o metoda motenit.
134
Practic, se definete o metod n clasa derivat cu aceeai semntur cu cea din clasa printe, dar care are o alt implementare. n momentul execuiei, metoda din clasa derivat este identificat i executat naintea metodei din clasa printe. Suprascrierea se folosete atunci cnd, pentru o anumit funcionalitate, exist o implementare implicit la nivelul clasei printe, implementare care poate fi rafinat prin suprascriere, n clasele derivate, dac se dorete acest lucru. De exemplu, pentru metoda simetrie1() din clasa Matrice se furnizeaz o alt implementare n clasa derivat Matrice2, prin care simetria este testat att pentru diagonala principal, ct i pentru diagonala secundar. Textul surs este:
class Matrice { public: virtual bool simetrie1() { . } }; class Matrice2 : public Matrice { public: bool simetrie1() { //test pentru diagonala principala bool result = Matrice::simetrie1(); //test pentru diagonala secundata if ( nrCol == nrLinii ) { for ( int i = 0; result && i < nrLinii; i++ ) { for ( int j = 0; result && j < nrCol; j++ ) { if ( mat[i][j] != mat[nrColi-1][nrCol-j-1] ) { result = false; } } } } else { result = false; } return result; } };
135
Se observ c n implementarea din clasa Matrice2 se folosete rezultatul implementrii din clasa Matrice, n plus testndu-se i simetria fa de diagonala secundar. Apelarea implementrii din clasa printe nu este o cerin obligatorie atunci cnd se suprascrie o metod, dar este o practic recomandat, deoarece, teoretic, suprascrierea trebuie s aduc ceva n plus fa de o prelucrare general, ntr-un context particular, al clasei derivate.
Suprancrcarea operatorilor
Reprezint un mod elegant de a pune n coresponden simbolurile unor operatori cu proceduri complexe de prelucrare, specifice unor tipuri de date derivate. Limbajul Basic a avut implementat adunarea, scderea, nmulirea de numere i adunarea de matrice. Prin suprancrcarea de operatori se creeaz premisele evalurii de calcule matriceal scriind expresiile direct, cu operatori de calcul i operanzi matrice. n acest fel, operatorul + este pus n coresponden cu procedura de adunare matrice, operatorul este pus n coresponden cu procedura scdere matrice, operatorul * este pus n coresponden cu nmulirea de matrice, iar operatorul = este pus n coresponden cu copierea de matrice. Urmtorii operatori nu se suprancarc: ([]) operatorul de selectare a unei componente membre ntr-o structur de tip articol; (*) operatorul de deferire a unei componente din clas; (::) operatorul de selecie a funciei membru dintr-o clas; operatorul de tratare variabile globale; (?:) operatorul condiional.
unde: tip1 tipul rezultatului returnat; tip2 tipului primului operand; tip3 tipul celui de-al doilea operand.
136
Dac suprancrcarea operatorului simbol_operator este realizat n clasa numit classa, definirea operatorului n afara clasei se realizeaz prin:
tip1 classa::calcul simbol_operator (tip2, tip3)
n exemplul de mai jos se prezint suprancrcarea operatorului + pentru a nsemna adunarea a dou matrice i a operatorului pentru a nsemna scderea a dou matrice pornind de la clasa Matrice.
class Matrice { void aduna (Matrice& m ) { aduna(m.mat, m.nrLinii, m.nrCol ); } Matrice& operator + (Matrice& m ) { aduna(m); return *this; } Matrice& operator - (Matrice& m ) { for (int i = 0; i < nrLinii; i++ ) { for ( int j = 0; j < nrCol; j++ ) { mat[i][j] = mat[i][j] m.mat[i][j]; } } return *this; } };
Datorit concepiei total diferite cu privire la modul n care o aplicaie este proiectat i implementat, evaluarea complexitii necesit utilizarea unor metrici specifice. Rezultatele furnizate de metricile prezentate n capitolul Ciclul de dezvoltare software, i anume, complexitatea ciclomatic i metricile Halstead nu sunt relevante. Pentru aplicaia care rezolv problema considerat ca exemplu de control n aceast carte, se construiete clasa Matrice, iar programul apelator instaniaz aceast clas cu date citite de la tastatur i va apela diverse metode ale clasei pentru a obine rezultatele cerute. Valorile metricilor enunate anterior suport, n acest caz dou niveluri de agregare; ele sunt determinate la nivelul metodelor clasei, sunt agregate la nivelul clasei, i n final, la nivelul ntregii aplicaii. Rezultatele sunt:
Metric Complexitatea ciclomatic (v) Lungimea programului Vocabularul programului Dificultatea = = = = Valoare 29 238 92 35,33
137
Analiznd comparativ cu rezultatele obinute prin celelalte tehnici de programare, exist clar o cretere de complexitate, care are diverse cauze: existena unor construcii specifice programrii orientateobiect, cum sunt constructorii, metodele de acces la atributele clasei etc.; analiza ntregii clase, nu numai a unora dintre funciile implicate n fluxul aplicaiei, din motivul c, o clas este privit ca un tot unitar, i prin urmare trebuie neleas de ctre programator n totalitatea sa; dei informaiile referitoare la dimensiunile i elementele matricei sunt nglobate n clas, i prin urmare, nu sunt transmise ca parametrii, acest lucru nu este surprins de metricile Halstead de complexitate, care iau n considerare, n fiecare dintre metodele clasei, atributele acesteia ca fiind operanzi distinci, lucru ce contribuie la creterea nejustificat a valorilor acestor metrici. De aceea, pentru analiza complexitii programelor orientate obiect, sunt utilizate metrici specifice. Exist dou tipuri de metrici: unele se refer la interdependena dintre clase i se numesc metrici de cuplare, altele se refer la consistena clasei i se numesc metrici de coeziune. Aceste metrici sunt n legtur cu caracteristicile specifice ale programrii orientate obiect: ncapsulare, motenire, polimorfism, interaciunea dintre obiecte. Metricile de coeziune sunt legate n principal de dimensiunea claselor. Unele cele mai relevante metrici din aceast clas de metrici sunt: numrul de atribute ale clasei (NOA), mprit n numrul de atribute ale instanei de clas (NOI) i numrul de atribute statice (NOS); numrul de metode ale clasei (NOM), mprit n numrul de metode publice (NOP), i numrul de metode private (NOPV); aceste metrici sunt mrimi ale ncapsulrii; gradul de ncapsulare (GI), vzut ca raport ntre numrul de elemente private ale clasei i numrul total de elemente. Aceast mrime trebuie s fie ct mai aproape de valoarea 1. Pentru atribute, el trebuie s fie ntotdeauna 1.
138
Pentru exemplul considerat, n care exist clasele Matrice i Matrice2, se obin urmtoarele valori pentru metricile prezentate mai sus:
Metric NOA NOI NOS NOM NOP NOPV GI = = = = = = = Matrice 3 3 0 7 7 0 0,33
Se observ lipsa metodelor de tip privat, ceea ce nseamn c funcionalitatea expus este foarte mare, practic toate metodele clasei sunt prezente n interfaa acesteia. O clas cu o interfa bogat n metode are un nivel de complexitate mai mare dect al unei clase care expune mai puine metode n interfaa sa, deoarece prima necesit un efort de nelegere din partea programatorului mai mare, cel puin teoretic, dect a doua. Metricile de cuplare sunt legate, n principal, de interaciunea dintre obiecte sau dintre clase. Interaciunea dintre obiecte se identific n parametrii transmii n apelurile de metode, instanierea de obiecte locale de tipuri diferite, tipul rezultatului apelului unei metode. Dou clase sunt cuplate atunci cnd metodele declarate n una dintre ele folosesc metode declarate n cealalt. O cuplare excesiv afecteaz nu numai reutilizabilitatea claselor, dar i complexitatea acestora datorit numrului mare de legturi dintre clase care determin o nelegerea mai greoaie a codului de ctre programator. Avantajele programrii orientate obiect sunt nenumrate, printre cele mai importante regsindu-se i reutilizarea codului. Prin intermediul mecanismelor de derivare, se obine acces la funcionalitatea de baz a obiectului, cu posibilitatea modificrii acesteia prin mecanisme polimorfice, cum sunt suprascrierea metodelor sau suprancrcarea operatorilor. Prin derivare, se obin clase noi, ce modeleaz mai atent anumite aspecte ale entitilor reale sau abstracte utilizate n modelul aplicaiei software.
139
8.1 Premise
Programarea orientat obiect ofer o abordare unitar asupra fenomenelor modelate de aplicaiile software att din punct de vedere al datelor, ct i al prelucrrilor. Acest lucru a fcut ca aceast tehnic de programare s se impun n detrimentul celorlalte. Programarea orientat obiect a dus i la construirea de metodologii de dezvoltare software orientate obiect. Un aspect foarte important n activitatea de programare l constituie reutilizarea codului. Acest lucru se face prin construirea de librrii cu elemente de cod, cum sunt funciile sau clasele de obiecte, i utilizarea acestora n noi aplicaii. Principiile de construire a unor astfel de librrii au fost expuse n capitolele Programarea standard i Programarea orientat obiect. Datorit dezvoltrii i rspndirii de noi limbaje i tehnologii de programare, a aprut o prim mare problem: cum se poate folosi o funcionalitate scris ntr-un limbaj de programare i nglobat ntr-o bibliotec, n cadrul unei aplicaii scrise n alt limbaj de programare? Aceasta este problema interoperabilitii dintre limbajele i tehnologiile diferite de programare. Situaia aceasta este foarte frecvent ntlnit, deoarece o companie care utilizeaz software pentru desfurarea activitilor sale, achiziioneaz acest software pe principii de eficien i cost, mai puin pe principii legate de tehnologii i limbaje folosite. Inevitabil, la un moment dat, acest software va trebui s comunice, pentru rezolvarea unor probleme mai complexe. Pentru aceasta, au fost fundamentate componentele. Programarea bazat pe componente reprezint o extensie a programrii orientate obiect n care elementul central este componenta. Componenta este definit ca program sau obiect binar de dimensiuni mici, cu funcionalitate specific, bine definit, proiectat ntr-o manier care
140
Utilizarea de componente
permite combinarea cu alte componente i/sau aplicaii. Modelul unei aplicaii care utilizeaz componente este prezentat n figura 8.1:
I1 COMP1
PRODUS SOFTWARE
I2
COMP2 .
In
COMPn
Componentele se mpart n trei mari categorii, dup cum urmeaz: componente care ofer servicii utilizatorilor de aplicaii; aici intr n principal componentele vizuale, care sunt afiate pe ecranele cu care lucreaz utilizatorii aplicaiei; componente care ofer servicii de business i care implementeaz reguli de business, cum sunt de exemplu, reguli de calcul al impozitelor i taxelor, modaliti de livrare etc.; rezultatele lor sunt furnizate componentelor din prima categorie, pentru afiarea ctre utilizator; componente care ofer acces la date; acestea sunt componente prin care se permite conectarea la baze de date, la alte aplicaii mai vechi, i care furnizeaz date serviciilor din categoria anterioar, de business, pentru prelucrri.
141
componente i/sau aplicaii. Principiul este simplu i are precedent n abordarea orientat obiect: atta timp ct funcionalitatea oferit nu se schimb, modul n care aceasta este implementat nu conteaz pentru utilizatorul componentei. Interfaa trebuie s aib o serie de caracteristici: s fie consistent, adic funcionalitile oferite s se refere la o aceeai entitate real; de exemplu, o interfa care expune funcionaliti legate de lucrul cu matrice, nu poate conine funcionaliti legate de lucrul cu fiiere; s fie clar, ceea ce nseamn c metodele din interfa s exprime clar funcionalitatea oferit; s fie scurt, deoarece o interfa cu foarte multe metode este greu de neles i de folosit de ctre programatori.
Rezultatul acestei faze este un document de specificaii privind interfaa componentei i, eventual, o descriere a acesteia ntr-un limbaj specializat. O component poate expune mai multe interfee, gndite ca subseturi de funcionalitate pe care componenta o implementeaz. Motivul este urmtorul: diveri utilizatori ai componentei au nevoie doar de anumite metode ale acesteia, prin urmare, expunerea ntregii interfee ctre ei, n primul rnd ngreuneaz utilizarea componentei, dar i deschide posibiliti de afectare a strii componentei de ctre utilizatori care nu ar trebui s o afecteze. Prin urmare, expunerea de interfee diferite pentru tipuri diferite de utilizatori nseamn i implementarea unui mecanism de securitate la nivelul componentei. Pentru problema definit n capitolul Ciclul de dezvoltare software, se imagineaz urmtoarea interfa ce este expus unei alte aplicaii, interfa descris folosind notaia UML:
Se observ c interfaa are o metod de iniializare a componentei cu date referitoare la matrice, o metod pentru determinarea minimului i una
142
Utilizarea de componente
pentru determinarea maximului dintre elementele matricei, iar ultima metod numr elementele din matrice, astfel: dac flag = 1, elementele mai mari dect valoarea specificat n parametrul k; dac flag = 2, elementele mai mici dect valoarea specificat n parametrul k; dac flag = 3, elementele egale cu valoarea specificat n parametrul k;
Cuvntul in care precede fiecare parametru indic faptul c respectivul este parametru de intrare. O alt interfa a componentei conine operaiile de adunare, scdere i nmulire. Descrierea, folosind notaia UML este dat n figura 8.3:
interface MatriceINTF2 +initializare(in nrLinii : long(idl), in nrCol : long(idl), in elemente : any(idl)) +aduna(in matrice : any(idl)) +scade(in matrice : any(idl)) +inmulteste(in matrice : any(idl)) +numarLinii() : long(idl) +numarColoane() : long(idl)
Se observ c, pe lng metodele corespunztoare operaiilor cu masive, exist i dou metode, cu rol de accesorii pentru cmpuri de date ale componentei. Fiecare dintre operaiile de baz, adunare, scdere, nmulire primete ca parametru o alt component de acelai tip cu aceasta, iar rezultatul este memorat n componenta curent. Pentru c o component este scris ntr-un limbaj de programare i folosit n alt limbaj de programare, mecanismul de construire a instanelor de componente este diferit fa de programarea orientat obiect. Din acest motiv este necesar o metod separat de iniializare a datelor componentei. Pasul al doilea nseamn crearea specificaiilor pentru componente, ce includ interfeele i funciile membre. Pornind de la specificaiile interfeelor, se elaboreaz modelul obiectual al componentei care include, pe lng clasa principal, i un set de clase ajuttoare. Obiectivul este de a asigura dezvoltatorilor o specificaie complet att din punct de vedere al funcionalitii implementate, ct i din punct de vedere al modului n care aceasta va fi implementat. Prin urmare, n aceast etap se ine cont de tehnologia ce este folosit la dezvoltarea componentei.
143
Odat ce specificaiile pentru component au fost ntocmite, programatorii ncep dezvoltarea propriu-zis. Unitatea de testare n ingineria software bazat pe componente este realizat prin crearea unei alte componente, care testeaz interfeele i funciile membre ale acesteia. Fiecare programator va trebui s i construiasc o a doua component prin care s testeze funcionalitatea componentei iniiale. Motivaia acestei abordri este urmtoarea: n primul rnd, fiecare funcionalitate, nainte de a fi folosit trebuie supus unei faze de testare; programatorul are obligaia ca funcionalitatea implementat, pe care el o ofer, s fie deja testat; tehnica se numete unit-testing i vizeaz stabilirea urmtoarelor: conformitatea funcionalitii cu specificaiile: componenta face ceea ce este specificat s fac; implementarea reacioneaz corespunztor i n situaiile excepionale de prelucrare: cazuri extreme, fluxuri alternative de prelucrare etc. n al doilea rnd, se testeaz comunicarea ntre component i mediul extern; practic, se simuleaz modul n care componenta va fi folosit n cadrul unor aplicaii concrete.
Faza de testare individual (unit-testing) a componentei presupune la rndul alte dou faze: testarea componentei ca i clas (pentru c la baz nu este altceva dect o clas); aceasta presupune testarea direct a metodelor componentei att cele private, ct mai ales cele publice; testarea componentei prin interfeele expuse; aceast testare trebuie s aduc n plus, fa de rezultatele celei anterioare, informaii despre cum sunt primii i tratai parametrii de intrare, cum se scriu datele n parametrii de ieire.
Dezvoltarea unei componente difer n funcie de tipul de component i tehnologia folosit. Fiecare tehnologie are propriile mecanisme. ntr-un fel sunt scrise componentele folosind tehnologia COM i limbajul C++ i n alt mod sunt scrise componentele folosind tehnologia EJB mpreun cu limbajul Java. Pentru exemplificare, s-a folosit tehnologia COM folosind
144
Utilizarea de componente
ATL i limbajul C++. Astfel, interfaa din figura 8.2 se scrie n felul urmtor, folosind IDL + Interface Definition Language:
[ object, uuid(F8AE2674-8836-433E-B809-D6DB90BFA8E9), dual, helpstring("IMatrice Interface"), pointer_default(unique) ] interface IMatrice : IDispatch { [id(1), helpstring("method initializare")] HRESULT initializare(int nrLinii, int nrCol, int* mat); [id(2), helpstring("method minim")] HRESULT minim(int* min); [id(3), helpstring("method maxim")] HRESULT maxim(int* max); [id(4), helpstring("method contor")] HRESULT contor(int flag,int k, int* numar); }; [ uuid(E64BA31E-7FBC-47CE-A993-474502FD2CB2), version(1.0), helpstring("Matrix 1.0 Type Library") ]
Pentru scrierea interfeei au fost utilizate mecanismele de tip wizard din cadrul mediului de dezvoltare Microsoft Visual Studio 6.0. Clasa C++ care implementeaz interfaa are urmtorul cod surs:
class ATL_NO_VTABLE CMatrice : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CMatrice, &CLSID_Matrice>, public IDispatchImpl<IMatrice, &IID_IMatrice, &LIBID_MATRIXLib> { public: CMatrice() { } DECLARE_REGISTRY_RESOURCEID(IDR_MATRICE) DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_COM_MAP(CMatrice) COM_INTERFACE_ENTRY(IMatrice) COM_INTERFACE_ENTRY(IDispatch) END_COM_MAP() // IMatrice
145
Analiza comparat a complexitii entitilor text generate prin tehnici de programare public: STDMETHOD(contor)(int flag,int k, int* numar); STDMETHOD(maxim)(int* max); STDMETHOD(minim)(int* min); STDMETHOD(initializare)(int nrLinii, int nrCol, int* mat); private: int n; int m; int a[M][N]; }; . STDMETHODIMP CMatrice::initializare(int nrLinii, int nrCol, int* mat) { m = nrLinii; n = nrCol; for ( int i = 0; i < m; i++ ) { for ( int j = 0; j < n; j++ ) { a[i][j] = mat[i*m + j]; } } return S_OK; } STDMETHODIMP CMatrice::minim(int *min) { *min = a[0][0]; for ( int i = 0; i < m; i++ ) { for ( int j = 0; j < n; j++ ) { if ( a[i][j] < *min ) { *min = a[i][j]; } } } return S_OK; } STDMETHODIMP CMatrice::maxim(int *max) { *max = a[0][0]; for ( int i = 0; i < m; i++ ) { for ( int j = 0; j < n; j++ ) { if ( a[i][j] < *max ) { *max = a[i][j]; } } } return S_OK; }
146
Utilizarea de componente
STDMETHODIMP CMatrice::contor(int flag, int k, int *numar) { int i = 0; int j = 0; *numar = 0; switch ( flag ) { case 1: for ( ; i < m; i++ ) { for ( ; j < n; j++ ) { if ( a[i][j] > k ) ( *numar )++; } } break; case 2: for ( ; i < m; i++ ) { for ( ; j < n; j++ ) { if ( a[i][j] < k ) ( *numar )++; } } break; case 3: for ( ; i < m; i++ ) { for ( ; j < n; j++ ) { if ( a[i][j] == k ) ( *numar )++; } } break; } return S_OK; }
Se observ c, pe lng clasa C++ propriu-zis, care implementeaz funcionalitatea componentei, mai sunt implicate o serie de alte clase, specifice tehnologiei de a cror scriere se ocup wizard-ul, mbuntindu-se astfel productivitatea. De asemenea, tipurile de date i mai ales modul n care sunt transmii parametrii i rezultatele difer fa de situaiile normale din programarea orientat obiect. De exemplu, rezultatul prelucrrii se returneaz printr-un parametru de tip ieire declarat n lista de parametrii; funcia returneaz un cod de succes sau de eroare specific. De asemenea, nu orice tip de dat este permis; numai tipurile fundamentale, irurile de caractere i pointerii apar n listele de parametrii ale metodelor componentei expuse prin interfa. Pentru metodele din interiorul componentei, nevizibile n afar, se folosesc i alte tipuri de date. Pentru evaluarea complexitii componentelor, metricile din capitolul Ciclul de dezvoltare software nu sunt relevante pentru c filozofia componentelor se refer la utilizarea acestora sub form binar, n care nu
147
are importan caracteristicile codului surs ci capacitatea componentei de a oferi funcionalitatea ateptat. Tot din acelai motiv, al lipsei accesului la codul surs, nici metricile specifice abordrii orientate obiect, unele enunate n capitolul Programarea orientat obiect nu sunt aplicabile. De asemenea, deoarece, componentele se bazeaz pe diferite tehnologii, dezvoltarea lor presupune nu numai doar scrierea funcionalitii dar i a codului surs ajuttor, specific fiecrei tehnologii n parte. Prin mecanismele de tip wizard, mare parte din codul surs ajuttor este generat deja, programatorul trebuind doar s adauge metodele specifice ce implementeaz funcionalitatea componentei. Datorit caracterului binar al componentelor, trebuie luate n considerare metrici care au legtur cu utilizarea componentei n aplicaii i practic surprind complexitatea componentei din punct de vedere al utilizrii ei i nu din punct de vedere al construciei; astfel de metrici sunt: numrul de metode dintr-o interfa (NMI), numrul de parametrii ai metodelor dintr-o interfa (NPM), mprit n numrul parametrilor de intrare (NPI), numrul parametrilor de ieire (NPE) i numrul parametrilor de intrare/ieire (NPIE); acestea sunt mrimi ale complexitii interfeei componentelor; numrul de interfee expuse de component (NI), numrul de metode de iniializare a acesteia (NIM); acestea sunt mrimi ale disponibilitii componentei. Pentru exemplul considerat, valorile metricilor enumerate anterior sunt:
Metric NMI NPM NPI NPE NPIE = = = = = Interfaa1 4 9 5 4 0 Interfata2 6 8 6 2 0
Referitor la metricile din categoria a doua, componenta construit expune dou interfee i are o singur metod de iniializare. Se observ c o metod poate face parte din mai multe interfee; de aceea, o agregare a metricilor anterioare la nivelul componentei nu are relevan, datorit riscului multiplicrii valorilor pentru metodele prezente n mai multe interfee.
148
Utilizarea de componente
Proiectarea i dezvoltarea bibliotecilor de componente se face ntr-o manier asemntoare cu proiectarea i dezvoltarea bibliotecilor de funcii i clase pentru un limbaj de programare. Construirea bibliotecilor de componente se realizeaz n mai multe moduri: arbitrar, atunci cnd componente potenial reutilizabile sunt grupate n librrii, iar reutilizarea lor depinde foarte mult de cunotinele despre ele ale programatorilor;
149
bazat pe cerere, prin care dezvoltatorii de produse software sunt ncurajai s solicite pieei componente pentru diferite funcionaliti; de exemplu, se solicit o component pentru lucru cu masive bidimensionale. Dac exist suficient de multe cereri pentru o anumit component, atunci acea component este dezvoltat, la cerere i nglobat ntr-o librrie; bazat pe dezvoltarea de ctre companie a unui set de componente, proprietare, pe care dezvoltatorii s le foloseasc n cadrul aplicaiilor. Aceast abordare este de regul la ndemna firmelor puternice, care i pot permite s investeasc timp i bani n dezvoltarea de librrii de componente. Pe de alt parte, pentru produse specifice i foarte pretenioase, este necesar chiar ca firma s aib propriile componente pentru funcionalitile critice, chiar dac acestea ar putea fi achiziionate din alt parte, tocmai pentru a avea un control mai bun asupra produsului.
prin
utilizarea
Obiectivul principal al dezvoltrii de componente este de a permite reutilizarea unei anumite funcionaliti implementat ntr-un limbaj de programare, n cadrul unei aplicaii dezvoltat n alt limbaj de programare. Decizia de a implementa i folosi componente trebuie s se bazeze pe urmtoarele argumente: costul dezvoltrii unei componente, raportat la numrul previzibil de utilizri ale acelei componente; dac o component este dezvoltat spre a fi folosit doar n cadrul unei aplicaii, atunci efortul necesar acestui proces este prea mare, pentru c tehnologiile folosite pentru construirea de componente, n general sunt complexe, nu sunt facil de manevrat i pentru aceasta sunt necesari oameni bine pregtii. Dac utilizarea ulterioar este foarte sczut, se opteaz pentru o implementare clasic, orientat obiect a funcionalitii, cu riscul duplicrii implementrii pentru fiecare din utilizrile ulterioare. exist situaia n care o anumit funcionalitate este optim implementat folosind un anumit limbaj de programare; de
150
Utilizarea de componente
exemplu, lucrul cu fiiere sau cu periferice este mult mai rapid pentru programele dezvoltate n C/C++ dect Java sau C#. De aceea, se opteaz pentru dezvoltarea de componente n C++ care s lucreze cu periferice, la un nivel mai apropiat de hardware, urmnd ca restul funcionalitii s fie dezvoltat ntr-un limbaj ca Java sau C#, din care se vor face apeluri ctre metodele componentei respective. Bineneles, aceast abordare este viabil doar n situaia n care ctigul de performan conteaz foarte mult n economia execuiei aplicaiei; dac nu este att de important acest ctig de performan, din nou, soluia clasic, orientat obiect este mult mai puin costisitoare. Interoperabilitatea aplicaiilor reprezint capacitatea acestora de a comunica, de a partaja funcionalitate i informaii, chiar dac aplicaiile ruleaz n medii diferite, au fost concepute i realizate folosind limbaje de programare sau tehnologii diferite. Soluiile de interoperabilitate sunt numeroase, dar de cele mai multe ori sunt specifice unui anumit grup de aplicaii. Soluiile de tip componente au la baz un standard de implementare i utilizare, care asigur o uniformitate n abordarea interoperabilitii. Componentele sunt dezvoltate pe baza unor schelete bine stabilite, interfeele sunt descrise prin instrumente standardizate, utilizarea componentelor n aplicaii se face ntr-o manier clar i bine definit. Standardizarea mecanismelor de comunicare dintre componente are mai multe avantaje: din punct de vedere al dezvoltatorului, este suficient s cunoasc un singur mecanism pentru a nelege diversele implementri i utilizri ale acestuia; din punct de vedere al productorului de software, permite un mai bun management al aplicaiilor pe care le produce i le distribuie i, implicit, costuri mult mai mici; din punct de vedere al calitii aplicaiilor, pentru c, dei poate o soluie particular, specific unui anumit context, este mai performant, costul pierderii de performan este mult mai mic dect costul mentenanei ulterioare a unei soluii specifice, proprietare.
n ultima vreme, a aprut un nou tip de arhitectur de aplicaii, care vizeaz reutilizare maxim de funcionalitate i permite interoperabilitate
151
facil, i anume arhitecturile orientate pe servicii. Serviciul este, de fapt, tot o component, ns, cu mecanismul de comunicare complet standardizat i bazat pe un schelet mult mai uor de folosit. Problema major a componentelor tradiionale, de tip COM/DCOM, din punct de vedere al dezvoltrii, o reprezenta complexitatea foarte mare a tehnologiei, lucru care era descurajant de multe ori pentru productorii de software, pui n situaia de a decide utilizarea lor. Prin intermediul serviciilor, acest impediment este eliminat; dezvoltarea este mult mai uoar, comunicarea cu un astfel de serviciu de asemenea. Pentru servicii, se folosete SOAP ca protocol de invocare a metodelor, folosit peste protocolul HTTP. SOAP se bazeaz pe XML pentru structurarea informaiei transferate. n acest fel, nu mai sunt implicate mecanisme de comunicare specifice unui anumit sistem de operare, cum era cazul componentelor tradiionale. Din punct de vedere al unui dezvoltator, serviciile WEB sunt o extensie a tehnologiei distribuite. Practic, aplicaiile sunt rezultatul combinrii prelucrrilor interne i prelucrrilor asigurate se serviciile WEB disponibile. Apare conceptul de software ca serviciu (software as service). Asemeni componentelor, serviciile WEB sunt ncapsulate complet; dezvoltatorul care le folosete nu trebuie s cunoasc detalii de implementare; funcionalitatea expus de serviciu este descris n interfaa sa, prin intermediului unui limbaj specific, WSDL Web Services Definition Language. Conceptul de software ca serviciu contribuie foarte mult la depirea granielor de integrare, inclusiv n cadrul organizaiei. Muli dezvoltatori i arhiteci de sistem vd serviciile WEB ca o opiune excelent pentru integrarea aplicaiilor i mult mai uor de implementat dect soluiile clasice bazate pe componente COM/DCOM de exemplu, sau cele orientate pe mesaje (comunicarea dintre aplicaii se face prin intermediul unui strat intermediar de mesaje). Acest lucru devine foarte important cnd se pune problema integrrii n extranet i permite interoperabilitatea dintre diversele locaii ale companiei sau dintre parteneri, clieni i furnizori. Dac se expune funcionalitate prin servicii WEB, bazate pe SOAP/XML i descrise ntr-un fiier WSDL, oricine o poate accesa fr s fie nevoie de aplicaii specifice care s se potriveasc platformei de tehnologie folosit pentru dezvoltarea funcionalitii.
152
Prezentarea tehnicilor de programare are menirea de a evidenia evoluia n timp a unui domeniu deosebit de important pentru tehnologia informaiei. Trecerea de la o tehnic de programare la alta este rezultatul natural al acumulrii de experien i rezultatul evoluiei structurilor hardware marcat de creterea vitezei de calcul, creterea capacitii de memorare i creterea numrului i tipurilor de echipamente periferice. Toate acumulrile evideniaz tendina spre performan, spre creterea productivitii muncii i creterea calitii produselor software. Indiferent de tehnica de programare ntrebuinat n dezvoltarea de aplicaii informatice, un rol deosebit revine persoanelor analiti i programatori, care definesc probleme, proiecteaz soluii i dezvolt cod surs. n mod natural, dezvoltarea de aplicaii informatice trebuie s urmeze cerinele tehnologiei pe obiecte sau cele ale tehnologiei pe componente. n cazul n care apare necesitatea reutilizrii de software, integrarea de componente elaborate folosind tehnici de programare mai vechi, devine eficient dac i numai dac aceste tehnici sunt cunoscute i se opereaz pe textele surs existente. Cunoaterea tehnicilor de programare permite identificarea unor pri stabile care se regsesc n toate structurile de programe i stabilirea acelor pri care difer radical de la o tehnic la alta. Separarea acestora permite ndreptarea eforturilor spre a executa trecerea a ct mai multor componente dintr-o categorie n alta n vederea obinerii unui echilibru ct mai stabil. Exist situaii cnd sisteme informatice din generaii mai vechi, aflate n exploatare curent, trebuie reproiectate pentru a prelua facilitile oferite de noile tehnologii. Existena de programe translatoare care automatizeaz procesul impune definirea unor parametri. Corectitudinea procesului de translatare depinde de nivelul de cunoatere a inputurilor, adic de nivelul de cunoatere a tehnicii sub care a fost dezvoltat sistemul informatic mai vechi. Este important s se cunoasc tehnicile de programare existente pentru a crea premisele evoluiei spre alte noi tehnici, atunci cnd se identific noi
153
cerine, noi exigene i se stabilesc limitele prin care se caracterizeaz att tehnicile de programare vechi, ct i cele de programare noi. Analiza complexitii software realizat pentru implementrile soluiei problemei PROB folosind diferite tehnici de programare, a artat ca exist o influen a abordrilor pe care fiecare tehnic o promoveaz asupra complexitii softwareului rezultat. ns, de asemenea, trebuie remarcat faptul c, pentru anumite tehnici de programare cum este cea orientat obiect sau cea bazat pe componente, metricile clasice, cum sunt cele prezentate n aceast carte, respectiv complexitatea ciclomatic i metricile Halstead, nu au relevan. n tabelul 9.1 sunt sintetizate rezultatele obinute pe codurile surs ale implementrilor pentru diferitele tehnici de programare, ale problemei PROB. Complexitile implementrilor problemei PROB Tabelul 9.1
Indicator Tehnica de programare Complexitate ciclomatic Dificultate (Halstead)
Abordarea clasic Programarea standard Programarea structurat Programarea modular Programarea orientat obiect Programarea orientat pe componente
7 14 12 15 29 * 15
n cazul programrii structurate, cele dou variante corespund situaiilor n care programul a fost dezvoltat ca un monolit, respectiv folosind funcii. Componentele sunt vzute ca i cutii negre, astfel c important este funcionalitatea expus i nu implementarea. Componentele pot fi scrise ntr-un limbaj de programare i utilizate n aplicaii dezvoltate folosind alt limbaj de programare. Prin urmare, analiza pe codul componentelor nu are relevan; n plus, n funcie de diferitele tehnologii orientate pe componente, exist elemente suplimentare de cod care apar n dezvoltare. Setul de metrici pentru evaluarea complexitii componentelor nu conine
154
Concluzii
indicatori ce se aplic asupra codului surs, mai ales c acesta poate fi rescris, fr a se afecta utilizarea componentei. Se observ o cretere a complexitii de la abordarea clasic ctre cea orientat obiect, cretere datorat specificitii diverselor tehnici. Diferenele de complexitate nu sunt mari, cel puin n cazul primelor tehnici (clasic, standard, structurat, modular). n cazul tehnologiei orientate obiect, exist un salt semnificativ de complexitate, explicabil prin faptul c exist multe construcii de limbaj care aduc un plus de complexitate (de exemplu, existena constructorilor), dar i prin faptul c metricile alese nu au mare relevan n cazul acestei tehnici de programare. De aceea, n practica curent, se pune accentul pe alegerea unui alt set de metrici atunci cnd se discut despre complexitatea codului surs orientat obiect sau despre complexitatea aplicaiilor bazate pe componente, acest lucru fiind subliniat n capitolele 7 i 8 ale lucrrii. ntr-o analiz de acest tip, important este reprezentativitatea setului de date pe care se face analiza, respectiv codul surs. Analiza efectuat n cadrul lucrrii ofer indicii primare cu privire la relaia dintre complexitate i tehnica de programare folosit la dezvoltarea produsului, ns, pentru o analiz complet, se impune aplicarea acestor metrici pe un lot mai mare de probleme, pentru care s se construiasc variante corespunztoare tuturor tehnicilor de programare prezentate.
155
Anexa 1
ei x[N] a[M][N] -
x ik Pi
denumire problem pentru care se dezvolt software element al unei colecii de date (articol) fiier care memoreaz o colecie de date (articole) lungimea n baii a unui element Ai lungimea total a fiierului F, n baii poziia n fiier a unui element Ai cheia elementului Ai deplasarea elementului Ai fa de nceputul fiierului F element al unui ir de numere suma elementelor xi ale unui ir instruciunea de pe poziia j din program program care rezolv o problem dat lungimea n numr de instruciuni a unui program numrul de arce din graful asociat programului PR numrul de noduri din graful asociat programului PR complexitatea software n sens McCabe frecvena operanzilor din program frecvena operatorilor din program complexitatea n sens Halstead numrul de elemente ale unui ir de numere frecvena de apariie a elementului xi n ir elementul de pe poziia i din vectorul x valoarea elementului minim din vectorul x poziia din vectorul x pe care o ocup elementul cu valoarea minim eticheta i din program masiv unidimensional cu dimensiunea maxim N masiv bidimensional cu dimensiunile maxime M linii i N coloane media aritmetic a elementelor unui masiv unidimensional variabil de stare subproblem a problemei P
156
subprogramul corespunztor subproblemei Pi subproblema j a subproblemei Pi numrul de instruciuni dintr-o secven expresie condiional secvena de instruciuni care se execut dac expresia condiional C1 este adevrat secvena de instruciuni care se execut daca expresia condiional C1 este fals variabila de control valoarea iniial a variabilei de control valoarea final a variabilei de control raia secvena de instruciuni care se execut n cadrul structurii repetitive secvena de instruciuni care se execut dup ncheierea execuiei unei structuri repetitive expresia condiional dintr-o secven repetitiv datele de intrare ale problemei datele de ieire ale unei probleme numrul datelor de ieire furnizate de produsul software modulul pentru obinerea outputurilor DEi aflat pe nivelul j, care utilizeaz datele de intrare DEij subproblema i a problemei P subproblema j, a subproblemei i, a problemei P
157
[ATHAN95] Irina ATHANASIU, Eugenia KALISZ, Valentin CRISTEA Iniiere n TURBO PASCAL, Bucureti, Editura Teora, 1995 [BARBU97] Gheorghe BARBU, Ion VDUVA, Mircea BOLOTEANU Bazele Informaticii, Bucureti, Editura Tehnic, 1997 [BUDD97] [CATR94] [CRIST98] Timothy BUDD An Introduction to Object Oriented Programming, Second Edition, Addison-Wesley, 1997 Octavian CATRINA, Iuliana COJOCARU Turbo C++, Bucureti, Editura Teora, 1994 Valeriu CRISTEA, Irina ATHANASIU, Eugenia KALISZ, Valeriu IORGA Tehnici de programare, Bucureti, Editura Teora, 1998 Bodgan GILIC-MICU, Ion Gh. ROCA, Constantin APOSTOL, Marian STOICA, Ctlina COCIANU Algoritmi n Programare, Bucureti, Editura ASE, 2002 Ion Gh. ROCA, Bogdan GHILIC-MICU, Ctlina COCIANU, Marian STOICA, Cristian USCATU Programarea calculatorului. tiina nvrii unui limbaj de programare. Teorie i aplicaii, Bucureti, Editura ASE, 2003 Ion SMEUREANU, Ion IVAN, Marian DRDAL Structuri i obiecte n C++, Bucureti, Editura Cison, 1998 Mark LORENTZ, Jeff KIDD Object-oriented software metrics: a practical guide, Englewood Cliffs, NJ PTR Prentice Hall, 1994 Rodica MIHALCA, Ion IVAN, Ioan ODGESCU Programe applicative, Bucureti, Editura ASE, 1983
[GMICU02]
[GMICU03]
[IVANI98] [LOREN94]
[MIHAL83]
158
[MIHAL98]
Rodica MIHALCA, Csaba FABIAN, Adina U, Felix SIMION Analiz i proiectare orientate obiect. Instrumente de tip CASE, Bucureti, Editura Societatea Autonom de Informatic, 1998
[MSPCD97] Microsoft Press Computer Dictionary, Third Edition, Microsoft Press, 1997 [ROCA94] Ion Gh. ROCA, Constantin APOSTOL, Valer ROCA, Bogdan GHILIC-MICU Prelucrarea fiierelor n PASCAL, Bucureti, Editura Tehnic, 1994 Ion Gh. ROCA, Ctlina COCIANU, Cristian USCATU Programarea calculatoarelor. Aplicaii, Bucureti, Editura ASE, 2003
[ROCA03]
[ROCA103] Ion Gh. ROCA, Bogdan GHILIC-MICU, Constantin APOSTOL, Valer ROCA, Ctlina COCIANU Programarea calculatoarelor. Tehnica programrii n limbajul PASCAL, Bucureti, Editura ASE, 2003 [ROTAR96] Eugen ROTARU Limbajul Java, Trgu-Mure, Computer Press Agora, 1996 [SMEU01] [SMEU02] Ion SMEUREANU, Marian DRDAL Programarea n limbajul C/C++, Editura Cison, Bucureti, 2001 Ion SMEUREANU, Marian DRDAL Programarea orientat obiect n limbajul C++, Bucureti, Editura Cison, 2002
[SMEUR04] Ion SMEUREANU, Marian DRDAL, Adriana REVEIU Visual C#. .NET, Bucureti, Editura Cison, 2004 [SPIR95] Claudia SPIRCU, Ionu LOPTAN POO Analiza, proiectarea i programarea orientate obiect, Bucureti, Editura Teora, 1995 Bjarne Stroustrup The C++ Programming Language, Addison Wesley, 1987
[STROU87]
159