Ingineria Programarii

135
1/135 INGINERIE SOFTWARE ........................................................................................................................ 5 1 FAZELE DEZVOLTĂRII UNUI PRODUS SOFTWARE ............................................................ 6 1.1 CE ESTE INGINERIA PROGRAMĂRII? ............................................................................................. 6 1.2 FAZELE INGINERIEI PROGRAMĂRII ............................................................................................... 9 1.2.1 Faza de analiză ................................................................................................................. 9 1.2.2 Faza de proiectare .......................................................................................................... 10 1.2.3 Faza de implementare ..................................................................................................... 12 1.2.4 Faza de testare ................................................................................................................ 12 1.3 CONCLUZII ................................................................................................................................ 13 2 METODOLOGII DE DEZVOLTARE A PROGRAMELOR ..................................................... 14 2.1 ETAPELE DEZVOLTĂRII PROGRAMELOR ..................................................................................... 14 2.2 METODOLOGII GENERICE .......................................................................................................... 15 2.2.1 Metodologia secvenţială ................................................................................................. 15 2.2.2 Metodologia ciclică ........................................................................................................ 16 2.2.3 Metodologia hibridă ecluză ............................................................................................ 17 2.3 METODOLOGII CONCRETE ......................................................................................................... 19 2.3.1 Metodologia cascadă ...................................................................................................... 19 2.3.2 Metodologia spirală ........................................................................................................ 20 2.3.3 Metodologia spirală WinWin .......................................................................................... 22 2.3.4 Prototipizarea ................................................................................................................. 22 2.3.5 Metodologia Booch ......................................................................................................... 23 2.3.6 Metode formale ............................................................................................................... 23 2.3.7 Extreme Programming .................................................................................................... 23 2.4 CONCLUZII ................................................................................................................................ 25 3 MANAGEMENTUL UNUI PROIECT SOFTWARE .................................................................. 26 3.1 MANAGEMENTUL SOFTWARE .................................................................................................... 26 3.1.1 Planificarea proiectului .................................................................................................. 27 3.1.2 Controlul proiectului ...................................................................................................... 28 3.2 MANAGEMENTUL CONFIGURAŢIEI ............................................................................................. 29 3.3 MANAGEMENTUL ECHIPEI ......................................................................................................... 31 3.3.1 Managementul forţei de muncă....................................................................................... 31 3.3.1.1 Mecanisme de coordonare ........................................................................................................... 32 3.3.1.2 Stiluri de management ................................................................................................................. 32 3.3.2 Organizarea echipei........................................................................................................ 33 3.3.2.1 Organizarea ierarhică ................................................................................................................... 33 3.3.2.2 Organizarea matrice ..................................................................................................................... 34 3.3.2.3 Echipa programatorului şef .......................................................................................................... 35 3.3.2.4 Principii generale de organizare a unei echipe ............................................................................. 35 3.4 CONCLUZII ................................................................................................................................ 36 4 ESTIMAREA COSTULUI UNUI PROIECT SOFTWARE........................................................ 37 4.1 INTRODUCERE ........................................................................................................................... 37 4.2 CUM NU TREBUIE ESTIMAT COSTUL ........................................................................................... 39 4.3 MODELE ALGORITMICE CLASICE ............................................................................................... 40 4.4 MODELE ALGORITMICE MODERNE............................................................................................. 42 4.4.1 Modelul Walston-Felix.................................................................................................... 44 4.4.2 Modelul COCOMO ......................................................................................................... 45 4.4.3 Modelul Putnam-Norden................................................................................................. 46 4.5 DISTRIBUIREA FORŢEI DE MUNCĂ ÎN TIMP ................................................................................. 46 4.6 CONCLUZII ................................................................................................................................ 48 5 REUTILIZAREA RESURSELOR SOFTWARE ......................................................................... 49 5.1 INTRODUCERE ........................................................................................................................... 49 5.2 REUTILIZAREA PRODUSELOR INTERMEDIARE ............................................................................ 50 5.2.1 Biblioteci şi componente software .................................................................................. 50 5.2.2 Şabloane.......................................................................................................................... 53 5.2.3 Reutilizarea proiectării ................................................................................................... 53

description

A course of Programming Engineering which is very useful for University, as well as after finishing it.

Transcript of Ingineria Programarii

Page 1: Ingineria Programarii

1/135

INGINERIE SOFTWARE........................................................................................................................ 5 1 FAZELE DEZVOLTĂRII UNUI PRODUS SOFTWARE ............................................................ 6

1.1 CE ESTE INGINERIA PROGRAMĂRII? ............................................................................................. 6 1.2 FAZELE INGINERIEI PROGRAMĂRII............................................................................................... 9

1.2.1 Faza de analiză ................................................................................................................. 9 1.2.2 Faza de proiectare .......................................................................................................... 10 1.2.3 Faza de implementare..................................................................................................... 12 1.2.4 Faza de testare................................................................................................................ 12

1.3 CONCLUZII ................................................................................................................................ 13 2 METODOLOGII DE DEZVOLTARE A PROGRAMELOR ..................................................... 14

2.1 ETAPELE DEZVOLTĂRII PROGRAMELOR..................................................................................... 14 2.2 METODOLOGII GENERICE .......................................................................................................... 15

2.2.1 Metodologia secvenţială ................................................................................................. 15 2.2.2 Metodologia ciclică ........................................................................................................ 16 2.2.3 Metodologia hibridă ecluză ............................................................................................ 17

2.3 METODOLOGII CONCRETE ......................................................................................................... 19 2.3.1 Metodologia cascadă ...................................................................................................... 19 2.3.2 Metodologia spirală........................................................................................................ 20 2.3.3 Metodologia spirală WinWin .......................................................................................... 22 2.3.4 Prototipizarea ................................................................................................................. 22 2.3.5 Metodologia Booch......................................................................................................... 23 2.3.6 Metode formale ............................................................................................................... 23 2.3.7 Extreme Programming.................................................................................................... 23

2.4 CONCLUZII ................................................................................................................................ 25 3 MANAGEMENTUL UNUI PROIECT SOFTWARE.................................................................. 26

3.1 MANAGEMENTUL SOFTWARE .................................................................................................... 26 3.1.1 Planificarea proiectului .................................................................................................. 27 3.1.2 Controlul proiectului ...................................................................................................... 28

3.2 MANAGEMENTUL CONFIGURAŢIEI............................................................................................. 29 3.3 MANAGEMENTUL ECHIPEI ......................................................................................................... 31

3.3.1 Managementul forţei de muncă....................................................................................... 31 3.3.1.1 Mecanisme de coordonare ........................................................................................................... 32 3.3.1.2 Stiluri de management ................................................................................................................. 32

3.3.2 Organizarea echipei........................................................................................................ 33 3.3.2.1 Organizarea ierarhică ................................................................................................................... 33 3.3.2.2 Organizarea matrice ..................................................................................................................... 34 3.3.2.3 Echipa programatorului şef.......................................................................................................... 35 3.3.2.4 Principii generale de organizare a unei echipe ............................................................................. 35

3.4 CONCLUZII ................................................................................................................................ 36 4 ESTIMAREA COSTULUI UNUI PROIECT SOFTWARE........................................................ 37

4.1 INTRODUCERE ........................................................................................................................... 37 4.2 CUM NU TREBUIE ESTIMAT COSTUL........................................................................................... 39 4.3 MODELE ALGORITMICE CLASICE ............................................................................................... 40 4.4 MODELE ALGORITMICE MODERNE............................................................................................. 42

4.4.1 Modelul Walston-Felix.................................................................................................... 44 4.4.2 Modelul COCOMO......................................................................................................... 45 4.4.3 Modelul Putnam-Norden................................................................................................. 46

4.5 DISTRIBUIREA FORŢEI DE MUNCĂ ÎN TIMP ................................................................................. 46 4.6 CONCLUZII ................................................................................................................................ 48

5 REUTILIZAREA RESURSELOR SOFTWARE ......................................................................... 49 5.1 INTRODUCERE ........................................................................................................................... 49 5.2 REUTILIZAREA PRODUSELOR INTERMEDIARE ............................................................................ 50

5.2.1 Biblioteci şi componente software .................................................................................. 50 5.2.2 Şabloane.......................................................................................................................... 53 5.2.3 Reutilizarea proiectării ................................................................................................... 53

Page 2: Ingineria Programarii

2/135

5.2.4 Sisteme de transformare.................................................................................................. 54 5.2.5 Generatoare de aplicaţii şi limbaje de a patra generaţie ............................................... 55

5.3 REUTILIZAREA INSTRUMENTELOR ŞI A TEHNICILOR .................................................................. 55 5.3.1 Limbaje de interconectare a modulelor .......................................................................... 55 5.3.2 Programarea orientată obiect ........................................................................................ 58

5.4 PERSPECTIVELE REUTILIZĂRII SOFTWARE ................................................................................. 58 5.5 ECONOMIA REUTILIZĂRII SOFTWARE......................................................................................... 61

5.5.1 Economia refolosirii software......................................................................................... 61 5.5.2 Reutilizarea software şi managementul .......................................................................... 61 5.5.3 Aspecte psihologice ale reutilizării software .................................................................. 62

5.6 CONCLUZII ................................................................................................................................ 62 6 INGINERIA CERINŢELOR. ANALIZA ORIENTATĂ OBIECT ............................................ 63

6.1 INGINERIA CERINŢELOR............................................................................................................. 63 6.1.1 Cerinţa ............................................................................................................................ 63 6.1.2 Extragerea cerinţelor...................................................................................................... 64 6.1.3 Metode pentru identificarea cerinţelor utilizatorilor...................................................... 64 6.1.4 Metode pentru specificarea cerinţelor utilizatorilor....................................................... 65

6.1.4.1 Limbajul natural........................................................................................................................... 65 6.1.4.2 Formalisme matematice ............................................................................................................... 65 6.1.4.3 Engleza structurată....................................................................................................................... 65 6.1.4.4 Tabele .......................................................................................................................................... 66 6.1.4.5 Diagrame bloc ale sistemului ....................................................................................................... 66

6.1.5 Documentul cerinţelor utilizatorului (DCU)................................................................... 66 6.1.5.1 Evoluţia DCU .............................................................................................................................. 66 6.1.5.2 Responsabilităţi............................................................................................................................ 67 6.1.5.3 Conţinutul DCU........................................................................................................................... 67

6.2 ANALIZA ORIENTATĂ OBIECT. METODE DE ANALIZĂ ORIENTATĂ OBIECT ................................. 67 6.2.1 Principiile analizei orientate obiect................................................................................ 68 6.2.2 Abstractizarea................................................................................................................. 69 6.2.3 Moştenirea ...................................................................................................................... 69 6.2.4 Comunicarea prin mesaje ............................................................................................... 69

6.3 METODA DE ANALIZĂ COAD-YOURDON.................................................................................... 69 6.3.1 Activitatea I: Identificarea claselor şi obiectelor............................................................ 70 6.3.2 Activitatea a Il-a: Identificarea structurilor ................................................................... 71

6.3.2.1 Structura gen-spec........................................................................................................................ 71 6.3.2.2 Structura întreg-parte ................................................................................................................... 73

6.3.2.2.1 Strategii de identificare a structurilor întreg-parte ............................................................... 75 6.3.2.2.2 Verificarea structurilor întreg-parte ..................................................................................... 76

6.3.3 Activitatea a IlI-a: Identificarea atributelor ................................................................... 76 6.3.3.1 Identificarea atributelor................................................................................................................ 77 6.3.3.2 Poziţionarea atributelor ................................................................................................................ 77 6.3.3.3 Identificarea conexiunilor instanţelor........................................................................................... 77 6.3.3.4 Verificarea cazurilor speciale....................................................................................................... 78 6.3.3.5 Specificarea atributelor ................................................................................................................ 78

6.3.4 Activitatea a IV-a: Identificarea serviciilor .................................................................... 79 6.3.5 Activitatea a V-a: Identificarea subiectelor .................................................................... 81

6.4 CONCLUZII ................................................................................................................................ 82 7 PROIECTAREA ORIENTATĂ OBIECT..................................................................................... 83

7.1 PROCESUL PROIECTĂRII SISTEMELOR SOFTWARE ...................................................................... 83 7.2 CARACTERISTICILE UNEI PROIECTĂRI CORECTE ........................................................................ 83

7.2.1 Extensibilitatea ............................................................................................................... 83 7.2.2 Siguranţa......................................................................................................................... 84 7.2.3 Eficienţa .......................................................................................................................... 84

7.3 PROIECTAREA ORIENTATĂ OBIECT ............................................................................................ 85 7.4 ETAPELE PROIECTĂRII ORIENTATE OBIECT ................................................................................ 85

7.4.1 Contextul sistemului şi modelele de utilizare.................................................................. 86 7.4.2 Proiectarea arhitecturii .................................................................................................. 86 7.4.3 Identificarea obiectelor................................................................................................... 87 7.4.4 Modele de proiectare ...................................................................................................... 87 7.4.5 Specificarea interfeţelor obiectelor................................................................................. 87

Page 3: Ingineria Programarii

3/135

7.5 METODA DE PROIECTARE COAD-YOURDON .............................................................................. 88 7.5.1 Activitatea I: Proiectarea componentei domeniului problemei ...................................... 88 7.5.2 Activitatea a Il-a: Proiectarea componentei interacţiunii cu factorul uman .................. 91 7.5.3 Activitatea a IlI-a: Proiectarea componentei coordonării task-urilor............................ 92 7.5.4 Activitatea a IV-a: Proiectarea componentei coordonării datelor ................................. 92

7.6 CONCLUZII ................................................................................................................................ 92 8 LIMBAJE DE MODELARE. UML ............................................................................................... 93

8.1 LIMBAJE DE MODELARE ............................................................................................................ 93 8.2 CE ESTE UML? ......................................................................................................................... 94 8.3 MODELAREA CAZURILOR DE UTILIZARE.................................................................................... 95 8.4 MODELAREA CONCEPTUALĂ. DIAGRAMA DE CLASE ................................................................. 97

8.4.1 Asocierea ........................................................................................................................ 98 8.4.2 Agregarea ..................................................................................................................... 100 8.4.3 Compunerea.................................................................................................................. 101 8.4.4 Vizibilitatea atributelor şi operaţiilor........................................................................... 101 8.4.5 Moştenirea .................................................................................................................... 102 8.4.6 Polimorfismul................................................................................................................ 103 8.4.7 Interfeţe......................................................................................................................... 104 8.4.8 Metode statice ............................................................................................................... 105

8.5 DIAGRAME DE INTERACŢIUNE ................................................................................................. 105 8.5.1 Diagrama de secvenţe................................................................................................... 105 8.5.2 Diagrama de colaborare............................................................................................... 106

8.6 DIAGRAME DE ACTIVITĂŢI ...................................................................................................... 107 8.7 DIAGRAME DE STĂRI ............................................................................................................... 109 8.8 DIAGRAMA PACHETELOR ........................................................................................................ 110 8.9 DIAGRAME DE IMPLEMENTARE ............................................................................................... 111

8.9.1 Diagrama componentelor ............................................................................................. 111 8.9.2 Diagrama de lansare .................................................................................................... 112

8.10 CONCLUZII .............................................................................................................................. 112 9 IMPLEMENTAREA ..................................................................................................................... 113

9.1 INTRODUCERE ......................................................................................................................... 113 9.2 LIMBAJE DE PROGRAMARE ...................................................................................................... 113

9.2.1 Limbaje imperative ....................................................................................................... 114 9.2.2 Limbaje declarative ...................................................................................................... 114

9.3 ANALIZA UNOR LIMBAJE DE PROGRAMARE ............................................................................. 115 9.3.1 C/C++........................................................................................................................... 115 9.3.2 Basic ............................................................................................................................. 117 9.3.3 Pascal ........................................................................................................................... 118 9.3.4 Java............................................................................................................................... 118 9.3.5 C#.................................................................................................................................. 119

9.4 COMPARAŢIE ÎNTRE UNELE LIMBAJE DE PROGRAMARE ........................................................... 119 9.5 UTILITARE PENTRU IMPLEMENTARE ŞI TESTARE...................................................................... 123 9.6 CONCLUZII .............................................................................................................................. 125

10 PSIHOLOGIA SI ETICA PROGRAMĂRII.......................................................................... 126 10.1 PROGRAMAREA CA ACTIVITATE UMANĂ ................................................................................. 126

10.1.1 Scrierea programelor.................................................................................................... 128 10.1.1.1 Comentariile............................................................................................................................. 129 10.1.1.2 Numele variabilelor ................................................................................................................. 129 10.1.1.3 Indentaţia ................................................................................................................................. 130

10.1.2 Concepte ale limbajelor de programare ....................................................................... 130 10.1.3 Interfaţa cu utilizatorul ................................................................................................. 131

10.2 ETICA PROGRAMĂRII ............................................................................................................... 133 10.2.1 Codul etic IEEE ............................................................................................................ 133 10.2.2 Legea drepturilor de autor............................................................................................ 133 10.2.3 Licenţa publică generală GNU ..................................................................................... 134

10.3 CONCLUZII .............................................................................................................................. 135

Page 4: Ingineria Programarii

4/135

Page 5: Ingineria Programarii

5/135

Inginerie software

Page 6: Ingineria Programarii

6/135

1 Fazele dezvoltării unui produs software

1.1 Ce este ingineria programării? Ştiinţa calculatoarelor este un domeniu relativ nou. Primele calculatoare au fost

construite la mijlocul anilor 1940 şi de atunci au avut loc dezvoltări spectaculoase, în anul 1946 Goldstine şi von Neumann apreciau că 1000 de instrucţiuni reprezintă o limită superioară rezonabilă pentru complexitatea problemelor ce pot fi concepute ca rezolvabile cu ajutorul calculatorului. După ce a prevăzut în 1981 că nici un program pentru calculatoare personale nu va necesita vreodată mai mult de 640 KB de memorie RAM, Bill Gates admite în 1995 că lucrurile s-au schimbat în ultimele două decenii.

Următoarele exemple oferă o imagine asupra gradului de complexitate la care au ajuns programele în zilele noastre:

• sistemul de rezervare a biletelor pentru compania aeriană KLM conţinea, în anul 1992, două milioane de linii de cod în limbaj de asamblare;

• sistemul de operare System V versiunea 4.0 (UNIX) a fost obţinut prin compilarea a 3.700.000 linii de cod;

• programele scrise pentru naveta spaţială NASA au circa 40 de milioane de linii de cod;

• pentru realizarea sistemului de operare IBM OS360 au fost necesari 5000 de ani-om.

Creşterea programelor în dimensiune şi complexitate a depăşit cu mult progresele făcute în domeniul tehnicilor de programare. De aceea, programarea a devenit şi a rămas mai mult o artă decât o meserie.

O paralelă cu ingineria construcţiilor este atractivă. Dacă dorim să construim o cuşcă pentru câine, putem să mergem prin grădină, să căutam lemne şi cuie, să luăm un ciocan şi să începem să lucrăm. Avem şanse destul de bune să reuşim, mai ales dacă suntem îndemânatici. Dacă totuşi nu reuşim, putem încerca a doua zi din nou cu alte lemne şi alte cuie. Iar dacă câinele nu încape în cuşcă, putem să ne cumpărăm alt câine.

Lucrurile stau radical diferit atunci când dorim să construim o casă pentru familia noastră. Atunci va trebui sau să angajăm un arhitect care să ne facă un proiect, sau să cumpărăm un proiect standard de casă. Va trebui să negociem cu o firmă de construcţii preţul, durata de realizare, calitatea finisajelor. Nu ne permitem să riscăm economiile familiei pe o construcţie care se va dărâma la a doua rafală de vânt. în plus, dacă membrilor familiei nu le place orientarea ferestrelor sau peisajul, nu îi putem schimba cu alţii (în cel mai rău caz, ne schimbă ei pe noi).

Cu atât mai mult, dacă o firmă plăteşte câteva milioane de dolari pentru a ridica un zgârie nori, reprezentanţii acesteia vor fi foarte atenţi cu cine şi în ce condiţii vor lucra. Ei vor dori garanţii că proiectul este viabil, vor angaja mai multe firme de arhitectură pentru a-1 verifica. De asemenea, studii geologice, de fizică a pământului sau meteorologie vor fi obligatorii. Vor fi folosite cele mai performante materiale şi se vor angaja cei mai competenţi şi cu experienţă constructori. Eşecul nu mai este o opţiune pentru contractantul proiectului.

Ştim că inginerii constructori întocmesc planuri, construiesc machete, studiază proprietăţile materialelor folosite şi fac rapoarte privind progresul operaţiunilor. Construcţii de o complexitate foarte mare au fost realizate în acest fel într-un mod raţional şi economic. Inginerii de programe ar trebui să procedeze similar pentru ca dezvoltarea programelor să nu mai fie un proces impredictibil.

Pe măsură ce complexitatea programelor creştea, la sfârşitul anilor '60 începea să se prefigureze deja o criză a programării. Un raport prezentat de către o companie, în care erau analizate câteva proiecte şi stadiile lor de finalizare, a constatat că:

Page 7: Ingineria Programarii

7/135

•2% din sistemele software contractate au funcţionat de la predare; •3% din sistemele software au putut funcţiona după câteva modificări; •29% au fost predate dar n-au funcţionat niciodată; •19% au fost folosite dar au fost abandonate; 47% au fost plătite dar niciodată predate. Pentru a contracara aceste tendinţe, la conferinţa organizată de comitetul ştiinţific al

NATO în anul 1968, a fost propus termenul de ingineria programării (engl. „software engineering"), într-un mod oarecum provocator. Se dorea ca arta programării să împrumute din rigoarea ştiinţelor inginereşti pentru a putea livra programe la timp şi în mod economic. Prima definiţie dată ingineriei programării a fost enunţată astfel (F. L. Bauer):

Ingineria programării este stabilirea şi utilizarea de principii inginereşti solide pentru a obţine în mod economic programe sigure şi care funcţionează eficient pe maşini de calcul reale.

In IEEE Standard Glossary of Software Engineering Technology (1983) ingineria programării este definită după cum urmează:

Ingineria programării reprezintă abordarea sistematică a dezvoltării, funcţionării, întreţinerii, şi retragerii din funcţiune a programelor.

Remarcăm că a doua definiţie este mai vagă decât prima, întrucât nu face referire la cost şi la eficienţă. Mai mult, se pare că experienţa în general negativă acumulată a făcut să se renunţe la formularea „principii inginereşti solide", întrucât se pare că acestea nu pot fi identificate fără a fi supuse contestaţiilor. A doua definiţie adaugă însă referiri la perioade importante din viaţa unui program, ce urmează creării şi funcţionării, şi anume întreţinerea şi retragerea din funcţionare.

Considerăm că ingineria programării are următoarele caracteristici importante: • este aplicabilă în producerea de programe mari; • este o ştiinţă inginerească; • scopul final este îndeplinirea cerinţelor clientului.

Programele mici se pot scrie relativ uşor, de către un singur programator, într-o perioadă destul de scurtă de timp. Un program de 100 de instrucţiuni este cu siguranţă un program mic. Nu putem identifica precis graniţa dintre un program mic şi unul mare, însă pe măsură ce dimensiunea programului creşte, apar provocări noi, calitativ diferite.

întrucât un singur sau câţiva programatori nu pot avea timpul fizic pentru terminarea programului, este necesară crearea uneia sau mai multor echipe de lucru. Este necesară coordonarea şi comunicarea între echipe. Complexitatea sistemului software şi a organizaţiei care realizează sistemul software devine importantă, putând depăşi capacitatea de înţelegere a unui singur individ. Apare ca dezirabilă o abordare riguroasă a acestor probleme, ce include stilul de lucru, modul de scriere a codului etc.

Nerespectarea cerinţelor poate avea efecte serioase. Un sistem de livrare a insulinei pentru diabetici poate provoca moartea pacientului dacă nu funcţionează corect. Funcţionarea incorectă a unui sistem de control al unui satelit poate provoca pagube de milioane de dolari.

Un program este fiabil dacă funcţionează şi continuă să funcţioneze fără întreruperi un interval de timp. Această noţiune exprimă de fapt rezistenţa la condiţiile de funcţionare. Un sistem de operare trebuie să fie fiabil pentru că trebuie să funcţioneze o perioadă suficient de lungă de timp fără să cadă, indiferent de programele care rulează pe el, chiar dacă nu totdeauna la performanţe optime.

Programul este sigur dacă funcţionează corect, fără operaţii nedorite. Un program pentru un automat bancar trebuie să fie sigur, pentru a efectua tranzacţiile în mod absolut corect, chiar dacă funcţionarea sa poate fi întreruptă din când în când. Atunci când funcţionează însă, trebuie să funcţioneze foarte bine.

Programul este sigur dacă funcţionează corect, fără operaţii nedorite. Un automat bancar trebuie să fie sigur, pentru a efectua tranzacţiile în mod absolut corect, chiar dacă funcţionarea

Page 8: Ingineria Programarii

8/135

sa poate fi întreruptă din când în când. Atunci când funcţionează însă, trebuie să funcţioneze foarte bine.

Un program are o eroare (engl. „bug") dacă nu se comportă corect. Se presupune că dezvoltatorul ştia ce ar fi trebuit programul să facă, iar comportamentul greşit nu este intenţionat. Iată câteva erori celebre:

• În primii ani în care calculatoarele au fost introduse la staţiile de benzină din SUA, consumatorii primeau cecuri pe sume enorme. Faptul era privit în general cu umor şi reclamaţiile erau rezolvate repede;

• Sistemul de operare IBM OS360 conţinea aproximativ 1.000 de greşeli la fiecare nouă versiune care încerca să rezolve greşelile din versiunea precedentă;

• Un vehicul de explorare a planetei Venus a fost pierdut deoarece programul primit de pe Pământ pentru rectificarea orbitei conţinea linia 'DO 31= 1.3'; instrucţiunea corectă în limbajul FORTRAN ar fi trebuit să conţină virgulă în loc de punct;

• In 1979 s-a descoperit o eroare în programele pentru sistemele de răcire în centralele nucleare din SUA; din fericire, nu fusese niciodată nevoie de execuţia rutinelor ce conţineau erorile;

• Din cauza unei erori în sistemul de avertizare împotriva atacului cu rachete balistice, procedurile de contraatac au fost declanşate înainte de a se descoperi că a fost o eroare;

• Racheta Arianne 5 a explodat în iunie 1996 din cauza unei greşeli de programare; costurile s-au ridicat la 500 milioane dolari.

Ingineria programării are ca scop obţinerea de sisteme funcţionale chiar şi atunci când teoriile şi instrumentele disponibile nu oferă răspuns la toate provocările ce apar. Inginerii fac lucrurile să meargă, ţinând seama de restricţiile organizaţiei în care lucrează şi de constrângerile financiare.

Problema fundamentală a ingineriei programării este îndeplinirea cerinţelor clientului. Aceasta trebuie realizată nu punctual, nu în acest moment, ci într-un mod flexibil şi pe termen lung. Ingineria programării se ocupă cu toate etapele dezvoltării programelor, de la extragerea cerinţelor de la client până la întreţinerea şi retragerea din folosinţă a produsului livrat. Pe lângă cerinţele funcţionale, clientul doreşte (de obicei) ca produsul final să fie realizat cu costuri de producţie cât mai mici. De asemenea, este de dorit ca aceasta să aibă performanţe cât mai bune (uneori direct evaluabile), un cost de întreţinere cât mai mic, să fie livrat la timp, şi să fie sigur. Rezumând, atributele cheie ale unui produs software se referă la:

• posibilitatea de a putea fi întreţinut: un produs cu un lung ciclu de viaţă este supus deseori modificărilor, de aceea el trebuie foarte bine documentat;

• fiabilitate: produsul trebuie să se comporte după cerinţele utilizatorului şi să nu „cadă" mai mult decât e prevăzut în specificaţiile sale;

• eficienţă: produsul nu trebuie să folosească în pierdere resursele sistemului ca memoria sau ciclii procesor;

• interfaţa potrivită pentru utilizator: interfaţa trebuie să ţină seama de capacitatea şi cunoştinţele utilizatorului.

Optimizarea tuturor acestor atribute e dificilă deoarece unele se exclud pe altele (o mai bună interfaţă pentru utilizator poate micşora eficienţa produsului), în cazurile în care eficienţa este critică, acest lucru trebuie specificat explicit încă din faza de preluare a cerinţelor utilizatorului, precum şi compromisurile pe care ea le implică privind ceilalţi factori.

Trebuie spus că ingineria programării nu rezolvă toate problemele care apar atunci când se scriu programe. Ingineria programării nu oferă nici teorii. Inginerii fac lucrurile să meargă. Totuşi, în momentul de faţă, ingineria programării ne poate spune sigur ce să nu facem.

Page 9: Ingineria Programarii

9/135

1.2 Fazele ingineriei programării Există patru faze fundamentale ale metodologiilor ingineriei programării:

• analiza (ce dorim să construim); • proiectarea (cum vom construi); • implementarea (construirea propriu-zisă); • testarea (asigurarea calităţii).

Deşi aceste faze se referă în mod special la ciclul de viaţă al produsului software, ele pot fi aplicate şi altor stadii de existenţă prin care trece un program de la „naştere" până la „moarte": lansare, întreţinere, ieşire din uz.

1.2.1 Faza de analiză Această fază defineşte cerinţele sistemului, independent de modul în care acestea vor fi

îndeplinite. Aici se defineşte problema pe care clientul doreşte să o rezolve. Rezultatul acestei faze este documentul cerinţelor, care trebuie să precizeze clar ce trebuie construit.

Documentul încearcă să redea cerinţele din perspectiva clientului, definind scopurile şi interacţiunile la un nivel descriptiv înalt, independent de detaliile de implementare, cum ar fi, de exemplu: formularea problemei, aşteptările clientului sau criteriile pe care trebuie să le îndeplinească produsul.

Graniţa dintre descrierile de nivel înalt şi cele de nivel scăzut nu este foarte bine trasată. Uneori, dacă un detaliu tehnic important trebuie specificat, el va apărea în document. Totuşi, aceasta trebuie să fie excepţia şi nu regula. Aceste excepţii pot fi determinate de necesitatea menţinerii compatibilităţii cu alte sisteme deja existente, sau a unor anumite opţiuni dorite de client, de exemplu utilizarea unui anumit standard sau o constrângere asupra dimensiunilor imaginii aplicaţiei, care poate fi destinată unei categorii speciale de utilizatori sau care va rula pe nişte sisteme cu o serie de particularităţi (monitoare care nu suportă rezoluţii mari). Faza de analiză poate fi văzută ca o rafinare a detaliilor. Distincţia dintre detaliile de nivel înalt şi cele de nivel scăzut sunt puse mai bine în evidenţă de abordările top-down (unde se merge către detaliile de nivel scăzut) şi bottom-up (care tind către detaliile de nivel înalt).

Documentul cerinţelor poate fi realizat într-o manieră formală, bazată pe logică matematică, sau poate fi exprimat în limbaj natural, în mod tradiţional, el descrie obiectele din sistem şi acţiunile care pot fi realizate cu ajutorul obiectelor. Aici noţiunea de „obiect" nu trebuie confundată cu obiectul din programarea orientată obiect. Descrierea obiectelor şi acţiunilor trebuie să fie generală şi să nu depindă de o anumită tehnologie. Desigur, într-o abordare POO, descrierile vor lua forma obiectelor şi metodelor, însă în alte abordări, obiectele pot fi de exemplu servicii care accesează baze de date.

In general, documentul cerinţelor descrie ontologia proiectului, adică vocabularul de cuvinte cheie (în special construcţii substantivale şi verbale) care va fi utilizat pentru definirea protocolului specific aplicaţiei. Descrierile acestea nu implică proiectarea arhitecturii aplicaţiei, ci enumerarea părţilor componente şi a modului în care acestea se comportă. Mai târziu, în faza de proiectare, acestea vor fi transformate în primitive informatice, precum liste, stive, arbori, grafuri, algoritmi şi structuri de date.

Mai concret, documentul trebuie să conţină descrieri pentru următoarele categorii: • Obiecte: Documentul trebuie să definească mai întâi ontologia sistemului, care

este bazată în mare parte pe construcţii substantivale pentru identificarea pieselor, părţilor componente, constantelor, numelor şi a relaţiilor dintre acestea;

• Acţiuni: Documentul trebuie să definească de asemenea acţiunile pe care trebuie să le îndeplinească sistemul şi care sunt sugerate în general de construcţii verbale. Exemple de acţiuni sunt: metodele, funcţiile sau procedurile;

Page 10: Ingineria Programarii

10/135

• Stări: Sunt definite ca mulţimi de setări şi valori care disting sistemul între două ipostaze spaţio-temporale. Fiecare sistem trece printr-o serie de schimbări de stare. Exemple de stări sunt: starea iniţială, cea finală sau stările de eroare. Cele mai multe stări depind de domeniul problemei. Stările sunt asociate cu obiectele sistemului. Un eveniment declanşează o tranziţie de stare care poate conduce la îndeplinirea unei acţiuni de către sistem;

• Scenarii tipice: Un scenariu este o secvenţă de paşi urmaţi pentru îndeplinirea unui scop. Când sistemul este terminat şi aplicaţia este disponibilă, clientul trebuie să poată utiliza, într-o manieră cât mai facilă şi clar specificată, toate scenariile tipice ale aplicaţiei. Scenariile tipice trebuie să reprezinte majoritatea scenariilor de utilizare ale aplicaţiei. Ponderea acestora variază de la un sistem la altul, dar 90% se consideră o proporţie acceptabilă. Bineînţeles că un sistem cu un singur scenariu de utilizare este relativ simplu de obţinut, pe când unul cu mii de scenarii posibile va fi mult mai dificil de analizat. Deseori este invocată regula 80/20: 80% din funcţionalitatea sistemului se realizează cu 20% din efortul de muncă. Executarea restului minoritar de funcţionalitate necesită marea majoritate a timpului de lucru;

• Scenarii atipice: Un scenariu atipic trebuie să fie îndeplinit de sistem numai în cazuri speciale. Clientul poate să spere, de exemplu, că o eroare neprevăzută este un eveniment atipic. Totuşi, sistemul trebuie să gestioneze un număr cât mai mare de categorii de erori, prin tehnici stabilite, precum tratarea excepţiilor, monitorizarea proceselor etc.;

• Cerinţe incomplete sau nemonotone: O enumerare completă a cerinţelor pentru toate situaţiile care pot apărea în condiţii de lucru reale nu este posibilă. In logica tradiţională, o teorie este definită de o mulţime finită de axiome. Teoremele din teoria respectivă sunt propoziţii adevărate. Dacă se adaugă ulterior noi axiome, teoremele existente rămân valide iar noile teoreme dezvoltate sunt adăugate teoremelor stabilite. In logica nemonotonă, adăugarea de noi axiome poate invalida unele teoreme care au fost demonstrate anterior. O nouă teorie nu mai este o simplă extensie a teoriei vechi, ci o mulţime de teoreme noi, împreună cu o parte din teoremele vechi. Procesul de stabilire a cerinţelor are o natură iterativă şi nemonotonă. Mulţimea iniţială de cerinţe (axiomele) defineşte posibilităţile (teoremele) sistemului. Noile cerinţe pot infirma soluţiile vechi. Pe măsură ce un sistem creşte în dimensiuni şi complexitate, stabilirea cerinţelor devine din ce în ce mai dificilă, mai ales când procesul de colectare a cerinţelor este distribuit, fiind realizat de indivizi cu specializări diferite.

1.2.2 Faza de proiectare Pe baza cerinţelor din faza de analiză, acum se stabileşte arhitectura sistemului:

componentele sistemului, interfeţele şi modul lor de comportare: • Componentele sunt elementele constructive ale produsului. Acestea pot fi create

de la zero sau reutilizate dintr-o bibliotecă de componente. Componentele rafinează şi capturează semnificaţia detaliilor din documentul cerinţelor;

• Interfeţele ajută la îmbinarea componentelor. O interfaţă reprezintă graniţa dintre două componente, utilizată pentru comunicarea dintre acestea. Prin intermediul interfeţei, componentele pot interacţiona;

• Comportamentul, determinat de interfaţă, reprezintă răspunsul unei componente la stimulii acţiunilor altor componente.

Page 11: Ingineria Programarii

11/135

Documentul de proiectare descrie planul de implementare a cerinţelor. Se identifică detaliile privind limbajele de programare, mediile de dezvoltare, dimensiunea memoriei, platforma, algoritmii, structurile de date, definiţiile de tip globale, interfeţele etc.

In această fază trebuie indicate şi priorităţile critice pentru implementare. Acestea sugerează sarcinile care, dacă nu sunt executate corect, conduc la eşecul sistemului. Totuşi, chiar dacă priorităţile critice sunt îndeplinite, acest fapt nu duce automat la succesul sistemului, însă creşte nivelul de încredere că produsul va fi o reuşită.

Folosind scenariile tipice şi atipice, trebuie realizate compromisurile inerente între performanţă şi complexitatea implementării. Analiza performanţelor presupune studierea modului în care diferitele arhitecturi conduc la diferite caracteristici de performanţă pentru fiecare scenariu tipic. In funcţie de frecvenţa de utilizare a scenariilor, fiecare arhitectură va avea avantaje şi dezavantaje. Un răspuns rapid la o acţiunea a utilizatorului se realizează deseori pe baza unor costuri de resurse suplimentare: indecşi, managementul cache-ului, calcule predictive etc. Dacă o acţiune este foarte frecventă, ea trebuie realizată corect şi eficient. O acţiune mai rară trebuie de asemenea implementată corect, dar nu este evident care e nivelul de performanţă necesar în acest caz. O situaţie în care o astfel de acţiune trebuie implementată cu performanţe maxime este închiderea de urgenţă a unui reactor nuclear.

Planul de implementare şi planul de test, descrise mai jos, pot fi incluse de asemenea în fazele de implementare şi respectiv testare. Insă unul din scopurile fazei de proiectare este stabilirea unui plan pentru terminarea sistemului, de aceea cele două planuri au fost incluse în paragraful curent.

Planul de implementare stabileşte programul după care se va realiza implementarea şi resursele necesare (mediul de dezvoltare, editoarele, compilatoarele etc.).

Planul de test defineşte testele necesare pentru stabilirea calităţii sistemului. Dacă produsul trece toate testele din planul de test, este declarat terminat. Cu cât testele sunt mai amănunţite, cu atât este mai mare încrederea în sistem şi deci creşte calitatea sa. Un anume test va verifica doar o porţiune a sistemului. Acoperirea testului este procentajul din produs verificat prin testare, în mod ideal, o acoperire de 100% ar fi excelentă, însă este rareori îndeplinită. De obicei, un test cu o acoperire de 90% este simplă, însă ultimele 10% necesită o perioadă de timp semnificativă.

De exemplu, să considerăm BIOS-ul (Basic Input/Output System) construit de IBM la începutul anilor '80 în strânsă legătură cu sistemul de operare DOS (Disk Operating System) al firmei Microsoft. Din raţiuni de performanţă, BIOS-ul a fost plasat într-un chip ROM, şi deci patch-urile pentru eventualele erori erau imposibile. Astfel, au fost necesare teste cu acoperire de 100%. Codul propriu-zis al BlOS-ului era destul de mic, câteva mii de linii. Insă deoarece BIOS-ul are o natură asincronă, testul a presupus mai întâi crearea unui mediu asincron care să aducă sistemul în starea dorită şi apoi trebuia generat un eveniment care să declanşeze un test. Foarte repede, setul de test a devenit mult mai mare decât BIOS-ul. A apărut astfel problema testării însuşi a mediului de test! In final, o acoperire de 100% a fost atinsă, dar cu un cost foarte ridicat. O soluţie mai ieftină a fost înscrierea BlOS-ului într-o combinaţie dintre EPROM (Electronic Programmable Read Only Memory) şi ROM. Cea mai mare parte a produsului era plasat în ROM, iar patch-urile erau plasate în EPROM. Aceasta a fost abordarea adoptată de Apple pentru Macintosh.

In general, este suficient ca testele să cuprindă scenariile tipice şi atipice, fără să verifice întregul sistem, cu absolut toate firele de execuţie. Acesta poate conţine ramificaţii interne, erori sau întreruperi care conduc la fire de execuţie netestate. Majoritatea sistemelor sunt pline de bug-uri nedescoperite. De obicei, clientul participă în mod logic la testarea sistemului şi semnalează erori care vor fi îndepărtate în versiunile ulterioare.

Page 12: Ingineria Programarii

12/135

1.2.3 Faza de implementare In această fază, sistemul este construit, ori plecând de la zero, ori prin asamblarea unor

componente pre-existente. Pe baza documentelor din fazele anterioare, echipa de dezvoltare ar trebui să ştie exact ce trebuie să construiască, chiar dacă rămâne loc pentru inovaţii şi flexibilitate. De exemplu, o componentă poate fi proiectată mai restrâns, special pentru un anumit sistem, sau mai general, pentru a satisface o direcţie de reutilizare.

Echipa trebuie să gestioneze problemele legate de calitate, performanţă, biblioteci şi debug. Scopul este producerea sistemului propriu-zis. O problemă importantă este îndepărtarea erorilor critice, într-un sistem există trei tipuri de erori:

• Erori critice: împiedică sistemul să satisfacă în mod complet scenariile de utilizare. Aceste erori trebuie corectate înainte ca sistemul să fie predat clientului şi chiar înainte ca procesul de dezvoltare ulterioară a produsului să poată continua;

• Erori necritice: Sunt cunoscute, dar prezenţa lor nu afectează în mod semnificativ calitatea observată a sistemului. De obicei aceste erori sunt listate în notele de lansare şi au modalităţi de ocolire bine cunoscute;

• Erori necunoscute: Există întotdeauna o probabilitate mare ca sistemul să conţină un număr de erori nedescoperite încă. Efectele acestor erori sunt necunoscute. Unele se pot dovedi critice, altele pot fi rezolvate cu patch-uri sau eliminate în versiuni ulterioare.

1.2.4 Faza de testare Calitatea produsului software este foarte importantă. Multe companii nu au învăţat însă

acest lucru şi produc sisteme cu funcţionalitate extinsă, dar cu o calitate scăzută. Totuşi, e mai simplu să-i explici clientului de ce lipseşte o anumită funcţie decât să-i explici de ce produsul nu este performant. Un client satisfăcut de calitatea produsului va rămâne loial firmei şi va aştepta noile funcţii în versiunile următoare.

In multe metodologii ale ingineriei programării, faza de testare este o fază separată, realizată de o echipă diferită după ce implementarea s-a terminat. Motivul este faptul că un programator nu-şi poate descoperi foarte uşor propriile greşeli. O persoană nouă care priveşte codul poate descoperi greşeli evidente care scapă celui care citeşte şi reciteşte materialul de multe ori. Din păcate, această practică poate determina o atitudine indiferentă faţă de calitate în echipa de implementare.

Tehnicile de testare sunt abordate preponderent din perspectiva producătorului sistemului, în mod ideal, şi clientul trebuie să joace un rol important în această fază. Testele de regresiune (engl. „regression test") sunt colecţii de programe care testează una sau mai multe trăsături ale sistemului. Rezultatele testelor sunt adunate şi dacă există erori, bug-ul este corectat. Un test de regresiune valid generează rezultate verificate, numite „standardul de aur". Validitatea rezultatului unui test ar trebui să fie determinată de documentul cerinţelor, în practică, echipa de implementare este responsabilă de interpretarea validităţii.

Testele sunt colectate, împreună cu rezultatele standardelor de aur, într-un pachet de test de regresiune. Pe măsură ce dezvoltarea continuă, sunt adăugate mai multe teste noi, iar testele vechi pot rămâne valide sau nu. Dacă un test vechi nu mai este valid, rezultatele sale sunt modificate în standardul de aur, pentru a se potrivi aşteptărilor curente. Pachetul de test este rulat din nou şi generează noi rezultate. Acestea sunt comparate cu rezultatele standardelor de aur. Dacă sunt diferite, în sistem a apărut o greşeală. Greşeala este corectată şi dezvoltarea continuă. Acest mecanism detectează situaţiile când starea curentă de dezvoltare a produsului invalidează o stare existentă. Astfel, se previne regresiunea sistemului într-o stare de eroare anterioară.

Există patru puncte de interes în testele de regresiune pentru asigurarea calităţii.

Page 13: Ingineria Programarii

13/135

Testarea internă tratează implementarea de nivel scăzut. Fiecare funcţie sau componentă este testată de către echipa de implementare. Aceste teste se mai numesc teste „clear-box" sau „white-box", deoarece toate detaliile sunt vizibile pentru test.

Testarea unităţilor testează o unitate ca un întreg. Aici se testează interacţiunea mai multor funcţii, dar numai în cadrul unei singure unităţi. Testarea este determinată de arhitectură. De multe ori sunt necesare aşa-numitele „schele", adică programe special construite pentru stabilirea mediului de test. Numai când mediul este realizat se poate executa o evaluare corectă. Programul schelă stabileşte stări şi valori pentru structurile de date şi asigură funcţii externe fictive. De obicei, programul schelă nu are aceeaşi calitate ca produsul software testat şi adesea este destul de fragil. O schimbare mică în test poate determina schimbări importante în programul schelă. Aceste teste se mai numesc teste „black-box" deoarece numai detaliile interfeţei sunt vizibile pentru test.

Testarea internă şi a unităţilor poate fi automatizată cu ajutorul instrumentelor de acoperire (engl. „coverage tools"), care analizează codul sursă şi generează un test pentru fiecare alternativă a firelor execuţie. Depinde de programator combinarea acestor teste în cazuri semnificative care să valideze rezultatelor fiecărui fir de execuţie. De obicei, instrumentul de acoperire este utilizat într-un mod oarecum diferit: el urmăreşte liniile de cod executate într-un test şi apoi raportează procentul din cod executat în cadrul testului. Dacă acoperirea este mare şi liniile sursă netestate nu prezintă mare importanţă pentru calitatea generală a sistemului, atunci nu mai sunt necesare teste suplimentare.

Testarea aplicaţiei testează aplicaţia ca întreg şi este determinată de scenariile echipei de analiză. Aplicaţia trebuie să execute cu succes toate scenariile pentru a putea fi pusă la dispoziţia clientului. Spre deosebire de testarea internă şi a unităţilor, care se face prin program, testarea aplicaţiei se face de obicei cu scripturi care rulează sistemul cu o serie de parametri şi colectează rezultatele. In trecut, aceste scripturi erau create manual. In prezent, există instrumente care automatizează şi acest proces. Majoritatea aplicaţiilor din zilele noastre au interfeţe grafice (GUI). Testarea interfeţei grafice pentru asigurarea calităţii poate pune anumite probleme. Cele mai multe interfeţe, dacă nu chiar toate, au bucle de evenimente, care conţin cozi de mesaje de la mouse, tastatură, ferestre etc. Asociate cu fiecare eveniment sunt coordonatele ecran. Testarea interfeţei presupune deci memorarea tuturor acestor informaţii şi elaborarea unei modalităţi prin care mesajele să fie trimise din nou aplicaţiei, la un moment ulterior.

Testarea la stres determină calitatea aplicaţiei în mediul său de execuţie. Ideea este crearea unui mediu mai solicitant decât cel în care aplicaţia va rula în mod obişnuit. Aceasta este cea mai dificilă şi complexă categorie de teste. Sistemul este supus unor cerinţe din ce în ce mai numeroase, până când acesta cade. Apoi produsul este reparat şi testul de stres se repetă până când se atinge un nivel de stres mai ridicat decât nivelul aşteptat de pe staţia clientului. Deseori apar aici conflicte între teste. Fiecare test funcţionează corect atunci când este făcut separat. Când două teste sunt rulate în paralel, unul sau ambele teste pot eşua. Cauza este de obicei managementul incorect al accesului la resurse critice. Mai apar şi probleme de memorie, când un test îşi alocă memorie şi apoi nu o mai dealocă. Testul pare să funcţioneze corect, însă după ce este rulat de mai multe ori, memoria disponibilă se reduce iar sistemul cade.

1.3 Concluzii în acest curs s-a făcut mai întâi o introducere în problematica domeniului ingineriei

programării, insistându-se pe cauzele care au determinat dezvoltarea sa: creşterea continuă a complexităţii sistemelor software. Apoi s-au descris cele patru faze fundamentale ale metodologiilor ingineriei programării: analiza, proiectarea, implementarea şi testarea, necesare pentru realizarea unor sisteme de calitate.

Page 14: Ingineria Programarii

14/135

2 Metodologii de dezvoltare a programelor 1. Etapele dezvoltării programelor 2. Metodologii generice 2.1. Metodologia secvenţială 2.2. Metodologia ciclică 2.3. Metodologia hibridă ecluză 3. Metodologii concrete 3.1. Metodologia cascadă 3.2. Metodologia spirală 3.3. Metodologia spirală WinWin 3.4. Prototipizarea 3.5. Metodologia Booch 3.6. Metode formale 3.7. Extreme Programming 4. Concluzii

2.1 Etapele dezvoltării programelor Când pornim la dezvoltarea unui program avem nevoie de:

• înţelegere clară a ceea ce se cere; • un set de metode şi instrumente de lucru; • un plan de acţiune.

Planul de acţiune se numeşte metodologie de dezvoltare. Dezvoltarea unui anumit program constă într-un set de paşi ce se fac pentru a-1 realiza. Luând în considerare tipul paşilor ce se efectuează se creează un model de lucru, ce poate fi aplicat unei serii mai largi de proiecte. Acesta este motivul pentru care planul de acţiune este numit model: el poate fi privit ca un şablon al dezvoltării de programe, în timpul dezvoltării programelor s-a constatat că există anumite tipuri de activităţi care trebuie făcute la un moment dat:

• Analiza cerinţelor: Se stabileşte ce anume vrea clientul ca programul să facă. Scopul este înregistrarea cerinţelor într-o manieră cât mai clară şi mai fidelă. Claritatea se referă la lipsa ambiguităţii iar fidelitatea la înregistrarea cât mai exactă (posibil cuvânt cu cuvânt);

• Proiectarea arhitecturală: Din motive de complexitate, programele mari nu pot fi concepute şi implementate ca o singură bucată. Programul va trebui construit aşadar din module sau componente. Proiectarea arhitecturală împarte sistemul într-un număr de module mai mici şi mai simple, care pot fi abordate individual;

• Proiectarea detaliată: Se realizează proiectarea fiecărui modul al aplicaţiei, în cele mai mici detalii;

• Scrierea codului: Proiectul detaliat este transpus într-un limbaj de programare. De obicei, aceasta se realizează modular, pe structura rezultată la proiectarea arhitecturală;

• Integrarea componentelor: Modulele programului sunt combinate în produsul final. Rezultatul este sistemul complet, în modelul numit big-bang componentele sunt dezvoltate şi testate individual, după care sunt integrate în sistemul final. Având în vedere că funcţionarea corectă a componentelor individuale a fost testată, integrarea ar trebui să fie o formalitate. Din păcate, componentele nu pot fi testate exhaustiv, iar când acestea lucrează împreună pot să apară situaţii pe care o anumită componentă nu le-a întâlnit în procesul de testare sau conflicte între anumite componente (de exemplu, conflicte de partajare a resurselor). S-a

Page 15: Ingineria Programarii

15/135

constatat că atunci când se aplică acest model, timpul de testare explodează, proiectul devenind greu de controlat; aceasta justifică denumirea de „big-bang". Modelul incremental propune crearea unui nucleu al aplicaţiei şi integrarea a câte o componentă la un moment dat, urmată imediat de testarea sistemului obţinut. Astfel, se poate determina mai uşor unde anume apare o problema în sistem. Acest tip de integrare oferă de obicei rezultate mai bune decât modelul big-bang;

• Validarea: în procesul de validare ne asigurăm că programul îndeplineşte cerinţele utilizatorului, întrebarea la care răspundem este: construim produsul corect? Un exemplu de validare este testul de acceptare, în care produsul este prezentat clientului. Clientul spune dacă este mulţumit cu produsul sau dacă mai trebuie efectuate modificări;

• Verificarea: în procesul de verificare ne asigurăm că programul este stabil şi că funcţionează corect din punctul de vedere al dezvoltatorilor, întrebarea la care răspundem este: construim corect produsul?

• Întreţinerea: După ce programul este livrat clientului, mai devreme sau mai târziu sunt descoperite defecte sau erori ce trebuie reparate. De asemenea, pot apărea schimbări în specificaţiile utilizatorilor, care vor diverse îmbunătăţiri, întreţinerea constă în gestionarea acestor probleme.

Se poate constata uşor că aceste activităţi sunt în strânsă legătură cu cele patru faze ale ingineriei programării: analiza, proiectarea, implementarea şi testarea.

2.2 Metodologii generice în acest paragraf, vor fi prezentate trei categorii importante de metodologii: secvenţială,

ciclică şi hibridă, în metodologia secvenţială (cascadă), cele patru faze urmează una alteia într-o modalitate serială, în metodologia ciclică (spirală), fazele sunt dispuse în cicluri care îşi generează incremental contribuţiile la sistemul final. Metodologia hibridă (ecluză) combină progresul constant al metodologiei secvenţiale cu incrementale iterative ale metodologiei ciclice.

2.2.1 Metodologia secvenţială în metodologia secvenţială, cunoscută şi sub numele de metodologia „cascadă", are loc

mai întâi faza de analiză, apoi cea de proiectare, urmată de cea de implementare, iar în final se realizează testarea. Echipele care se ocupă de fiecare fază pot fi diferite, iar la fiecare tranziţie de fază poate fi necesară o decizie managerială.

Figura 2-1 Metodologia secvenţială

Avantaje Metodologia secvenţială este potrivită când complexitatea sistemului este mică iar

cerinţele sunt statice. Ea spune că mai întâi trebuie să ne gândim ce trebuie construit, apoi să stabilim un plan de lucru şi apoi să realizăm proiectul, ţinând cont de standardele de calitate. De asemenea, se aliniază metodelor de inginerie hardware. Forţează menţinerea unei discipline de lucru care evită presiunea scrierii codului înainte de a cunoaşte precis ce produs va trebui de fapt construit. De multe ori, echipa de implementare se află în situaţia de a programa înainte de finalizarea analizei, ceea ce conduce inevitabil la descoperirea unor părţi de cod inutile sau care contribuie foarte puţin (poate chiar şi ineficient) la funcţionalitatea produsului final. Totuşi, acest cod devine un balast foarte costisitor: dificil de abandonat şi

Page 16: Ingineria Programarii

16/135

greu de schimbat. Această metodologie forţează analiza şi planificarea înaintea implementării, o practică foarte nimerită în multe situaţii.

Un mare număr de sisteme software din trecut au fost construite cu o metodologie secvenţială. Multe companii îşi datorează succesul acestui mod de realizare a programelor. Trebuie spus totuşi şi că presiunea de schimbare din partea surselor externe era destul de limitată la momentul respectiv.

Dezavantaje Unul din principalele dezavantaje ale metodologiei secvenţiale este faptul că acordă o

foarte mare importanţă fazei de analiză. Membrii echipei de analiză ar trebui să fie probabil clarvăzători ca să poată defini toate detaliile aplicaţiei încă de la început. Greşelile nu sunt permise, deoarece nu există un proces de corectare a erorilor după lansarea cerinţelor finale. Nu există nici feedback de la echipa de implementare în ceea ce priveşte complexitatea codului corespunzător unei anumite cerinţe. O cerinţă simplu de formulat poate creşte considerabil complexitatea implementării, în unele cazuri, este posibil să fie chiar imposibil de implementat cu tehnologia actuală. Dacă echipa de analiză ar şti că o cerinţă nu poate fi implementată, ei ar putea-o schimba cu o cerinţă diferită care să satisfacă cele mai multe dintre necesităţi şi care să fie mai uşor de efectuat.

Comunicarea dintre echipe este o problemă: cele patru echipe pot fi diferite iar comunicarea dintre ele este limitată. Modul principal de comunicare sunt documentele realizate de o echipă şi trimise următoarei echipe cu foarte puţin feedback. Echipa de analiză nu poate avea toate informaţiile privitoare la calitate, performanţă şi motivare.

într-o industrie în continuă mişcare, metodologia secvenţială poate produce sisteme care, la vremea lansării, să fie deja învechite. Accentul atât de mare pus pe planificare nu poate determina răspunsuri suficient de rapide la schimbare. Ce se întâmplă dacă clientul îşi schimbă cerinţele după terminarea fazei de analiză? Acest lucru se întâmplă însă frecvent; după ce clientul vede prototipul produsului, el îşi poate schimba unele cerinţe.

2.2.2 Metodologia ciclică Metodologia ciclică, cunoscută şi sub numele de metodologia „spirală", încearcă să

rezolve unele din problemele metodologiei secvenţiale. Şi această metodologie are patru faze, însă în fiecare fază se consumă un timp mai scurt, după care urmează mai multe iteraţii prin toate fazele. Ideea este de fapt următoarea: gândeşte un pic, planifică un pic, implementează un pic, testează un pic şi apoi ia-o de la capăt. In mod ideal, fiecărei faze trebuie să i se acorde atenţie şi importanţă egale.

Documentele de la fiecare fază îşi schimbă treptat structura şi conţinutul, la fiecare ciclu sau iteraţie. Pe măsură ce procesul înaintează, sunt generate din ce în ce mai multe detalii. In final, după câteva cicluri, sistemul este complet şi gata de lansare. Procesul poate însă continua pentru lansarea mai multor versiuni ale produsului.

Figura 2-2 Metodologia ciclică

Avantaje Metodologia ciclică se bazează pe ideea perfecţionării incrementale ale metodologiei

secvenţiale. Permite feedback-ul de la fiecare echipă în ceea ce priveşte complexitatea cerinţelor. Există etape în care pot fi corectate eventualele greşeli privind cerinţele. Clientul poate arunca o privire asupra rezultatului şi poate oferi informaţii importante mai ales în faza dinaintea lansării produsului. Echipa de implementare poate trimite echipei de analiză

Page 17: Ingineria Programarii

17/135

informaţii privind performanţele şi viabilitatea sistemului. Acesta se poate adapta mai bine progresului tehnologic: pe măsură ce apar noi soluţii, ele pot fi încorporate în arhitectura produsului.

Dezavantaje Metodologia ciclică nu are nici o modalitate de supraveghere care să controleze

oscilaţiile de la un ciclu la altul. In această situaţie, fiecare ciclu produce un efort mai mare de muncă pentru ciclul următor, ceea ce încarcă orarul planificat şi poate duce la eliminarea unor funcţii sau la o calitate scăzută. Lungimea sau numărul de cicluri poate creşte foarte mult. De vreme ce nu există constrângeri asupra echipei de analiză să facă lucrurile cum trebuie de prima dată, acest fapt duce la scăderea responsabilităţii. Echipa de implementare poate primi sarcini la care ulterior se va renunţa. Echipa de proiectare nu are o viziune globală asupra produsului şi deci nu poate realiza o arhitectură completă. Nu există termene limită precise. Ciclurile continuă fără o condiţie clară de terminare. Echipa de implementare poate fi pusă în situaţia nedorită în care arhitectura şi cerinţele sistemului sunt în permanenţă schimbare.

2.2.3 Metodologia hibridă ecluză Metodologia ecluză (engl. „watersluice"), propusă de Ronald LeRoi Burback (1998),

separă aspectele cele mai importante ale procesului de dezvoltare a unui produs software de detaliile mai puţin semnificative şi se concentrează pe rezolvarea primelor. Pe măsură ce procesul continuă, detaliile din ce în ce mai fine sunt rafinate, până când produsul poate fi lansat. Această metodologie hibridă preia natura iterativă a metodologiei spirală, la care adaugă progresul sigur al metodologiei cascadă.

AnalizaProiectare

ImplementareTestare

ProiectareImplementare

Testare

ImplementareTestare

Testare

Produs

Schita de principiu

Prototip

Alfa / Beta

Produs

Figura 2-3 Metodologia hibridă ecluză

La început, într-un proces iterativ, fazele de analiză, proiectare, implementare şi testare sunt împărţite în mai multe sarcini potenţiale, fiecăruia atribuindu-i-se o prioritate care reflectă beneficiul îndeplinirii sarcinii respective pentru scopul final. La fiecare moment se execută sarcina cu prioritate maximă. In funcţie de dimensiunea echipelor, mai multe sarcini pot fi realizate în paralel. Sarcinile rămase, de prioritate minimă, sunt păstrate pentru

Page 18: Ingineria Programarii

18/135

examinare ulterioară. Descompunerea problemei este foarte importantă. Cu cât descompunerea şi stabilirea priorităţilor sunt mai bune, cu atât mai eficientă este metodologia.

Pe măsură ce sarcinile stabilite sunt îndeplinite, noi sarcini pot fi descoperite. Acestea sunt adăugate sarcinilor rămase nesoluţionate şi se reatribuie priorităţile. Procesul continuă până când produsul este gata de lansare.

Priorităţile se stabilesc pe baza unei funcţii de prioritate, care depinde atât de domeniul problemei şi de normele firmei. Ea trebuie să realizeze un compromis între cantitate şi calitate, între funcţionalitate şi constrângerile privind resursele, între aşteptări şi realitate. Toate funcţiile de prioritate ar trebuie să aibă ca prim scop lansarea produsului.

Pe lângă rolul evident de a stabili priorităţile şi deci ordinea de execuţie a sarcinilor de lucru, funcţia mai trebuie să gestioneze sarcinile conflictuale şi nemonotone. Ea trebuie să împartă aceste sarcini în grupuri consistente, să reglementeze selecţia grupurilor consistente şi apoi să dirijeze selecţia sarcinilor din cadrul grupurilor. Pe măsură ce sistemul creşte, funcţia de prioritate trebuie să aleagă sarcini consistente cu partea deja constituită din sistem. O sarcină nemonotonă vine în contradicţie cu sistemul realizat deja şi trebuie eliminată dacă nu este absolut necesară pentru succesul sistemului.

Odată ce o componentă este terminată şi acceptată de echipă, schimbările asupra sa sunt îngheţate. Componenta va fi schimbată numai dacă modificările sunt absolut necesare iar echipa este dispusă să întârzie lucrul la restul sistemului pentru a le efectua. Schimbările trebuie să fie puţine la număr, bine justificate şi documentate.

Etapele principale ale metodei sunt: schiţa de principiu, prototipul, versiunile alfa/beta şi produsul final.

În prima etapă, schiţa de principiu, echipele lucrează simultan la toate fazele problemei. Echipa de analiză sugerează cerinţele. Echipa de proiectare le discută şi trimite sarcinile critice de implementare echipei de implementare. Echipa de testare pregăteşte şi dezvoltă mediul de test în funcţie de cerinţe. Echipa de implementare se concentrează asupra sarcinilor critice, care în general sunt cele mai dificile. Această abordare contrastează cu practica curentă de realizare mai întâi a sarcinilor simple. Totuşi, majoritatea produselor care urmează acest model eşuează. Odată ce componentele critice au fost realizate, sistemul este gata de a face tranziţia către stadiul de prototip. Unul din scopurile aceste etape este de a se convinge echipele că o soluţie poate fi găsită şi pusă în practică.

În cea de a doua etapă, de prototip, cerinţele şi documentul cerinţelor sunt îngheţate. Schimbările în cerinţe sunt încă permise, însă ar trebuie să fie foarte rare şi numai dacă sunt absolut necesare, deoarece modificările cerinţelor în acest stadiu al proiectului sunt foarte costisitoare. Este posibilă totuşi ajustarea arhitecturii, pe baza noilor opţiuni datorate tehnologiei. După ce sarcinile critice au fost terminate, echipa de implementare se poate concentra pe extinderea acestora, pentru definirea cât mai multor aspecte ale aplicaţiei. Unul din scopurile acestei etape este de a convinge persoanele din afara echipelor că o soluţie este posibilă.

Acum produsul este gata pentru lansarea versiunilor alfa şi beta. Arhitectura este îngheţată, iar accentul cade pe implementare şi asigurarea calităţii. Prima versiune lansată se numeşte în general alfa. Produsul este încă imatur; numai sarcinile critice au fost implementate la calitate ridicată. Numai un număr mic de clienţi sunt în general dispuşi să accepte o versiune alfa şi să-şi asume riscurile asociate. O a doua lansare reprezintă versiunea beta. Rolul său este de a convinge clienţii că aplicaţia va fi un produs adevărat şi de aceea se adresează unui număr mai mare de clienţi. Când o parte suficient de mare din sistem a fost construită, poate fi lansat în sfârşit produsul. în această etapă, implementarea este îngheţată şi accentul cade pe asigurarea calităţii. Scopul este realizarea unui produs competitiv. In produsul final nu se acceptă erori critice.

Avantaje

Page 19: Ingineria Programarii

19/135

Metodologia ecluză recunoaşte faptul că oamenii fac greşeli şi că nici o decizie nu trebuie să fie absolută. Echipele nu sunt blocate într-o serie de cerinţe sau într-o arhitectură imobilă care se pot dovedi mai târziu inadecvate sau chiar greşite. Totuşi, pentru respectarea termenelor limită, metodologia impune date de îngheţare a unor faze. Există timp suficient pentru corectarea greşelilor decizionale pentru atingerea unui nivel suficient de ridicat de încredere. Se pune mare accent pe comunicarea între echipe, ceea ce reduce cantitatea de cod inutil la care ar trebui să se renunţe în mod normal. Metodologia încearcă să mute toate erorile la începutul procesului, unde corectarea, sau chiar reînceperea de la zero a lucrului, nu sunt foarte costisitoare.

Dezavantaje Metodologia presupune asumarea unor responsabilităţi privind delimitarea etapelor şi

îngheţarea succesivă a fazelor de dezvoltare. Ea presupune crearea unui mediu de lucru în care acceptarea responsabilităţii pentru o decizie care se dovedeşte mai târziu greşită să nu se repercuteze în mod negativ asupra individului. Se doreşte de asemenea schimbarea atitudinii echipelor faţă de testare, care are loc încă de la început, şi faţă de comunicarea continuă, care poate fi dificilă, întrucât cele patru faze reprezintă perspective diferite asupra realizării produsului.

2.3 Metodologii concrete

2.3.1 Metodologia cascadă Metodologia cascadă, propusă de Barry Boehm, este una din cele mai cunoscute

exemple de metodologie de ingineria programării. Există numeroase variante ale acestui proces. Intr-o variantă detaliată, metodologia cascadă cuprinde următoarele etape:

Cerinte sistem si validare

Cerinte software si validare

Analiza si validare

Proiectare detaliata si validare

Scriere cod si debug

Testare si validare

Intretinere si revalidare

Figura 2-4 Metodologia cascadă

După fiecare etapă există un pas de validare. Procesul „curge" de la etapă la etapă, ca apa într-o cascadă, în descrierea originară a lui Boehm, există o întoarcere, un pas înapoi interactiv între fiecare două etape. Astfel, metoda cascadă este de fapt o combinaţie de metodologie secvenţială cu elemente ciclice. Totuşi, în practica inginerească, termenul „cascadă" este utilizat ca un nume generic pentru orice metodologie secvenţială.

Page 20: Ingineria Programarii

20/135

Acesta este modelul după care de obicei sistemele sunt dezvoltate în practică. De asemenea, reordonarea fazelor s-a dovedit a fi sub-optimală. Există o mare atracţie pentru acest model datorită experienţei, tradiţiei în aplicarea sa şi succesului pe care 1-a implicat. O sarcină complexă este împărţită în mai mulţi paşi mici, ce sunt mai uşor de administrat. Fiecare pas are ca rezultat un produs bine definit (documente de specificaţie, model, etc.)

Modelul cascadă cu feedback propune remedierea problemelor descoperite în pasul precedent. Problemele la pasul /' care sunt descoperite la pasul / + 3 rămân neremediabile. Un model realist ar trebui să ofere posibilitatea ca de la un anumit nivel să se poată reveni la oricare dintre nivelele anterioare.

Dezavantajul principal al modelului în cascadă apare deoarece clientul obţine o viziune practică asupra produsului doar în momentul terminării procesului de dezvoltare. De asemenea, modelul nu are suficientă putere descriptivă, în sensul că nu integrează activităţi ca managementul resurselor sau managementul configuraţiei. Aceasta face dificilă coordonarea proiectului.

După cum am menţionat la prezentarea metodologiei generice secvenţiale, şi modelul cascadă impune îngheţarea specificaţiilor foarte devreme în procesul de dezvoltare pentru a evita iteraţiile frecvente (reîntoarcerile în fazele anterioare atunci când în faza curentă s-au detectat erori: în timpul analizei se descoperă erori de specificaţii, în timpul implementării se descoperă erori de specificaţii/proiectare etc., astfel încât procesul poate implica multiple secvenţe de iteraţii ale activităţilor de dezvoltare), îngheţarea prematură a cerinţelor conduce la obţinerea unui produs prost structurat şi care nu execută ceea ce doreşte utilizatorul. Conduce de asemenea la obţinerea unei documentaţii neadecvate deoarece schimbările intervenite în iteraţiile frecvente nu sunt actualizate în toate documentele produse.

2.3.2 Metodologia spirală Metodologia spirală, propusă tot de Boehm, este un alt exemplu bine cunoscut de

metodologie a ingineriei programării. Acest model încearcă să rezolve problemele modelului în cascadă, păstrând avantajele acestuia: planificare, faze bine definite, produse intermediare. El defineşte următorii paşi în dezvoltarea unui produs:

• studiul de fezabilitate; • analiza cerinţelor; • proiectarea arhitecturii software; • implementarea.

Modelul în spirală recunoaşte că problema principală a dezvoltării programelor este riscul. Riscul nu mai este eliminat prin aserţiuni de genul: „în urma proiectării am obţinut un model corect al sistemului", ca în modelul cascadă. Aici riscul este acceptat, evaluat şi se iau măsuri pentru contracararea efectelor sale negative. Exemple de riscuri:

• în timpul unui proces îndelungat de dezvoltare, cerinţele noi ale clientului sunt ignorate;

• clientul schimbă cerinţele; • o firmă concurentă lansează un program rival pe piaţă; • un dezvoltator/arhitect părăseşte echipa de dezvoltare; • o echipă nu respectă un termen de livrare pentru o anumită componentă.

In modelul spirală se consideră că fiecare pas din dezvoltare conţine o serie de activităţi comune:

• pregătirea: se identifică obiectivele, alternativele, constrângerile; • gestionarea riscului: analiza şi rezolvarea situaţiilor de risc; • activităţi de dezvoltare specifice pasului curent (de exemplu analiza

specificaţiilor sau scrierea de cod);

Page 21: Ingineria Programarii

21/135

• planificarea următorului stadiu: termenele limită, resurse umane, revizuirea stării proiectului.

Metodologia spirală cuprinde următoarele etape, grupate pe patru cicluri:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18 19

20

21

Figura 2-5 Metodologia spirală

• Ciclul l - Analiza preliminară: 1. Obiective, alternative, constrângeri 2. Analiza riscului şi prototipul 3. Conceperea operaţiilor 4. Cerinţele şi planul ciclului de viaţă 5. Obiective, alternative, constrângeri 6. Analiza riscului şi prototipul

• Ciclul 2 - Analiza finală: 7. Simulare, modele, benchmark-uri 8. Cerinţe software şi validare 9. Plan de dezvoltare 10. Obiective, alternative, constrângeri 11. Analiza riscului şi prototipul

• Ciclul 3 - Proiectarea: 12. Simulare, modele, benchmark-uri 13. Proiectarea produsului software, validare şi verificare 14. Integrare şi plan de test 15. Obiective, alternative, constrângeri 16. Analiza riscului şi prototipul operaţional

• Ciclul 4 - Implementarea şi testarea: 17. Simulare, modele, benchmark-uri 18. Proiectare detaliată 19. Cod 20. Integrarea unităţilor şi testarea acceptării 21. Lansarea produsului

Page 22: Ingineria Programarii

22/135

Procesul începe în centrul spiralei. Fiecare ciclu terminat reprezintă o etapă. Pe măsură ce spirala este parcursă, produsul se maturizează. Cu fiecare ciclu, sistemul se apropie de soluţia finală. Deşi este considerată ca un exemplu generic pentru metodologia ciclică, metoda are şi elemente secvenţiale, puse în evidenţă de evoluţia constantă de la o etapă la alta.

2.3.3 Metodologia spirală WinWin Această metodologie extinde spirala Boehm prin adăugarea unui pas de stabilire a

priorităţii la începutul fiecărui ciclu din spirală şi prin introducerea unor scopuri intermediare, numite puncte ancoră. Procesul WinWin identifică un punct de decizie. Pentru fiecare punct de decizie, se stabilesc obiectivele, constrângerile şi alternativele.

Punctele ancoră stabilesc trei scopuri intermediare. Primul punct ancoră, numit obiectivul ciclului de viaţă, precizează cazurile sigure de funcţionare pentru întregul sistem, arătând că există cel puţin o arhitectură fezabilă (adică posibilă din punct de vedere practic) care satisface scopurile sistemului. Primul scop intermediar este stabilit când sunt terminate obiectivele de nivel înalt ale sistemului, arhitectura, modelul ciclului de viaţă şi prototipul sistemului. Această primă ancoră spune de ce, ce, când, cine, unde, cum şi estimează costul produsului. După executarea acestor operaţii, este disponibilă analiza de nivel înalt a sistemului.

Al doilea punct ancoră defineşte arhitectura ciclului de viaţă, iar al treilea - capacitatea operaţională iniţială, incluzând mediul software necesar, hardware-ul, documentaţia pentru client şi instruirea acestuia.

Aceste puncte ancoră corespund etapelor majore din ciclul de viaţă al unui produs: dezvoltarea iniţială, lansarea, funcţionarea, întreţinerea şi ieşirea din funcţiune.

2.3.4 Prototipizarea O problemă generală care apare la dezvoltarea unui program este să ne asigurăm că

utilizatorul obţine exact ceea ce vrea. Prototipizarea vine în sprijinul rezolvării acestei probleme, încă din primele faze ale dezvoltării, clientului i se prezintă o versiune funcţională a sistemului. Această versiune nu reprezintă întregul sistem, însă este o parte a sistemului care cel puţin funcţionează.

Prototipul ajută clientul în a-şi defini mai bine cerinţele şi priorităţile. Prin intermediul unui prototip, el poate înţelege ce este posibil şi ce nu din punct de vedere tehnologic. Prototipul este de obicei produs cât mai repede; pe cale de consecinţă, stilul de programare este de obicei (cel puţin) neglijent. Insă scopul principal al prototipului este de a ajuta în fazele de analiză şi proiectare şi nu folosirea unui stil elegant.

Se disting două feluri de prototipuri: • de aruncat (throw-away); • evoluţionar.

În cazul realizării unui prototip de aruncat, scopul este exclusiv obţinerea unei specificaţii. De aceea nu se acordă nici o importanţă stilului de programare şi de lucru, punându-se accent pe viteza de dezvoltare. Odată stabilite cerinţele, codul prototipului este „aruncat", sistemul final fiind rescris de la început, chiar în alt limbaj de programare.

În cazul realizării unui prototip evoluţionar, scopul este de a crea un schelet al aplicaţiei care să poată implementa în primă fază o parte a cerinţelor sistemului. Pe măsură ce aplicaţia este dezvoltată, noi caracteristici sunt adăugate scheletului existent, în contrast cu prototipul de aruncat, aici se investeşte un efort considerabil într-un design modular şi extensibil, precum şi în adoptarea unui stil elegant de programare.

Această metodă are următoarele avantaje: • permite dezvoltatorilor să elimine lipsa de claritate a specificaţiilor;

Page 23: Ingineria Programarii

23/135

• oferă utilizatorilor şansa de a schimba specificaţiile într-un mod ce nu afectează drastic durata de dezvoltare;

• întreţinerea este redusă, deoarece validarea se face pe parcursul dezvoltării; • se poate facilita instruirea utilizatorilor finali înainte de terminarea produsului.

Dintre dezavantajele principale ale prototipizării amintim: • deoarece prototipul rulează într-un mediu artificial, anumite dezavantaje ale

produsului final pot fi scăpate din vedere de clienţi; • clientul nu înţelege de ce produsul necesită timp suplimentar pentru dezvoltare,

având în vedere că prototipul a fost realizat atât de repede; • deoarece au în fiecare moment şansa de a face acest lucru, clienţii schimbă

foarte des specificaţiile; • poate fi nepopulară printre dezvoltatori, deoarece implică renunţarea la propria

muncă.

2.3.5 Metodologia Booch Această metodologie asigură o dezvoltare orientată obiect în fazele de analiză şi

proiectare. Faza de analiză este împărţită în mai mulţi paşi. Primul pas este stabilirea cerinţelor din perspectiva clientului, generând o descriere de nivel înalt a funcţionării şi structurii sistemului. Al doilea pas este analiza domeniului, realizată prin definirea claselor: atributele, metodele, moştenirea. Faza de analiză este terminată cu un pas de validare. Această fază iterează cei trei paşi până când soluţia este consistentă.

Şi faza de proiectare este iterativă. Design-ul logic este transformat în design fizic, cu detalii privind firele de execuţie, procesele, performanţele, tipurile de date, structurile de date etc. Se creează un prototip şi apoi se testează. Faza iterează design-ul logic, cel fizic, prototipurile şi testarea.

Metodologia Booch este secvenţială în sensul că mai întâi este terminată analiza şi apoi proiectarea. Ea este ciclică datorită iteraţiilor din fiecare fază. Metoda se concentrează în special asupra acestor două faze, de analiză şi proiectare, fără să insiste foarte mult asupra implementării şi testării.

2.3.6 Metode formale In acest model de dezvoltare, sunt folosite formalismul şi rigoarea matematicii. In prima

fază este construită o specificaţie în limbaj matematic. Apoi, această specificaţie este transformată în programe, de obicei într-un proces incremental.

Avantaje: • precizia obţinută prin specificarea formală; • păstrarea corectitudinii în timpul transformării specificaţiei în cod executabil; • oferă posibilitatea generării automate de cod; • sunt potrivite pentru sisteme cu cerinţe critice.

Dezavantaje: • specificarea formală este de obicei o barieră de comunicare între client şi analist; • necesită personal foarte calificat (deci mai scump); • folosirea impecabilă a tehnicilor specificării formale nu implică neapărat

obţinerea de programe sigure, deoarece anumite aspecte critice pot fi omise din specificaţiile iniţiale.

2.3.7 Extreme Programming Extreme Programming (Kent Beck, 1996) este o metodologie care propune rezolvări

originale pentru problemele care apar în dezvoltarea de programe. Fiind o tehnologie nouă (şi

Page 24: Ingineria Programarii

24/135

extremă) are atât adepţi cât şi critici. XP consideră că dezvoltarea programelor nu înseamnă ierarhii, responsabilităţi şi termene limită, aşa cum se află acestea pe masa administratorului, ci înseamnă colaborarea oamenilor din care este formată echipa. Aceştia sunt încurajaţi să îşi afirme personalitatea, să ofere şi să primească cunoştinţe şi să devină programatori străluciţi.

De asemenea, XP consideră că dezvoltarea de programe înseamnă în primul rând scrierea de programe. Această sintagmă banală se pare că este uitată de multe companii care se ascund în spatele proceselor de dezvoltare stufoase, a şedinţelor şi a rapoartelor de activitate. XP ne aminteşte cu respect ca fişierele PowerPoint nu se pot compila.

De altfel, inspirarea proceselor de dezvoltare a programelor din ingineria construcţiilor se pare că nu este cea mai fericită alegere. Este adevărat că un inginer care vrea să construiască un pod peste un râu face mai întâi măsurători, realizează un proiect şi abia apoi trece la execuţie, toate acestea într-un mod secvenţial şi previzibil. Dar dezvoltarea de programe nu seamănă cu aşa ceva, oricât am vrea să credem asta. Dacă inginerului constructor respectiv i s-ar schimba cerinţele de rezistenţă şi i s-ar muta malurile chiar când a terminat de construit jumătate de pod, putem fi siguri că acel inginer şi-ar schimba modul de lucru. Din păcate însă, nu ştim (încă) cum.

Iniţiatorii XP definesc următoarele două carte, ca bază filosofică pentru această metodologie.

Carta drepturilor dezvoltatorului: • Ai dreptul să ştii ceea ce se cere, prin cerinţe clare, cu declaraţii clare de

prioritate; • Ai dreptul să spui cât îţi va lua să implementezi fiecare cerinţă, şi să îţi

revizuieşti estimările în funcţie de experienţă; • Ai dreptul să îţi accepţi responsabilităţile, în loc ca acestea să-ţi fie asignate; • Ai dreptul să faci treabă de calitate în orice moment; • Ai dreptul la linişte, distracţie şi la muncă productivă şi plăcută. • Carta drepturilor clientului: • Ai dreptul la un plan general, să ştii ce poate fi făcut, când, şi la ce preţ; • Ai dreptul să vezi progresul într-un sistem care rulează şi care se dovedeşte că

funcţionează trecând teste repetabile pe care le specifici tu; • Ai dreptul să te răzgândeşti, să înlocuieşti funcţionalităţi şi să schimbi

priorităţile; • Ai dreptul să fii informat de schimbările în estimări, suficient de devreme pentru

a putea reduce cerinţele astfel ca munca să se termine la data prestabilită. Poţi chiar să te opreşti la un moment dat şi să rămâi cu un sistem folositor care să reflecte investiţia până la acea dată.

Aceste afirmaţii, deşi par de la sine înţelese, conţin semnificaţii profunde. Multe din problemele apărute în dezvoltarea programelor pornesc de la încălcarea acestor principii. Enumerăm pe scurt câteva dintre caracteristicile XP:

• Echipa de dezvoltare nu are o structură ierarhică. Fiecare contribuie la proiect folosind maximul din cunoştinţele sale;

• Scrierea de cod este activitatea cea mai importantă; • Proiectul este în mintea tuturor programatorilor din echipă, nu în documentaţii,

modele sau rapoarte; • La orice moment, un reprezentant al clientului este disponibil pentru clarificarea

cerinţelor; • Codul se scrie cât mai simplu; • Se scrie mai întâi cod de test; • Dacă apare necesitatea rescrierii sau eliminării codului, aceasta se face fără milă;

Page 25: Ingineria Programarii

25/135

• Modificările aduse codului sunt integrate continuu (de câteva ori pe zi); • Se programează în echipă (programare în perechi). Echipele se schimbă la

sfârşitul unei iteraţii (1-2 săptămâni); • Se lucrează 40 de ore pe săptămână, fără lucru suplimentar.

2.4 Concluzii Au fost prezentate aici cele mai importante metodologii de dezvoltare a programelor.

Mai întâi au fost descrise metodologiile generice: secvenţială, ciclică şi hibridă, cu avantajele şi dezavantajele fiecăreia. Apoi s-au amintit câteva metode concrete de dezvoltate: modelul cascadă, modelul spirală, WinWin, prototipizarea, metodologia Booch, metodele formale şi aşa-numita „programare extremă".

Page 26: Ingineria Programarii

26/135

3 Managementul unui proiect software

3.1 Managementul software Multe proiecte de dezvoltare software au probleme, mai devreme sau mai târziu

deoarece ori programul nu este livrat la timp, ori bugetul este depăşit, ori clienţii sunt nemulţumiţi. De multe ori cauzele sunt de natură tehnică. Insă, în la fel de multe situaţii, problemele îşi au originea în organizarea şi managementul proiectului. Când un produs este livrat prea târziu, motivele prezentate sunt în general următoarele:

• programatorii nu au spus adevărul despre situaţia reală a codului; • clientul nu ştia ce vrea de fapt; • echipa de management a subestimat timpul necesar terminării proiectului; • echipa de management nu a acordat suficient timp pentru planificarea atentă a

proiectului; • productivitatea programatorilor s-a dovedit mult inferioară aşteptărilor.

Aparent, nu e simplu să termini cu succes un proiect de dezvoltare software. Pe lângă aspectele tehnice, legate în general de cele patru faze prezentate anterior (analiză, proiectare, implementare, testare), s-a dovedit că aspectele privind organizarea şi managementul proiectului sunt cel puţin la fel de importante.

Un proiect de dezvoltare software nu este de obicei complet izolat. In cadrul firmei se poate lucra şi la alte proiecte şi deci poate fi necesară identificarea unor legături între proiecte, stabilirea de priorităţi etc. Pentru acest proces de meta-planificare (planificare a planificării) se foloseşte deseori termenul planificare informaţională. Scopul său este precizarea unor condiţii sau constrângeri pentru fiecare proiect concret.

De asemenea, nici din punct de vedere tehnic programul nu este pornit de la zero. El trebuie să fie interfaţat cu programe existente, să extindă funcţionalitatea altor programe, să folosească biblioteci de funcţii etc. De fapt, însuşi termenul de dezvoltare software poate fi considerat nepotrivit. Nu doar programul este dezvoltat, ci un întreg sistem. Software-ul este o componentă principală, dar nu este unică.

Să considerăm un sistem pentru automatizarea unei biblioteci. El va conţine diverse componente soft, precum baze de date despre cărţi şi persoane sau interfeţe pentru prelucrarea interactivă a cererilor utilizatorilor. Pe lângă acestea, trebuie găsite modalităţi de rezolvare a unor probleme de genul:

• identificarea electronică a cărţilor prin coduri de bare; • achiziţionarea de componente hardware care să scaneze aceste coduri şi să

creeze elemente de identificare pentru cărţile noi; • instruirea bibliotecarilor pentru utilizarea noilor instrumente de lucru (cursuri,

instruire practică etc.); • alcătuirea unei documentaţii prietenoase pentru abonaţi.

Un proiect de dezvoltare software poate fi văzut astfel:

Page 27: Ingineria Programarii

27/135

Program

Program

Software

Oameni Documentatie

Proceduri

Intrare Iesire

Con

stra

nger

i

Figura 3-1 Abordarea sistemică a unui proiect de dezvoltare software

Se observă că sistemul cuprinde un număr de componente. La rândul său, în sens mai restrâns, şi componenta software poate include mai multe module de program care interacţionează şi asigură funcţionalitatea dorită în mod colectiv.

Pe baza constrângerilor, se începe lucrul la un anumit proiect concret. Primul pas, de mare importanţă, este planificarea proiectului. Apoi, pe parcursul execuţiei trebuie controlate cinci entităţi: timpul, informaţiile, organizarea, calitatea şi banii.

3.1.1 Planificarea proiectului Planificarea proiectului presupune identificarea proprietăţilor care vor influenţa

dezvoltarea. Aceste proprietăţi nu sunt foarte bine înţelese decât după terminarea fazei de analiză. De aceea, planificarea are în mod necesar o natură dinamică.

Cooper (1984) subliniază 13 elemente importante ale planului proiectului: b) Introducerea: Sunt descrise condiţiile şi istoricul proiectului, împreună cu

scopurile, numele persoanelor responsabile şi un sumar al proiectului; c) Implicarea utilizatorului: Viitorii utilizatori vor fi implicaţi din timp în timp

în proiect. Planul trebuie să cuprindă ce informaţii, servicii şi resurse sunt asigurate de utilizatori şi când se va întâmpla acest lucru;

d) Riscurile: In orice moment există riscuri: hardware-ul nu va fi livrat la timp, personalul calificat este insuficient, lipsesc informaţii esenţiale ş.a.m.d. în mod ideal, riscurile potenţiale trebuie identificate cât mai devreme şi echipa de management trebuie să ia măsuri pentru eliminarea lor. Pe măsură ce necunoscutele proiectului devin mai numeroase, creşte şi numărul riscurilor;

e) Standardele, tehnicile, procedurile: Proiectele software sunt proiecte mari, în care sunt implicate multe persoane. De aceea, este nevoie de o disciplină de lucru clară, bazată pe standarde şi proceduri asupra cărora toată lumea a căzut de acord. O mare importanţă o are documentaţia: când trebuie livrată, cum este evaluată calitatea sa, cum se asigură permanenta sa actualizare;

Page 28: Ingineria Programarii

28/135

f) Organizarea proiectului: în cadrul echipei proiectului se identifică diverse roluri: project manager, tester, programator, analist etc. Aceste roluri trebuie clar delimitate iar atribuţiile fiecăruia trebuie bine precizate şi clarificate. In acest scop, poate fi necesară o perioadă de instruire a membrilor echipei;

g) Fazele proiectului: în primul curs au fost amintite fazele de dezvoltare ale unui produs software. Metodologiile de aplicare a acestora sunt numeroase. Echipa de management trebuie să se decidă care model va fi urmat şi care sunt paşii obligatorii. Proiectul trebuie divizat în părţi gestionabile care pot fi alocate unor echipe diferite. De asemenea, trebuie estimate timpul şi costul fiecărei faze. Tipuri diferite de proiecte au diferite caracteristici şi necesită deci modele diferite;

h) Analiza cerinţelor şi proiectarea: Se indică metodele şi tehnicile care vor fi utilizate în analiză şi proiectare, precum şi resursele şi instrumentele necesare pentru acestea;

i) Implementarea: Se identifică resursele şi instrumentele necesare pentru implementare şi se precizează cum va fi gestionat volumul foarte mare de documentaţie tehnică ce va rezulta de aici;

j) Testarea: Se descriu mediile şi echipamentele de test necesare; k) Resursele: Sunt enumerate resursele hardware şi instrumentele necesare

pentru proiect; l) Asigurarea calităţii: Se identifică procedurile care vor fi folosite pentru a

verifica dacă proiectul îndeplineşte standardele de calitate dorite; m) Modificările: Pentru un proiect, modificările sunt inevitabile. Echipa de

management trebuie să se asigure că aceste schimbări sunt tratate într-un mod organizat, pe baza unor proceduri bine definite. Fiecare modificare propusă trebuie înregistrată şi revăzută. Când o modificare este aprobată, se estimează costurile corespunzătoare. După încorporarea sa în sistemul existent, trebuie redactate noi versiuni ale documentaţiei pentru a reflecta schimbările din cod;

n) Livrarea: în final, trebuie respectate procedurile necesare pentru livrarea produsului către client.

Planul proiectului trebuie să furnizeze o imagine clară a ceea ce trebuie făcut, atât pentru client cât şi pentru echipa de realizare. Dacă obiectivele nu sunt clare, ele nu vor fi niciodată îndeplinite.

3.1.2 Controlul proiectului După ce planul proiectului a fost realizat şi aprobat, poate începe executarea propriu-

zisă. Pe parcursul executării, trebuie controlate următoarele dimensiuni: • timpul; • informaţiile; • organizarea; • calitatea; • banii.

Cursul de evoluţie al proiectului (aspectul timp) este greu de măsurat. Afirmaţiile de genul „90% din cod a fost deja scris" trebuie cântărite de două ori înainte de a fi crezute. De obicei, se prezintă o situaţie mai bună a proiectului decât este în realitate. Timpul necesar construirii unui sistem este în mod evident legat de dimensiunea sa şi deci de forţa de muncă utilizată. Cu cât un sistem este mai mare, cu atât este necesar mai mult timp pentru dezvoltarea sa, deşi se poate încerca scurtarea intervalului prin alocarea suplimentară de personal. Una din problemele care apar aici este găsirea unui compromis cât mai bun între timpul de dezvoltare şi resursele umane folosite. Totuşi, cu cât sunt implicate mai multe

Page 29: Ingineria Programarii

29/135

persoane, cu atât va creşte timpul pentru coordonare şi comunicare. Peste un anumit punct, mărirea personalului va avea ca efect creşterea timpului de dezvoltare. În acest sens, legea lui Brooks afirmă că adăugarea de personal la un proiect întârziat îl va întârzia şi mai mult.

Din punctul de vedere al informaţiilor, accentul principal cade pe realizarea documentaţiei: documentele utilizatorului, documentele tehnice şi documentaţia proiectului însuşi, care include starea curentă de lucruri, modificările convenite, deciziile luate.

În cadrul echipei, fiecare persoană trebuie să-şi cunoască foarte clar rolul. Dacă rolurile nu sunt definite sau sunt neclare, fiecare individ îşi va stabili propriile scopuri, care pot intra în contradicţie cu scopurile proiectului. Project manager-ul trebuie să ţină seama de aceste aspecte organizaţionale la alcătuirea echipei şi stabilirea atribuţiilor.

Calitatea este foarte importantă, deoarece clienţii nu se mulţumesc cu soluţii pur tehnice, ci doresc sisteme adecvate nevoilor lor reale. De multe ori, cerinţele de calitate pot fi conflictuale. Pe parcursul executării proiectului, acestea trebuie mereu evaluate, pentru ca să poată fi luate măsuri din timp pentru remedierea problemelor. Calitatea nu trebuie să fie o trăsătură adăugată, ci o caracteristică intrinsecă a sistemului.

Controlarea resurselor financiare înseamnă în mare parte controlarea costurilor de personal. Deşi costurile asociate hardware-ului şi diferitelor instrumente nu poate fi neglijat, acestea pot fi de obicei estimate destul de precis în fazele incipiente ale proiectului. Estimarea costului se reduce astfel la estimarea forţei de muncă necesare, care depinde de mulţi factori. Un factor important este dimensiunea software-ului (determinată de exemplu de mărimea codului). Există însă şi alţi factori care influenţează costurile sau productivitatea. O echipă bine echilibrată de persoane experimentate vor avea o productivitate mult mai mare decât o echipă nou formată de persoane fără experienţă.

3.2 Managementul configuraţiei Pe parcursul ciclului de viaţă al unui proiect de dezvoltare software sunt create şi

actualizate un mare număr de elemente, precum module de cod sursă, cerinţe de modificare etc. Pentru gestionarea acestora trebuie să existe un set de proceduri bine definite, care poartă numele de managementul configuraţiei.

De cele mai multe ori, un sistem .software nu este monolitic. Sistemele există în diferite versiuni şi configuraţii. Versiunile diferite apar atunci când sunt făcute modificări după ce sistemul a fost livrat clientului. Din timp în timp, clientul este confruntat cu o nouă lansare. Diferite versiuni pot exista şi pentru componentele din sistem. Dacă o modificare a fost aprobată, un programator poate implementa schimbarea prin rescrierea unei componente, în acelaşi timp, un altul poate utiliza în continuare versiunea anterioară a aceleiaşi componente.

La orice moment trebuie să existe o singură versiune oficială setului de documente legate de proiect. Aceasta se numeşte linie de bază (engl. „baseline"), definită ca standard IEEE: „o specificaţie sau un produs care a fost analizat şi acceptat în mod formal, care serveşte în continuare ca bază pentru dezvoltarea viitoare şi care poate fi modificat numai prin proceduri formale de control al modificărilor". Linia de bază este deci o bază de date partajată care conţine toate entităţile aprobate, denumite entităţi de configurare (engl. „configuration items"). O entitate de configurare este definită de asemenea ca standard IEEE astfel: „o colecţie de elemente hardware sau software tratate ca o unitate în scopul gestionării configuraţiei".

Exemple de entităţi de configuraţie sunt: • modulele de cod sursă; • modulele de cod obiect; • specificarea cerinţelor; • documentaţia de proiectare; • planul de test;

Page 30: Ingineria Programarii

30/135

• cazurile de test; • rezultatele de test; • manualul utilizatorului.

O sarcină fundamentală a managementului configuraţiei este menţinerea integrităţii acestui set de elemente, lucru deosebit de important atunci când modificările sunt integrate în sistem. Să presupunem că în faza de testare este descoperită o eroare majoră într-un modul. Atunci toţi paşii deja efectuaţi trebuie parcurşi în ordine inversă şi pe lângă modificarea codului trebuie modificate şi documentele de proiectare sau chiar cerinţele. Aceste schimbări pot interacţiona cu munca altor programatori, care folosesc versiunea anterioară. Mai rău, un alt programator poate efectua propriile sale modificări la acelaşi modul. Managementul configuraţiei se ocupă de gestionarea tuturor acestor schimbări pe parcursul întregului ciclu de viaţă al produsului.

Orice modificare propusă pentru linia de bază se numeşte cerere de modificare şi este tratată precum urmează:

• Modificarea este trimisă comisiei de control al configuraţiei (CCC). Pentru evaluarea propunerii, comisia are nevoie de informaţii privind modul în care schimbările vor afecta produsul şi procesul de dezvoltare: volumul de cod nou sau modificat, cerinţe suplimentare de test, legătura cu alte schimbări, costurile potenţiale, complexitatea modificării, severitatea erorii (dacă e cazul), resursele necesare etc.;

• CCC evaluează cererea, care poate fi aprobată, respinsă sau amânată dacă sunt necesare mai multe informaţii. Dacă este aprobată, se stabileşte un termen de implementare;

• CCC se asigură că toate entităţile de configuraţie afectate de schimbare vor fi actualizate corespunzător.

Am menţionat că entităţile din baza de date partajată pot fi utilizate fără restricţii de toţi membrii echipei. Dacă o entitate trebuie modificată, persoana responsabilă de schimbare primeşte o copie a entităţii şi apoi entitatea este blocată temporar, astfel încât nimeni să nu poată o modifica în acelaşi timp. Persoana desemnată acţionează asupra copiei. După ce modificarea este testată, este trimisă înapoi la CCC. După ce comisia o aprobă, entitatea este inclusă în baza de date, schimbarea e documentată şi apoi entitatea e deblocată. Şirul de modificări documentate formează istoricul reviziei entităţii.

Când o entitate e modificată, este păstrată şi versiunea veche. Aceasta poate fi încă utilizată de alte persoane până când acestea se adaptează schimbării. Având versiuni diferite pentru aceeaşi entitate, trebuie să existe o modalitate de a le distinge. De obicei, se preferă o schemă numerică, în care fiecare versiune următoare este identificată de următorul număr. Pentru o componentă X vom avea deci versiunile X.0, X.1, X.2 ş.a.m.d.

Schema GNU de numerotare denotă versiunea unui produs software printr-un triplet de întregi: versiunea majoră, versiunea minoră şi patch-ul. între două versiuni majore, în produs au loc schimbări majore ale funcţionalităţii şi în acest caz nu este garantată compatibilitatea înapoi.

Dimpotrivă, între două versiuni minore ale aceleiaşi versiuni majore trebuie să existe compatibilitate. De exemplu, formatele de fişiere suportate de o versiune ulterioară trebuie să fie suportate şi de versiunea minoră anterioară. Rolul unei versiuni minore este introducerea unor funcţii noi. Funcţiile vechi nu vor fi îndepărtate; totuşi documentaţia programului îl poate avertiza pe utilizator că la anumite funcţii se va renunţa în viitor.

Patch-ul nu poate realiza decât schimbări în implementarea unor funcţii. De obicei, rolul său este corectarea unor erori ale programului.

Aceste reguli nu se aplică însă şi pentru programele în versiuni alfa sau beta (O.F). înainte de versiunea 1.0, deoarece programul este în curs de formare, sunt permise modificări

Page 31: Ingineria Programarii

31/135

importante între versiuni. Totuşi, versiunea 1.0 trebuie să reprezinte o platformă stabilă, cu cât mai puţine bug-uri, care să poată fi utilizată cu încredere de clienţi. Nu e neapărat necesar ca aceasta să conţină cât mai multe funcţionalităţi, ci ca funcţiile implementate să fie cât mai sigure. Noi funcţionalităţi pot fi adăugate în versiunile următoare.

La nivelul codului sursă, managementul configuraţiei este sprijinit de instrumente puternice, care blochează şi deblochează elementele, asigură numerotarea automată a reviziilor şi furnizează utilizatorului ultima versiune disponibilă.

Un astfel de instrument este CVS (Concurrent Versions System), care permite crearea istoricului schimbărilor din cod. Când sursele sunt modificate uneori apar erori, care pot fi detectate abia după un interval mare de timp de la modificare, în aceste situaţii este utilă identificarea versiunilor vechi şi implicit a schimbărilor care au produs eroarea. CVS nu salvează toate fişierele sursă din diferitele versiuni, ci numai modificările aduse fişierelor. De asemenea, când mai mulţi programatori lucrează la acelaşi proiect, trebuie evitată suprascrierea de către o persoană a modificărilor alteia. O rezolvare a acestei probleme este blocarea unui fişier astfel încât acesta să nu poată fi modificat decât de un singur dezvoltator la un anumit moment de timp. Abordarea CVS se bazează pe izolarea programatorilor, astfel încât fiecare lucrează în propriul său director, iar la sfârşit, când toţi au terminat, programele sunt integrate.

3.3 Managementul echipei În multe organizaţii care dezvoltă proiecte software, programatorii, analiştii şi alţi

profesionişti lucrează împreună într-o echipă. Stabilirea structurii adecvate a unei echipei depinde de mulţi factori, precum numărul de persoane, experienţa şi gradul de implicare în proiect, tipul proiectului, diferenţele individuale şi stilul.

Orice tip de proiect implică un număr de sarcini de lucru. O responsabilitate fundamentală a project manager-ului este coordonarea şi distribuirea sarcinilor către toţi membrii echipei. Pentru un proiect mic, echipa va consta în câteva persoane. Pe măsură ce creşte dimensiunea proiectului, şi dimensiunea echipei va creşte. Echipele mari sunt dificil de coordonat iar volumul comunicării dintre membri tinde să crească exponenţial cu mărimea echipei. De aceea, echipele mari sunt împărţite în subechipe astfel încât majoritatea comunicării şi coordonării să se desfăşoare la nivelul acestora.

3.3.1 Managementul forţei de muncă O echipă este formată din indivizi, fiecare cu scopurile sale personale. Este sarcina

project manager-ului să formeze din aceştia o echipă, în care scopurile individuale sunt subscrise scopului proiectului ca întreg. Identificarea încă de la început a scopurilor proiectului este foarte importantă, iar aceste scopuri trebuie aduse la cunoştinţa membrilor echipei, în caz contrar, fiecare persoană îşi va stabili propriile scopuri, ceea ce poate cauza probleme serioase. De exemplu, un programator pune accent pe viteza programului, altul doreşte să folosească cât mai puţină memorie, iar altul consideră că cel mai important este să scrie cât mai multe linii de cod.

După ce au fost stabilite scopurile, trebuie monitorizate şi evaluate performanţele membrilor echipei. Acest lucru este dificil, deoarece mare parte din ceea ce se face este „invizibilă" iar progresul e greu de măsurat. De aceea, în mod ideal, se defineşte productivitatea drept suma funcţionalităţilor realizate în unitatea de timp. Din punct de vedere practic, productivitatea se defineşte ca numărul de linii de cod realizate pe lună-om. Toată lumea este de acord că nu este o măsură optimă, dar până în prezent nu s-a găsit una mai bună. Una din marile pericole ale utilizării acestei metode este faptul că programatorii tind să scrie cât mai mult cod cu putinţă, care are efecte negative. De asemenea, definiţia

Page 32: Ingineria Programarii

32/135

aceasta a productivităţii nu încurajează reutilizarea componentelor, care ar conduce la scrierea unui cod mai mic.

3.3.1.1 Mecanisme de coordonare Mintzberg (1983) distinge cinci configuraţii organizaţionale tipice:

• Structura simplă: într-o structură simplă există unul sau numai câţiva manageri şi un nucleu de persoane care lucrează la producţia proiectului propriu-zis. Această configuraţie este întâlnită de obicei în firmele noi, cu personal redus. Specializarea este limitată, la fel şi instruirea şi formalizarea. Mecanismul de coordonare corespunzător este supervizarea directă;

• Birocraţia automată: Când conţinutul sarcinilor este complet specificat, este posibilă executarea acestora pe bază de instrucţiuni precise. Exemple tipice ale acestui tip de configuraţie sunt producţia de masă şi liniile de asamblare. Instruirea este redusă, în schimb se pune mare accent pe specializare şi formalizare. Coordonarea se obţine prin standardizarea proceselor de lucru;

• Forma divizionalizată: în acest tip de configuraţie fiecărei divizii (fiecărui proiect) i se acordă o mare autonomie în ceea ce priveşte modul de atingere a scopurilor. Detaliile sunt stabilite de divizii. Coordonarea se obţine prin standardizarea producţiei. Controlul se exercită regulat prin măsurarea performanţelor diviziilor. Acest mecanism de coordonare este posibil numai atunci când este specificat precis rezultatul final;

• Birocraţia profesională: Dacă nu este posibilă specificarea rezultatului sau conţinutul sarcinilor, coordonarea poate fi realizată prin standardizarea calificării. Profesioniştii talentaţi se bucură de o autonomie considerabilă privind modul de îndeplinire a atribuţiilor;

• Adhocraţia: în proiecte mari şi/sau inovatoare, lucrul este divizat între mai mulţi specialişti, în acest caz, poate fi greu sau chiar imposibil de stabilit cu precizie ce trebuie să facă fiecare specialist sau modul în care trebuie să-şi îndeplinească sarcinile. Succesul proiectului depinde de capacitatea grupului de a atinge un scop nespecificat într-un mod nespecificat. Coordonarea se obţine prin ajustare reciprocă.

Trebuie spus că majoritatea organizaţiilor reale nu pot fi încadrate într-un singur tip de configuraţie. Diferite departamente ale firmei pot fi organizate diferit. De asemenea, tipurile prezentate reprezintă idei abstracte, în realitate, organizaţiile pot tinde spre unul din aceste tipuri, păstrând în acelaşi timp şi caracteristici ale celorlalte.

3.3.1.2 Stiluri de management Teoria lui Reddin (1970) a stilurilor de management pune accent pe factorii interni.

Autorul distinge două dimensiuni ale managementului forţei de muncă: • •gradul de dirijare a relaţiei: se referă la atenţia acordată individului şi relaţiilor

lui cu alţi indivizi din organizaţie; • •gradul de dirijare a sarcinii: priveşte atenţia acordată rezultatelor care trebuie

obţinute şi modului în care acestea trebuie obţinute. Gradele de dirijare pot fi scăzute sau ridicate, ceea ce conduce la patru combinaţii de

bază, prezentate în Tabelul 3-1. Desigur, aceste combinaţii corespund unor orientări extreme. Pentru fiecare dimensiune, există în realitate o întreagă gamă de nuanţe.

gradul de dirijare a sarcinii gradul de dirijare a

relaţiei scăzut ridicat

Page 33: Ingineria Programarii

33/135

scăzut stil de separare stil de angajament ridicat stil de relaţionare stil de integrare

Tabelul 3-1 Stilurile de management ale lui Reddin

Stilul cel mai potrivit pentru o anumită situaţie depinde de tipul lucrării: • Stilul de separare: Este cel mai potrivit pentru munca de rutină. Eficienţa este

tema centrală. Project manager-ul se comportă ca un birocrat care aplică reguli şi proceduri. Acest stil corespunde configuraţiei birocraţiei automate;

• Stilul de relaţionare: Este cel mai eficient în situaţiile în care oamenii trebuie motivaţi, coordonaţi şi instruiţi. Sarcinile sunt clar atribuite indivizilor. Munca nu are un caracter de rutină, ci este inovatoare şi specializată. Stilul este asemănător configuraţiei de adhocraţie;

• Stilul de angajament: Este cel mai eficient când se lucrează sub tensiune. Project manager-ul trebuie să ştie cum să se atingă scopurile fără să trezească resentimente. Stilul e asemănător configuraţiei de birocraţie profesională;

• Stilul de integrare: Se potriveşte situaţiilor când rezultatul este nesigur. Munca are o natură exploratorie şi sarcinile au un puternic caracter de interdependenţă. Rolul project managerului este să stimuleze şi să motiveze. Şi în acest caz, configuraţia de adhocraţie este cea mai apropiată.

Fiecare proiect de dezvoltare software poate necesita diferite mecanisme. De exemplu, pentru o echipă experimentată, care trebuie să dezvolte o aplicaţie bine specificată într-un domeniu familiar, coordonarea poate fi realizată prin standardizarea producţiei. Pentru o aplicaţie complexă şi novatoare, acest mecanism ar fi ineficient.

3.3.2 Organizarea echipei Indivizii cooperează în cadrul unei echipe pentru a obţine un rezultat optim, într-o

echipă se pot distinge diverse roluri: există manageri, testeri, designeri, programatori ş.a.m.d. În funcţie de dimensiunea proiectului, o persoană poate îndeplini mai multe roluri, sau diferite persoane pot avea acelaşi rol. Este foarte important ca atribuţiile rolurilor să fie bine precizate şi delimitate. Este de asemenea important ca anumite roluri să fie separate; de exemplu, se recomandă separarea echipei de test de echipa de implementare.

Echipele mari sunt greu de gestionat şi deseori sunt împărţite în subechipe. Prin definirea clară a responsabilităţilor subechipelor, comunicarea dintre membrii echipei se limitează la comunicarea în cadrul aceleiaşi subechipe, ceea ce măreşte productivitatea.

3.3.2.1 Organizarea ierarhică În mediile dedicate producerii de software, se întâlnesc deseori structuri ierarhice. În

funcţie de dimensiunea proiectului şi/sau a organizaţiei, pot exista diferite nivele de management.

Figura 3-2 prezintă un exemplu de organizare ierarhică. Dreptunghiurile denotă subechipele în care se lucrează efectiv. Cercurile reprezintă managerii. Aici avem două nivele de management. La nivelul inferior, subechipele sunt responsabile de dezvoltarea diferitelor părţi ale proiectului. Managerii acestora au rolul de a le coordona activitatea. La nivelul superior, se coordonează activitatea subechipelor pe ansamblu.

Page 34: Ingineria Programarii

34/135

P

A DCB

Subechipa A Subechipa B Subechipa C Testare Figura 3-2 Organizare ierarhică

Organizarea ierarhică reflectă deseori structura globală a sistemului care trebuie dezvoltat. Dacă sistemul are trei subsisteme majore, pot exista trei subechipe. De asemenea, pot exista unităţi funcţionale asociate cu responsabilităţi specifice proiectului, cum ar fi testarea.

O problemă a organizării ierarhice este distanţa dintre nivelele superioare şi inferioare ale piramidei. Munca „reală" se face de obicei pe nivelele inferioare, unde sunt prelucrate cunoştinţele concrete despre aplicaţie. Pe măsură ce ne ridicăm în ierarhie, cunoaşterea devine din ce în ce mai puţin specifică; de aceea managementul de pe nivelele superioare tinde să aplice coordonarea prin standardizare. Totuşi, chiar dacă deciziile importante se iau la aceste nivele, în multe cazuri sunt luate în considerare semnalele venite de pe nivelele de jos, care sunt de obicei însumate pe nivele intermediare.

De multe ori, pe măsură ce informaţia urcă în ierarhie, lucrurile tind să pară din ce în ce mai bune. Următorul scenariu nu este imposibil:

• nivelul de jos: avem probleme serioase la implementarea modulului X; • nivelul 1: sunt ceva probleme cu modulul X; • nivelul 2: progresul este constant, nu prevăd probleme reale; • nivelul de sus: totul merge conform planului.

Aceste distorsiuni sunt totuşi dificil de împiedicat. Ele sunt cauzate de faptul că linia ierarhică pe care se raportează progresul este aceeaşi cu cea utilizată pentru evaluarea performanţelor. Oamenii doresc evaluări pozitive şi de aceea tind să-şi modifice şi rapoartele în consecinţă. Dacă datele privind progresul proiectului sunt colectate şi prelucrate de persoane neimplicate în evaluarea membrilor echipei, cresc mult şansele ca şi informaţiile să fie suficient de corecte.

Un alt aspect problematic al acestui tip de organizare este faptul că individul este judecat, atât din punct de vedere social cât şi financiar, după nivelul pe care îl ocupă în ierarhie. Este de înţeles aspiraţia de a ajunge pe nivele din ce în ce mai înalte, deşi nu este foarte clar dacă acest lucru este de dorit. Principiul lui Peter din legile lui Murphy spune: într-o organizaţie ierarhică, fiecare angajat urcă până la nivelul său de incompetenţă. Totuşi, un programator bun nu e neapărat şi un bun manager. Programarea necesită competenţe diferite de cele ale managementului. Ideală ar fi stimularea oamenilor pentru menţinerea lor pe nivelele la care lucrează cel mai bine.

3.3.2.2 Organizarea matrice Acest tip de organizare este deseori întâlnită în situaţia când la un proiect software

lucrează oameni din diferite departamente şi care pot avea simultan mai mulţi şefi. Unitatea de bază este un grup mic, specializat. Pot exista mai multe unităţi cu aceeaşi specializare. Unităţile sunt organizate în conformitate cu specializarea lor; de exemplu: grafică computerizată, baze de date, interfeţe utilizator, controlul calităţii etc. Proiectele implică

Page 35: Ingineria Programarii

35/135

unităţi cu diferite specializări. Indivizii sunt organizaţi pe două axe: una reprezentând grupurile specializate iar cealaltă - proiectele la care participă:

programare în

timp real grafică baze de date testare

proiectul A X X proiectul B X X X proiectul C X X X

Tabelul 3-2 Organizare matrice

În această situaţie, project manager-ul este responsabil de terminarea cu succes a proiectului. El controlează una sau mai multe unităţi şi trebuie să menţină sau să mărească, pe termen lung, baza de cunoştinţe şi experienţa membrilor echipei. El poate pune accent pe ridicarea gradului de dirijare a sarcinii, în timp ce managerii unităţilor se pot concentra pe creşterea gradului de dirijare a relaţiei. O astfel de organizare poate fi foarte eficientă, cu condiţia să existe suficientă încredere reciprocă şi dorinţă de cooperare.

3.3.2.3 Echipa programatorului şef Acest tip de organizare a fost propus de Harlan Mills (1970). Nucleul unei astfel de

echipe constă în trei persoane. Programatorul şef este conducătorul echipei şi răspunde de proiectarea şi implementarea porţiunilor cheie ale sistemului. El este ajutat de un asistent. Dacă e nevoie, acesta poate lua temporar locul programatorului şef. Un bibliotecar se ocupă de administrare şi documentare. Pe lângă aceste trei persoane, mai poate exista un mic grup de experţi. Evident, programatorul şef joacă un rol central şi trebuie să fie foarte competent, mai ales în domeniul tehnic, dar şi managerial. Se pune problema dacă există suficienţi astfel de programatori şefi.

Noţiunea iniţială de programator şef pare elitistă. Pentru un proiect de dimensiuni foarte mari este mai plauzibilă ideea unui grup restrâns responsabil colectiv. Rolurile nu sunt structurate în funcţie de stadiile ciclului de viaţă a produsului. Nu există analişti, designeri, programatori. Testarea însă poate fi alocată unei anumite persoane. In grup pot exista diferite nivele de experienţă. Persoanele cele mai experimentate au rolul de programator şef şi asistent, începătorii învaţă din mers şi la început pot îndeplini rolul bibliotecarului.

3.3.2.4 Principii generale de organizare a unei echipe Indiferent de modul în care este organizată o echipă, cel mai important este faptul că

trebuie să fie o echipă. Testele privind productivitatea în proiectele de dezvoltare software au arătat că factorii privind capacităţile echipei au o importanţă mult mai mare decât orice altceva. Principiile de organizare a unei echipe în general se aplică şi în cazul proiectelor software:

• Folosirea unui număr mic de oameni capabili: Productivitatea maximă este obţinută de un grup relativ mic de oameni. Grupurile mari necesită mai multă comunicare, care are efecte negative asupra productivităţii şi duce la erori mai multe;

• Sarcinile trebuie puse în acord cu motivaţiile şi capacităţile oamenilor disponibili: Cu alte cuvinte, trebuie să ne asigurăm ca principiul lui Peter nu se aplică şi în cazul echipei noastre, în cele mai multe organizaţii, programatorilor foarte buni li se oferă funcţii manageriale. Este mult mai bine să li se ofere posibilităţi de a avansa în carieră în arii mai tehnice ale dezvoltării şi întreţinerii software-ului;

Page 36: Ingineria Programarii

36/135

• Pe termen lung, organizaţia are mai mult de câştigat dacă îi ajută pe angajaţi să dea ceea ce au mai bun: Altfel spus, nici una din situaţiile următoare nu trebuie să aibă loc:

• Principiul lui Peter inversat: angajaţii urcă în ierarhie până la nivelul la care devin indispensabili: De exemplu, un programator poate deveni singurul expert într-un anumit domeniu. El nu va avea şansa să lucreze în alt domeniu. S-ar putea ca persoana respectivă să părăsească firma pentru a lucra la altceva mai interesant;

• Principiul lui Paul: angajaţii urcă în ierarhie până la nivelul la care experienţa lor devine învechită în cinci ani: Având în vedere viteza cu care noutăţile intră pe piaţa dezvoltării software, angajaţii trebuie să aibă posibilitatea să fie la curent cu acestea;

• Este bine să fie selectaţi oameni care să formeze o echipă bine echilibrată şi armonioasă: Nu este suficient să existe în echipă doar câţiva experţi de vârf. Trebuie să existe şi persoane care să îndeplinească sarcinile mai simple, de rutină, pe care experţii s-ar putea simţi chiar insultaţi să le rezolve;

• Cine nu se potriveşte cu echipa trebuie îndepărtat: Dacă echipa nu funcţionează ca o unitate coerentă, de multe ori suntem înclinaţi să mai aşteptăm puţin, să vedem cum evoluează lucrurile şi să sperăm ca totul va merge bine. Acest lucru este dăunător pe termen lung.

3.4 Concluzii În acest curs au fost prezentate mai întâi noţiuni legate de managementul unui proiect

software, precum planificarea şi controlul proiectului. Apoi au fost tratate problemele legate de gestionarea creării şi modificării elementelor caracteristice unui proiect de dezvoltare software. Au fost explicate mecanismele care ţin de managementul unei echipe, privind în deosebi coordonarea şi stilurile de management, alături de modalităţi de organizare a echipei şi sfaturi practice pentru organizarea optimă a unei echipe de dezvoltare a unui proiect software.

Page 37: Ingineria Programarii

37/135

4 Estimarea costului unui proiect software

4.1 Introducere La construirea unei case, la decorarea unei băi sau a unei grădini, dorim să cunoaştem

costul precis al operaţiilor ce urmează a fi efectuate înainte de începerea acestora. Un grădinar este capabil să facă o aproximare a costurilor bazându-se, de exemplu, pe tipul pământului, mărimea dorită a terasei sau a zonei cu iarbă, prezenţa sau absenţa unui iaz şi pe alte informaţii similare. Apoi această estimare poate fi făcută mai precisă printr-un dialog ulterior înainte de a se începe lucrările. Cine doreşte o precizie similară în ceea ce priveşte estimarea costurilor pentru un proiect de dezvoltare software ar putea avea o surpriză.

Estimarea costurilor unei aplicaţii software reprezintă un domeniu foarte puţin dezvoltat, în care toţi se bazează doar pe aproximări. Din fericire există şi excepţii de la această situaţie. Acum există un număr de algoritmi care ne permit să estimăm costul total şi timpul de dezvoltare pentru un proiect software pe baza unui număr limitat de generatori relevanţi de costuri.

În cele mai multe modele de estimare, este presupusă o relaţie simplă între cost şi efort. Efortul poate fi măsurat de exemplu în luni-om (adică numărul estimativ de luni necesar unui singur om să realizeze lucrarea), şi fiecărei luni-om i se asociază o sumă fixă de bani, corespunzător salariului angajaţilor. Costul total estimat se obţine înmulţind numărul estimat de luni-om cu factorul constant considerat.

Noţiunea de cost total reprezintă de obicei costul efortului iniţial de dezvoltare software, costul analizei cerinţelor, proiectării, implementării şi testării, fără a fi luate în considerare costurile de întreţinere. Prin timp de dezvoltare se înţelege timpul dintre specificarea cerinţelor şi momentul în care produsul software va fi predat clientului. Noţiunea de cost, care se va folosi în continuare, nu include şi posibilele costuri hardware. Ea include numai costurile legate de personalul angajat în dezvoltarea produsului software.

Cercetările în domeniul estimării costului sunt departe de a fi cristalizate. Diferite modele folosesc diferite sisteme de măsură şi generatori de costuri, încât este foarte dificilă compararea lor. Să presupunem că un model foloseşte o ecuaţie de forma:

1.052.7E KLOC= × Acesta ecuaţie arată o relaţie între efortul necesar şi mărimea produsului (KLOC = Kilo-

Lines Of Code, kilo-linii de cod). Unitatea de măsură a efortului poate fi numărul de luni-om necesare. Apar aici mai multe întrebări. Ce este o linie de cod? Numărăm codul maşină sau codul sursă într-un limbaj de nivel înalt? Numărăm de asemenea şi comentariile, liniile libere care măresc vizibilitatea sau nu? Luăm în considerare şi zilele libere, vacanţele, concediile medicale în noţiunea de luni-om sau se referă numai la o măsurare netă? Diferite interpretări ale acestor întrebări pot conduce la rezultate complet diferite. Uneori nici nu este cunoscut ce definiţii au fost folosite în aplicarea modelului.

Pentru a determina ecuaţiile unui model algoritmic de estimare a costului, putem urma diferite abordări, în primul rând ne putem baza ecuaţia pe rezultatul experimentelor, într-un asemenea experiment, în general variem un parametru, în timp ce ceilalţi parametri rămân constanţi. In acest fel încercăm să determinăm influenţa pe care o are parametrul care variază. Un exemplu tipic este cel ce testează dacă comentariile ajută la înţelegerea unui program. Vom pune un număr de întrebări despre acelaşi program unor programatori împărţiţi în două grupuri. Primul grup va primi programul fără comentarii, iar al doilea grup va primi textul aceluiaşi program, dar comentat. Folosind rezultatele celor două grupuri, putem testa ipoteza realistă că o înţelegere mai bună şi mai rapidă a programului are efecte pozitive asupra întreţinerii programului.

Page 38: Ingineria Programarii

38/135

O altă modalitate de a descoperi modele algoritmice de estimare a costului se bazează pe o analiză a datelor unui proiect real, dar şi pe un suport teoretic. O organizaţie poate strânge date despre un număr de sisteme software care au fost dezvoltate. Aceste date pot privi timpul petrecut la diferite faze bine determinate, calificarea personalului implicat, momentele de timp în care au apărut erori, atât în timpul testării cât şi după instalare, complexitatea, robusteţea şi alţi factori importanţi ai proiectului, mărimea diferitelor entităţi implicate şi o analiză statistică a acestor date. Un exemplu pentru o asemenea relaţie este cea dată mai sus, ce exprimă o legătură între E şi KLOC. Aplicabilitatea şi corectitudinea acestor ecuaţii este, în mod evident, dependentă de corectitudinea datelor pe care se bazează. De asemenea, trebuie cunoscută ipoteza ce stă la baza ecuaţiei.

Rezultatele obţinute în acest fel reprezintă o medie, cea mai bună aproximare bazată pe datele disponibile, de aceea rezultatele obţinute trebuie aplicate cu atenţie. De exemplu, programul ce urmează a fi dezvoltat în cadrul unui nou proiect nu se poate compara cu produse anterioare datorită inovaţiilor implicate. Estimarea costurilor pentru un proiect legat de o navetă spaţială nu poate fi făcută printr-o simplă extrapolare a proiectelor anterioare.

Trebuie reţinut că aplicarea oarbă a unor formule din modele existente nu va rezolva problema estimării costului. Fiecare model necesită o adaptare la mediul în care va fi folosită. Aceasta conduce la necesitatea colectării continue a datelor din propriul proiect şi aplicarea unor metode statistice pentru a calibra parametrii modelului.

Neconcordanţele dintre diferitele modele mai pot apărea deoarece: • Majoritatea modelelor oferă o relaţie între numărul lunilor-om necesar şi mărime

(în linii de cod). După cum am remarcat mai devreme, există mai multe definiţii diferite pentru aceste noţiuni;

• Efortul nu înseamnă întotdeauna acelaşi lucru. Uneori se consideră activităţile pornind de la conceperea produsului, după ce au fost fixate cerinţele. Alteori se includ eforturile de întreţinere.

• Cu toate acestea, modelele de estimare a costului au multe caracteristici comune. Acestea reflectă importanţa factorilor care intervin asupra costului de dezvoltare şi de efortului. Creşterea înţelegerii costului programelor ne permite să identificăm strategii de creştere a productivităţii software, cele mai importante fiind:

• Scrierea de mai puţin cod: Mărimea sistemului este una din cauzele principale a efortului şi a costului. Prin metode care încearcă sa reducă mărimea, cum ar fi reutilizarea programului şi utilizarea de limbaje de nivel înalt, se pot obţine reduceri semnificative;

• Stimularea oamenilor să lucreze la capacitatea maximă: Capacităţile de lucru individual şi în echipă au un mare impact asupra productivităţii. Angajarea celor mai buni oameni este de obicei o afacere profitabilă. O mai bună stimulare, condiţii mai bune de lucru, cursurile de perfecţionare asigură oportunităţi de creştere a productivităţii; •Evitarea refacerii componentelor dezvoltate anterior: Studiile au arătat că pentru a rescrie ceea ce s-a produs deja este necesar un efort considerabil. Aplicând modele cum ar fi realizarea prototipurilor şi utilizarea metodelor moderne de programare precum ascunderea informaţiilor, se pot reduce semnificativ costurile;

• Dezvoltarea şi folosirea mediilor de dezvoltare integrate, cu instrumentele ce pot ajuta la eliminarea sau eficientizarea unor etape.

Au fost făcute numeroase studii în care scopul principal era estimarea efortului necesar pentru o sarcină limitată de programare. Primele experimente au fost realizate de Halstead (1977). La baza modelului său stă faptul că numărarea liniilor de cod poate constitui o problemă, chiar dacă avem o definiţie exactă a termenului „linie de cod". Unele linii sunt mult

Page 39: Ingineria Programarii

39/135

mai complicate decât altele. Conform teoriei lui Halstead, este mai bine să se pornească de la un număr de unităţi sintactice, după cum sunt recunoscute de compilator. Halstead face distincţia între operatori şi operanzi. Operatorii denotă o operaţie; exemple de operatori sunt operatorii standard (+, -, *, etc.), dar şi semnul de punctuaţie punct şi virgulă (;) care arată structura unei instrucţiuni şi construcţii ca if-then-else şi while-do. Operanzii înseamnă date: variabile şi constante. Calcularea numărului de operatori şi operanzi dintr-un program va oferi o mai bună măsură a mărimii decât simplă calculare a numărului de linii.

Cele patru entităţi de bază pentru un program, în modelul Halstead sunt: • 1n = numărul de operatori diferiţi; • 2n = numărul de operanzi diferiţi; • 1N = numărul total de apariţii ale operatorilor; • 2N = numărul total de apariţii ale operanzilor;

Pentru lungimea unui program, Halstead dă următoarea ecuaţie: 1 2N N N= +

Astfel se obţine o rafinare a măsurării numărului de linii simple de cod, LOC. Atât LOC cât şi N oferă o bună corelaţie cu efortul de programare. De aceea este important să se găsească modalităţi de estimare a entităţilor precum LOC şi N în primele etape. Valoarea lui N depinde de 1n şi 2n . Valoarea lui 1n este, de cele mai multe ori, un factor constant pentru multe programe scrise într-un anumit limbaj de programare de nivel înalt. Această constantă depinde de limbajul de programare ales. De exemplu, pentru un limbaj dat, numărul maxim de operatori care poate fi utilizat în orice program este fix: toţi sunt prezentaţi în sintaxa limbajului. Majoritatea programelor vor folosi un mare procent din acestea, cel puţin o dată. O ipoteză consideră că 2n este determinat în principal de numărul de variabile (VĂRS) care apar în program. Bazându-se pe aceste presupuneri, a fost enunţată următoarea relaţie empirică:

102 5.31LOC VARS= + Astfel, fiecare program va conţine aproximativ 100 de linii de cod, plus 5 linii

suplimentare pentru fiecare variabilă ce apare în program. Primele experimente arată că în acest fel se pot obţine aproximări destul de corecte ale mărimii şi efortului necesar. Valoarea estimată a lui VĂRS poate fi obţinută relativ devreme dacă este folosită o metodă de proiectare top-down în combinaţie cu un limbaj de nivel înalt.

Generalizarea acestor rezultate la programe mai mari nu este indicată, în programele mai complexe, factori precum interfaţa dintre componente şi comunicarea necesară între persoanele implicate joacă un rol ce nu poate fi neglijat. Cunoscând mărimea estimată a unui proiect, vom fi interesaţi în continuare să evaluăm timpul de dezvoltare necesar. într-o abordare naivă am putea considera că un proiect ce necesită 100 de luni-om poate fi realizat într-un an cu o echipă de 8,5 persoane sau într-o lună cu o echipă de 100 de persoane. Această abordare însă este prea simplistă. Un proiect de o anumită dimensiune corespunde unei anumite perioade de timp fizic. Dacă vom încerca să micşorăm acest timp nominal de dezvoltare prea mult, vom intra într-o „zonă imposibilă", iar şansele de eşec vor creşte.

4.2 Cum nu trebuie estimat costul Costurile estimate au deseori o culoare politică, iar rezultatele sunt determinate de

argumente care nu au o natură tehnică. Motive tipice care reflectă aceste argumente non-tehnice sunt prezentate în exemplele următoare:

• Dacă avem 12 luni pentru a finaliza o lucrare, ea va necesita 12 luni. Acest motiv poate fi privit ca o variantă a legii Parkinson: munca ocupă tot timpul disponibil;

Page 40: Ingineria Programarii

40/135

• Dacă ştim că concurenţa a făcut o ofertă de 100.000 de euro, noi vom face o ofertă de 90.000 de euro. Acesta este cunoscut sub denumirea de preţ de câştig;

• Dorim să ne promovăm produsul la un anumit târg de tehnică de calcul şi din acest motiv programul trebuie scris şi testat în următoarele 9 luni, deşi realizăm că timpul este limitat. Această situaţie este cunoscută sub denumirea de metoda bugetului de estimare a costului.

• Proiectul poate fi dezvoltat într-un an, dar şeful nu ar accepta acest termen. Ştim că termenul de 10 luni este acceptabil şi atunci îl programăm pentru 10 luni.

Aceste estimări pot avea efecte dezastruoase, după cum s-a demonstrat frecvent în scurta istorie a acestui domeniu. Argumentele politice intervin întotdeauna dacă estimările sunt realizate de cei implicaţi direct în proiect, cum ar fi managerul proiectului sau o persoană ce lucrează direct cu acesta, în curând estimările vor influenţa sau vor fi influenţate de către dorinţele acestor persoane.

Pe de altă parte, simpla comparare a caracteristicilor unui proiect cu un proiect precedent nu garantează o estimare corectă a costului său. Dacă o echipă lucrează în mod repetat la proiecte asemănătoare, timpul de lucru necesar va scădea, datorită acumulării experienţei, în 1968, unei echipe de programatori i s-a cerut să dezvolte un compilator FORTRAN pentru trei maşini diferite. Efortul necesar pentru aceste trei proiecte este descris în tabelul de mai jos:

Compilatorul Efortul (în luni-om) Compilatorul Efortul (în luni-om)

1 72 2 36 3 14

Pentru estimarea costului se poate apela la serviciul unui expert, care apelează la propria sa experienţă. Factori greu de cuantificat, precum caracteristicile de personalitate sau caracteristici neobişnuite ale proiectului, pot fi astfel luaţi în considerare, în acest caz, calitatea estimării nu poate depăşi calitatea expertului.

Pentru o estimare mai precisă se pot solicita mai mulţi experţi. Totuşi, dacă un grup de persoane trebuie să găsească împreună o soluţie, vom observa că unii membri ai grupului au un impact mai mare asupra rezultatului decât ceilalţi. Unii nu îşi vor exprima opinia sau vor fi impresionaţi de opiniile celorlalţi. Aceasta poate avea un impact negativ asupra rezultatului final. Pentru a anticipa acest efect negativ, putem folosi metoda Delphi, în metoda Delphi, fiecare expert îşi expune opinia în scris. Un moderator colectează estimările obţinute astfel şi le redistribuie celorlalţi experţi, în acest proces nu sunt asociate numele experţilor cu estimările lor. Fiecare expert va preda o nouă estimare bazată pe informaţiile primite de la moderator. Acest proces continuă până când se ajunge la un consens.

O altă metodă care doreşte obţinerea unei estimări mai bune este aceea de a avea un expert care să realizeze mai multe estimări: o estimare optimistă a, o estimare realistă m şi o estimare pesimistă b. Folosind o distribuţie beta, efortul aşteptat va fi:

46

a m bE + +=

Această estimare va fi probabil mai bună decât dacă s-ar fi considerat numai media aritmetică a lui a şi b.

4.3 Modele algoritmice clasice Mesajul din paragraful precedent este clar: pentru a obţine estimări acceptabile, trebuie

înregistrate datele proiectelor anterioare. Procedând astfel, vom prezice costul pe baza

Page 41: Ingineria Programarii

41/135

proprietăţilor măsurabile ale proiectului curent. In acest paragraf, vom prezenta primele demersuri pentru a obţine modele algoritmice de estimare a costului programelor.

Nelson (1966) oferă un model liniar pentru estimarea efortului necesar pentru un proiect de dezvoltare software. Modelele liniare au următoarea formă:

01

n

i ii

E a a x=

= +∑ n

Coeficienţii ia sunt constante, iar ix reprezintă factorii care au impact asupra efortului necesar. Un număr mare de factori poate influenţa productivitatea şi implicit efortul necesar. Analizând cu atenţie datele proiectelor precedente şi diferite combinaţii de factori, putem încerca să obţinem un model cu un număr mic de factori. Nelson, de exemplu, sugerează un model care ia în considerare 14 factori:

1 2 3 4 5 5 7

8 9 10 11 12 13 14

33.63 9.15 10,73 0.51 0.46 0.40 7.28 21.4513.5 12.35 58.82 30.61 29.55 0.54 25.20

E x x x x x x xx x x x x x x

= − + + + + + + −+ + + + + + −

În această ecuaţie E reprezintă numărul estimat de luni-om necesar. Semnificaţia factorilor ix şi domeniul lor de definiţie sunt prezentate în tabelul următor:

Factor Descriere Valori posibile

1x Instabilitatea specificaţiilor cerinţelor 0-2

2x Instabilitatea proiectării 0-3 3x Procentajul de instrucţiuni matematice 0-100

4x Procentajul de instrucţiuni I/O 0-100

5x Numărul subprogramelor număr

6x Utilizarea unui limbaj de nivel înalt 0 (da) / 1 (nu)

7x Aplicaţie comercială 0 (da) / 1 (nu) 8x Program de sine stătător 0 (da) / 1 (nu)

9x Primul program pe această maşină 1 (da) / 0 (nu) 10x Dezvoltare concurentă de hardware 1 (da) / 0 (nu) 11x Utilizarea dispozitivelor random-access 1 (da) / 0 (nu)

12x Maşina gazdă diferită de maşina ţintă 1 (da) / 0 (nu) 13x Număr de erori număr 14x Dezvoltare pentru o organizaţie militară 0 (da) / 1 (nu)

Putem face mai multe observaţii asupra acestui model, în dezvoltarea programelor pentru aplicaţiile de apărare, în care programele vor fi încorporate în maşini diferite de maşina gazdă (un exemplu ar putea fi programul de control al zborului pentru rachete), factori precum

12x şi 14x vor avea cu siguranţă un impact mare asupra costului. Aceasta nu se va mai verifica însă într-un caz complet diferit. Observăm din nou că baza de date cu datele proiectelor care stau la baza modelului au un impact semnificativ asupra factorilor care influenţează costul. Puţin probabilă în acest model este penalizarea din cauza folosirii limbajului de asamblare în locul unui limbaj de nivel înalt ( 6x ): aproximativ 7 luni-om, indiferent de mărimea proiectului, în mod similar, constanta negativă a0 şi ceilalţi doi factori care au coeficienţi negativi au o probabilitate mică de apariţie.

în general modelele liniare nu funcţionează foarte bine. Deşi există un număr mare de factori care influenţează productivitatea, este puţin probabil ca ei să intervină independent şi liniar.

Page 42: Ingineria Programarii

42/135

Trebuie atrasă atenţia asupra preciziei acestui tip de formulă, în formula lui Nelson, constantele sunt date cu precizia a 2 zecimale. Aplicând această formulă va rezulta o estimare a efortului de genul 97,32 de luni-om. Va trebui totuşi să avem grijă la capcana exprimată în sloganul: există trei tipuri de minciuni - minciuni mici, minciuni mari şi statistici. Formula lui Nelson este rezultatul analizei statistice a datelor unui proiect real şi trebuie interpretată ca atare, adică în termeni probabilistici. Dacă avem o estimare E, atunci efortul real R va verifica formula:

( ) ( )( )1 1P E R Eα α β− ≤ ≤ + ≥ , unde valori acceptabile pentru α şi β sunt: α = 0,2 şi β = 0,9. Să presupunem că estimarea este de 100 luni-om. Semnificaţia formulei este

următoarea: probabilitatea ca proiectul să necesite în realitate între 80 şi 120 de luni-om este mai mare ca 90%.

Costurile estimate prin acest tip de model rezultă într-un anumit interval, rămânând o probabilitate diferită de zero ca acesta să fie în afara intervalului. Aplicabilitatea acestor estimări este puternic influenţată de mărimea intervalului şi de probabilitatea ca valoarea reală a costului sa fie într-adevăr în acel interval, în special pentru proiectele ce necesită efort mai mare, este bine să se considere valoarea superioară a intervalului în care se află costul în locul valorii estimate.

O altă modalitate prin care un expert poate ajunge la o estimare a costului este printr-un proces bottom-up. Pentru fiecare modul, va fi obţinut un cost estimativ iar costul final va fi suma costurilor modulelor, cu o corecţie aplicată datorită integrării modulelor.

Wolverton descrie un model în care o matrice a costurilor este folosită ca punct de plecare în determinarea costurilor modulelor, în această matrice există un număr limitat de tipuri diferite de module şi un număr de nivele de complexitate. Tabelul următor ilustrează o matrice ipotetică de costuri. Elementele matricei reflectă costul pentru fiecare linie de cod.

Complexitate Mică <-> Mare

Tipul modulului

1 2 3 4 5 1 . Management de date 11 13 15 18 22

2. Management de memorie 25 26 27 29 32 3. Algoritm 6 8 14 27 51

4. Interfaţă utilizator 13 16 19 23 29 5. Control 20 25 30 35 40

Fiind dată o matrice de costuri C, un modul de tip I, complexitate j şi mărime kS , va rezulta un cost al modulului k k ijM S C= .

Acest tip de model are de asemenea probleme, în afară de dificultatea estimării costului de integrare a modulelor, utilizatorul trebuie să estimeze subiectiv clasa de complexitate din care face parte fiecare modul, ceea ce determină un grad mare de nesiguranţă. Alţi factori care vor avea impact asupra productivităţii, cum ar fi experienţa în programare şi caracteristicile hardware, nu sunt luaţi în considerare. Extinderea matricei pentru a include şi aceşti factori ar creşte gradul de subiectivitate al metodei.

4.4 Modele algoritmice moderne În paragrafele anterioare am remarcat faptul că efortul de programare este corelat cu

mărimea programului. Există diferite modele (neliniare) care exprimă această legătură. O formă generală este:

( ) ( )1,...,c

nE a b KLOC f x x= + × × ,

Page 43: Ingineria Programarii

43/135

unde KLOC reprezintă mărimea programului în kilo-linii de cod, iar E reprezintă efortul în luni-om. a, b şic sunt constante iar ( )1,..., nf x x este o funcţie care depinde de valorile factorilor 1,..., nx x . În general, formula de bază este:

cE a b KLOC= + × . Ea este obţinută printr-o analiză de regresiune a datelor proiectelor disponibile. Totuşi

primul generator de cost este mărimea programului, măsurată în linii de cod. Acest cost nominal estimat este apoi adaptat prin corectarea sa pentru un număr de factori care influenţează productivitatea (aşa numiţii generatori de cost). De exemplu, dacă unul din factorii folosiţi reprezintă „experienţa echipei de programatori” aceasta poate cauza o corecţie a costului nominal estimat cu 1.50, 1.20, 1.00, 0.80, 0.60 pentru un nivel de expertiză foarte scăzut, scăzut, mediu, înalt şi respectiv foarte înalt.

Tabelul următor conţine unele formule de bază foarte cunoscute pentru relaţia dintre mărimea programului şi efort. Din motivele enunţate anterior este dificil să comparăm aceste modele. Este interesant de observat că valoarea lui c variază în jurul valorii de l în toate modelele.

Autor Formula Halstead 1.500.7E KLOC= ×Boehm 1.052.4E KLOC= ×

Walston-Felix 0.915.2E KLOC= ×Când c < l, se remarcă apariţia unui fenomen foarte bine cunoscut din teoria economică.

Pentru o producţie de masă, se presupune că este mai ieftin să se producă mari cantităţi din acelaşi produs. Costurile fixe vor fi împărţite astfel unui număr mai mare de unităţi, ceea ce conduce la scăderea costului pe unitate. In cazul programelor, liniile de cod sunt produsul. Dacă presupunem că producând multe linii de cod va scade costul pe linie de cod. Motivul este costul instrumentelor scumpe precum generatoare de program, medii de dezvoltare şi instrumente de testare, care poate fi distribuit unui număr mai mare de linii de cod.

In cazul opus (c > 1), observăm că după un anumit punct, producerea de unităţi suplimentare implică costuri suplimentare. Putem afirma că programele foarte mari vor fi mult mai scumpe. Suma necesară va fi mai mare deoarece creşte necesitatea de comunicare şi de control managerial, deoarece problemele şi interfeţele devin mai complexe. Deci fiecare linie de cod suplimentară necesită mai mult efort.

Nu există nici un argument convingător pentru nici un tip de relaţie, deşi ultima (c > 1) pare mai plauzibilă, în mod sigur, pentru proiecte foarte mari, efortul creşte mai mult decât liniar cu mărimea. Este evident că valoarea exponentului c influenţează foarte mult valoarea calculată E, în special pentru valori mari ale KLOC. Tabelul următor prezintă valorile lui E, calculate prin metodele prezentate anterior şi pentru câteva valori ale KLOC. Se remarcă marea diferenţă dintre modele. Pentru programe mici, prin metoda Halstead rezultă costuri estimate mici. Pentru proiecte cu aproximativ un milion de linii de cod, acelaşi model generează estimări ale costului cu un ordin de mărime mai mari decât prin aplicarea metodei Walston-Felix.

KLOC Halstead Boehm Walston-Felix 1 0.7 2.4 5.2

10 22.1 26.9 42.3 50 247.5 145.9 182.8

100 700 302.1 343.6 1000 22135.9 3390.1 2792.6

Aceste observaţii nu trebuie să ne conducă la concluzia că aceste metode sunt complet inutile. Este mai probabil că există diferenţe mari între caracteristicile seturilor de proiecte pe

Page 44: Ingineria Programarii

44/135

care se bazează diferite modele. Ştim că numerele utilizate în modele provin din urma analizei datelor proiectelor reale. Dacă aceste date reflectă diferite proiecte şi/sau medii de dezvoltare, modelele se vor comporta la fel. Nu putem copia pur şi simplu aceste formule. Fiecare mediu are caracteristicile sale proprii şi este deci necesară adaptarea parametrilor la mediul specific (proces numit calibrare).

Cea mai importantă problemă a acestui model este obţinerea unei estimări sigure a mărimii programului de la început. Cum am putea să estimăm numărul de pagini ale unui roman care nu a fost scris încă? Chiar dacă ştim numărul de personaje, de locaţii şi intervalul în care se va desfăşura povestea, este o iluzie aşteptarea de la început unei estimări realiste a mărimii. Cu cât înaintăm în realizarea proiectului, cu atât va fi mai exactă estimarea mărimii. Dacă proiectarea se apropie de final, ne putem forma o impresie rezonabilă asupra mărimii programului rezultat. Dar numai când sistemul va fi predat vom cunoaşte numărul exact.

Clientul însă solicită o estimare a preţului de la început. In acest caz, numărul liniilor de cod reprezintă o măsură prea inexactă pentru a reprezenta o bază pentru estimarea costului. De aceea trebuie căutată o alternativă, în paragrafele următoare, vom analiza câteva modele ce se bazează pe mărimi cunoscute într-o etapă iniţială.

4.4.1 Modelul Walston-Felix Ecuaţia ce stă la baza modelului Walston-Felix este: 0.915.2E KLOC= × . Acest model a

fost creat prin analiza a 60 de proiecte de la IBM. Aceste proiecte erau complet diferite ca mărime, iar programele au fost scrise în mai multe limbaje de programare. Totuşi nu reprezintă o surpriză faptul că dacă aplicăm acest model chiar unei submulţimi a celor 60 de proiecte, nu vom avea rezultate satisfăcătoare.

Încercând să explice aceste rezultate dintr-o plajă mare de valori, Walston şi Felix au identificat 29 de variabile care influenţează în mod sigur productivitatea. Pentru fiecare din aceste variabile au fost considerate trei nivele: mare, mediu şi mic. Pentru un număr de 51 de proiecte, Walston şi Felix au determinat nivelul fiecărei variabile din cele 29, împreună cu productivitatea obţinută (exprimată ca numărul liniilor de cod pe lună-om). Aceste rezultate sunt prezentate în tabelul următor pentru câteva din cele mai importante variabile. De exemplu, productivitatea medie este de 500 de linii de cod pe lună-om pentru proiecte cu o interfaţă utilizator de complexitate scăzută. Pentru o interfaţă de complexitate înaltă sau medie, productivitatea este de 295 şi respectiv 124 de linii de cod pe lună. Ultima coloană reprezintă variaţia productivităţii, PC (engl. „productivity change"), valoarea absolută a diferenţei dintre valorile minime şi maxime.

Variabila Productivitatea medie pentru valoarea variabilei PC

Complexitatea interfeţei utilizator

< normală 500

normală 295

> normală 124 376

Calificarea şi experienţa personalului

mică 132

medie 257

mare 410 278

Experienţă anterioară cu aplicaţii similare

minimă 146

medie 221

vastă 410 264

Procentajul de programatori participanţi în faza de proiectare

< 25% 153

25-50% 242

> 50% 391 238

Raportul dintre mărimea medie a echipei şi durata proiectului

(persoane/lună)

<0.5 305

0.5-0.9 310

>0.9 171 134

Walston şi Felix consideră că indexul productivităţii I poare fi determinat pentru noile proiecte după următoarea relaţie:

Page 45: Ingineria Programarii

45/135

29

1i i

iI W X

=

= ∑

unde ponderile iW sunt definite astfel:

( )100.5 logi iW PC= × . În relaţia de mai sus, iPC reprezintă variaţia productivităţii factorului I . Pentru primul

factor din tabelul de mai sus, complexitatea interfeţei cu utilizatorul, rezultă următoarele: 1PC = 376, deci 1W = 1.29. Variabilele iX pot lua valorile 1− , 0 sau l, unde factorul corespunzător este de nivel scăzut, mediu sau înalt. Indexul productivităţii obţinut poate fi tradus într-o productivitate aşteptată (linii de cod scrise pe lună-om).

Trebuie menţionat că numărul factorilor luaţi în considerare în acest model este destul de ridicat (29 de factori din 51 de proiecte). De asemenea, nu este clar cum aceşti factori se influenţează unul pe celălalt. Un alt dezavantaj ar fi că numărul de alternative pentru fiecare factor este de numai 3, şi nu oferă destule opţiuni pentru situaţiile practice.

4.4.2 Modelul COCOMO COCOMO (COnstuctive COst MOdel) este unul din cei mai bine documentaţi

algoritmi de estimare a costului (Boehm, 1981). în forma sa cea mai simplă, numită „Basic COCOMO", formula care exprimă legătura dintre efort şi mărimea programului este:

cE b KLOC= × , unde b şi c sunt constante ce depind de tipul proiectului ce este executat. Boehm

distinge trei clase de proiecte: • Organice: în proiectele de tip organic o echipă relativ mică dezvoltă programul

într-un mediu cunoscut. Persoanele implicate au în general experienţă în proiecte similare realizate în organizaţia lor. Astfel, ei pot să lucreze de la început, nefiind necesare investiţii iniţiale. Proiectele de acest tip sunt de multe ori programe relativ mici;

• Integrate: Proiectele din acest tip implică sisteme unde mediul impune constrângeri severe. Produsul va fi integrat într-un mediu care este foarte strict. Exemplu de asemenea proiecte sunt programe de control al traficului aerian sau aplicaţiile militare;

• Semidetaşate: Aceasta este o formă intermediară. Echipa poate fi formată din persoane experimentate şi neexperimentate, proiectul poate fi destul de mare, dar nu foarte mare.

Pentru clase diferite, parametrii metodei Basic COCOMO iau următoarele valori: Clasa de proiect b c

organică 2.4 1.05 semidetaşată 3.0 1.12

integrată 3.6 1.20 Tabelul următor prezintă estimări ale efortului pentru fiecare din cele trei moduri,

pentru diferite valori ale KLOC (deşi un proiect organic de un milion de linii de cod nu este realist). Se observă influenţa foarte mare a constantei c asupra estimărilor obţinute. Estimările efortului sunt exprimate tot în luni-om.

KLOC organic semidetaşat integrat 1 2.4 3.0 3.6

10 26.9 39.6 57.1 50 145.9 239.4 392.9

100 302.1 521.3 904.2

Page 46: Ingineria Programarii

46/135

1000 3390 6872 14333

4.4.3 Modelul Putnam-Norden Norden a studiat distribuţia forţei de muncă în timp într-un număr de proiecte de

dezvoltare software în anii '60. A descoperit că această distribuţie avea deseori o formă caracteristică. Această formă caracteristică este bine aproximată de distribuţia Rayleigh. Bazându-se pe această descoperire, Putnam a dezvoltat un model de estimare a costului în care forţa de muncă necesară la un moment de timp t este dată de:

( ) 2

2 atFM t K a t e−= × × × × unde a este un factor de accelerare care determină panta iniţială a curbei, iar K

reprezintă forţa de muncă totală necesară, incluzând faza de întreţinere. K este egal cu volumul zonei delimitate de curba Rayleigh, reprezentată în figura l.

Integrarea ecuaţiei pentru ( )FM t determină efortul cumulat I:

( ) ( )2

1 atI t K e−= −

1.414K a

0.858K a

0.386K a

12a

32a

t

2

Pant

a ini

tiala

Ka

Figura 1. Curba Rayleigh Dacă considerăm momentul de timp T în care curba Rayleigh ajunge în punctul de

maxim, atunci 2

1aT

= . Acest moment T va fi apropiat de momentul de timp în care proiectul

va fi predat clientului. Aria delimitată de curba Rayleigh între punctele 0 şi T va fi o bună aproximare a efortului iniţial de dezvoltare. Pentru acesta obţinem:

( ) 0.3945E I T K= = Acest rezultat este foarte apropiat de o regulă euristică foarte des utilizată: 40% din

efortul total este cheltuit pentru dezvoltarea efectivă, în timp ce 60% este cheltuit pentru întreţinere. Specificarea cerinţelor nu este inclusă în model. Estimările conform acestui model nu se pot aplica decât începând cu proiectarea şi implementarea.

4.5 Distribuirea forţei de muncă în timp Mai mulţi cercetători au criticat folosirea curbei Rayleigh pentru estimarea costului

(Pillai, 1997). Modelul lui Norden nu se bazează pe o teorie, ci mai mult pe observaţii. Mai mult, datele sale se referă mai mult la proiecte hardware. Nu s-a demonstrat însă faptul că

Page 47: Ingineria Programarii

47/135

proiectele software se comportă la fel. Acestea prezintă uneori o acumulare rapidă a forţei de muncă care invalidează modelul în fazele incipiente ale proiectului.

Putnam a folosit observaţii empirice legate de nivelele de productivitate pentru a deriva ecuaţia software-ului din curba Rayleigh:

1 43 3D kE t=

unde D este dimensiunea proiectului, E este efortul total în ani-om, t este timpul scurs până la lansare în ani iar k este un factor tehnologic bazat pe 14 componente, precum:

• maturitatea generală a proiectului şi tehnicile de management; • gradul de utilizare a tehnicilor de ingineria programării; • nivelul limbajelor de programare folosite; • capacitatea şi experienţa echipei de dezvoltare; • complexitatea aplicaţiei.

Puterea supraunitară a timpului are implicaţii puternice asupra alocării resurselor în proiecte mari. Prelungiri relativ mici ale datei de livrare pot determina reducerea substanţială a efortului (Pressman, 1997).

Pentru estimarea efortului, Putnam a introdus ecuaţia acumulării forţei de muncă:

3EA t=

unde A este numită accelerarea forţei de muncă iar E şi t au semnificaţiile de mai sus. Accelerarea forţei de muncă este 12,3 pentru proiecte software noi, cu multe interfeţe şi

interacţiuni cu alte sisteme, 15 pentru sisteme de sine stătătoare şi 27 pentru reimplementări ale sistemelor existente.

Pe baza celor două ecuaţii, putem elimina timpul şi determina efortul:

( )9 47 7DE Ak= .

Acest rezultat este interesant deoarece arată că efortul este proporţional cu dimensiunea la puterea 9 / 7 1.286≈ , valoare similară cu factorul lui Boehm, între 1,05 şi 1,20.

Evident, scurtarea timpului de dezvoltare implică un număr mai mare de persoane necesare pentru proiect. Referindu-ne la modelul curbei Rayleigh, scurtarea timpului de dezvoltare conduce la mărirea valorii a, factorul de accelerare care determină panta iniţială a curbei. Vârful curbei Rayleigh se deplasează spre stânga şi în acelaşi timp în sus. Astfel obţinem o creştere a puterii necesare la începutul proiectului şi o forţă de muncă maximă mai mare.

Există şi dezavantaje ale acestei deplasări. Mai multe studii au arătat că productivitatea individuală scade odată cu creşterea echipei. Conform lui Brooks, există două cauze ale acestui fenomen:

• Dacă o echipă se măreşte, va creşte timpul acordat comunicării cu ceilalţi membri ai echipei (pentru consultare, sincronizarea sarcinilor etc.);

• Dacă este adăugată forţă de muncă suplimentară unei echipe în timpul dezvoltării unui proiect, mai întâi scade productivitatea. Noii membri ai echipei nu sunt productivi de la început, în acelaşi timp ei necesită ajutor, deci timp, de la ceilalţi membri ai echipei în timpul procesului de învăţare. Luate împreună, acestea conduc la scăderea productivităţii totale.

Combinând aceste două informaţii, ajungem la fenomenul cunoscut sub denumirea de legea lui Brooks: adăugarea de personal la un proiect întârziat îl va întârzia şi mai mult.

Analizând o mare bază de date ale proiectelor, Conte a descoperit următoarea relaţie între productivitatea medie L (măsurată în linii de cod pe lună-om) şi mărimea medie a unei echipe P:

0.5777L P−= .

Page 48: Ingineria Programarii

48/135

Formula atestă faptul că productivitatea individuală scade exponenţial cu mărimea echipei. Numărul de legături de comunicare între persoanele implicate într-un proiect este determinat de mărimea şi structura echipei. Dacă într-o echipă de mărime P fiecare membru trebuie să-şi coordoneze activităţile sale cu toţi ceilalţi din echipă, numărul legăturilor de

comunicaţie va fi: ( )12

P P −. Dacă fiecare membru trebuie să comunice numai cu un singur

alt membru, acest număr va fi: P-1. Mai puţină comunicare decât aceasta nu este rezonabilă, deoarece ne-am confrunta cu echipe independente. Numărul de legături de comunicaţie variază de la aproximativ P la aproximativ

2

2P . Într-o organizare ierarhică, aceasta conduce

la Pα căi de comunicaţie, cu 1 2α< < . Pentru un membru al echipei, numărul de legături de comunicaţie variază de la l la P -1.

Dacă productivitatea individuală maximă este L şi fiecare legătură de comunicaţie conduce la o pierdere a productivităţii /, atunci productivitatea medie va fi:

( )1L L l P γγ = − − ,

unde ( ]0,1γ ∈ este o măsură a numărului de legături de comunicaţie. Presupunem că există cel puţin o persoană care să comunice cu mai mult de o persoană, deci 0γ > . Pentru o echipă de mărime P, aceasta conduce la o productivitate totală:

( )( )1totL PL P L l P γγ= = − − .

Pentru o mulţime dată de valori pentru L, l şi γ , pentru valori crescătoare ale lui P, această funcţie creşte de la 0 la o valoare maximă şi apoi scade din nou. Deci există o mărime optimă a echipei optP , care conduce la o productivitate maximă a echipei. Productivitatea echipei pentru diferite valori ale lui P este data în tabelul de mai jos. Aici, presupunem că productivitatea individuală este de ( )500 / 500LOC lună om L+ = , iar scăderea de productivitate este de 10% pe canal de comunicaţie (l = 50). Cu interacţiune completă între membrii echipei (γ = 1), rezultă o echipă optimă de 5.5 persoane:

Mărimea echipei Productivitatea individuală Productivitatea totală 1 500 500 2 450 900 3 400 1200 4 350 1400 5 300 1500

5.5 275 1512 6 250 1500 7 200 1400 8 150 1200

4.6 Concluzii În acest curs au fost prezentate diferite modele pentru estimarea efortului necesar pentru

dezvoltarea unui proiect software, a forţei de muncă necesare şi a timpului efectiv de dezvoltare. S-au descris şi factorii care nu trebuie să contribuie la estimarea costului proiectului. In final, s-a prezentat o modalitate de estimare a distribuţiei forţei de muncă în timp pentru găsirea optimului numărului de persoane implicate în proiect.

Page 49: Ingineria Programarii

49/135

5 Reutilizarea resurselor software “Între timp, Dedal, sătul de Creta şi de lunga sa absenţă de acasă, simţea un dor nebun

de ţara sa, dar era blocat de mare. Apoi el spuse: „Regele îmi poate închide calea pe uscat şi pe mare, dar, desigur, cerul este liber şi noi pe acolo vom merge. Poate că Minos deţine restul, dar aerul nu îi aparţine ". Spunând aceste cuvinte, Dedal şi-a îndreptat atenţia spre ştiinţe încă neexplorate şi a sfidat legile naturii. A aşezat un rând de pene, începând cu cele mai mici, apoi crescându-le dimensiunea, astfel încât marginea părea că se înclină în sus. In acelaşi fel naiul cu care cântă ciobanii este construit din trestie, fiecare puţin mai lungă decât cealaltă. Apoi, a fixat penele la mijloc cu aţă, iar la bază cu ceară; după ce le-a aranjat în acest fel, le-a îndoit puţin, dându-le o formă uşor curbă, aşa cum arată aripile păsărilor.”

Publius Ovidius Naso: Metamorfoze, VIII, 183-194

5.1 Introducere Dedal merită un loc în mitologia ingineriei programării. Pe vremea regelui Minos,

software-ul nu exista şi totuşi existau problemele şi noţiunile pe care le regăsim în ingineria programării de astăzi. Un exemplu îl constituie construirea sistemelor complexe. Dedal deţine cu siguranţă un record în acest domeniu. A reuşit să administreze un proiect care poate fi comparat cu proiectele de dezvoltare software din ziua de astăzi: construirea labirintului din Knossos.

După o vreme, Dedal a dorit să părăsească insula Creta, aşa cum Ovidius povesteşte mai sus. Totuşi regele Minos nu a vrut să îi dea drumul. Ştim cum se termină povestea: Dedal zboară din Creta împreună cu fiul său, Icar. În ciuda avertismentelor tatălui său, Icar zboară din ce în ce mai sus, se aproprie prea mult de soare şi ceara din aripi se topeşte. Icar cade în mare şi se îneacă iar Dedal reuşeşte în schimb să ajungă cu bine în Sicilia.

Construcţia lui Dedal este interesantă din punctul de vedere al reutilizării resurselor. Faptul că acest lucru interesează mai mult partea de hardware decât pe cea de software nu prezintă importanţă aici. Ceea ce interesează în discuţia de faţă este aplicarea anumitor principii în construcţie:

• reutilizarea resurselor: Dedal a folosit pene adevărate; • reutilizarea proiectării: el a imitat aripi reale; • interfaţarea componentelor: în acea vreme, oamenii foloseau ceara pentru a lipi

diverse lucruri între ele; calitatea lipiciului are un impact enorm asupra trăiniciei produsului final. în urma aplicării corecte şi ferme a acestor principii, a fost realizat cu succes un proiect ambiţios (zborul lui Dedal până în Italia), încercarea de a cuceri cerul cu o tehnologie insuficientă s-a terminat cu un dezastru (prăbuşirea lui Icar în mare).

Să facem acum un salt în istorie, la sfârşitul anilor '70. Criza software era deja acută de mulţi ani. Cererea de noi aplicaţii depăşea cu mult posibilităţile forţei de muncă din domeniu. Diferenţa dintre cerere şi ofertă era în continuare în creştere. Reutilizarea soft-ului este una din căile explorate pentru a obţine o creştere semnificativă în productivitatea software.

De ce să se scrie cod pentru nişte calcule deja cunoscute? Reutilizarea componentelor software de calitate nu duce oare la creşterea drastică a siguranţei şi productivităţii? Totuşi, nu este chiar atât de simplu. Utilizarea componentelor software existente necesită standardizarea denumirilor şi interfeţelor. Ideea de a lipi componente între ele nu este direct transferabilă în domeniul programării.

Punctul de vedere modern nu restricţionează noţiunea de reutilizare a soft-ului la reutilizarea componentelor. Şi informaţiile care privesc proiectarea pot fi reutilizate, aşa cum pot fi reutilizate şi alte cunoştinţe culese în timpul construcţiei unui proiect software.

Page 50: Ingineria Programarii

50/135

Foarte apropiată de reutilizarea software este şi flexibilitatea programelor, care trebuie să fie în continuă adaptare la modificările circumstanţelor, în implementarea următoarei versiuni a unui sistem, am prefera, evident, să folosim cât mai mult din versiunea curentă. Aceasta este de asemenea considerată a fi o formă de reutilizare a soft-ului.

5.2 Reutilizarea produselor intermediare Bibliotecile cu bucăţi de cod gata de utilizat, aşa cum sunt cele numerice sau calculele

statistice, sunt folosite de ceva timp şi pe o arie destul de largă. Această formă de reutilizare nu este în mod necesar potrivită altor domenii. În alte domenii ne putem descurca mai bine refolosind schelete de componente, componente ale căror detalii nu au fost încă definite, într-un mediu în care acelaşi tip de software este implementat mereu, aceste schelete pot fi modelate într-o proiectare reutilizabilă. O tehnică similară este aceea de a reutiliza arhitectura unui sistem software, aşa cum este întâlnită la construcţia compilatoarelor, de exemplu. Prin integrarea cunoştinţelor despre domeniu în accesorii software, ajungem în domeniul sistemelor de transformări, al generatoarelor de aplicaţii şi al limbajelor de generaţia a patra.

O clasificare generală a tehnologiilor de reutilizare presupune: • tehnologii bazate pe compunere; • tehnologii bazate pe generare.

Într-o tehnologie bazată pe compunere, reutilizarea se face parţial prin compunerea unui nou sistem din componentele existente. Elementele cu care se construieşte (engl. „building blocks") sunt fragmente pasive ce sunt copiate dintr-o bază de componente existentă. Într-o tehnologie bazată pe generare, este mult mai dificilă identificarea componentelor reutilizate. Mai precis, cunoştinţele reutilizate se regăsesc într-un program care generează un alt program, într-o tehnologie bazată pe generare, tiparele reutilizabile reprezintă un element activ folosit pentru a genera sistemul ţintă. Exemple mai vechi ale acestor două tehnologii sunt bibliotecile de subrutine şi, respectiv, generatoarele de aplicaţii. Cele mai multe sisteme reutilizate deţin aspecte din ambele perspective.

5.2.1 Biblioteci şi componente software Dacă avem de realizat un program care trebuie să facă apel la funcţii trigonometrice, în

mod normal nu ne gândim să ne construim propriile rutine de calcul al acestor funcţii, în general, acestea sunt deja implementate în majoritatea limbajelor de programare. Ne-am pune problema reimplementării lor doar în cazuri particulare, când performanţele programului depind în mod direct de precizia sau viteza calculelor.

Punând întrebarea de ce este aşa de simplă reutilizarea funcţiilor matematice, ajungem să găsim un număr mare de piedici ce sunt puse în faţa reutilizării componentelor software în alte domenii. Pentru reutilizarea cu uşurinţă a unor funcţii, trebuie să avem:

• un domeniu bine cunoscut, cu o terminologie standardizată; „cosinus" (sau „cos") reprezintă acelaşi lucru pentru toată lumea;

• o interfaţă bine precizată: avem nevoie de exact un număr pentru a calcula funcţia cosinus;

• un format de date standardizat: un număr poate fi reprezentat în virgulă fixă, în virgulă mobilă, în dublă precizie, şi cam atât.

Reutilizarea rutinelor funcţionează cel mai bine într-un domeniu de aplicaţii bine documentat, cu noţiuni clare şi pentru care datele de intrare sunt într-un format standardizat. Istoria modernă a reutilizării software începe cu Mellroy, care a prevăzut un viitor strălucit pentru o tehnologie a unei componente software la Conferinţa NATO de Ingineria programării din 1968. Din punctul său de vedere, ar trebui să fie posibilă asamblarea sistemelor şi componentelor mari dintr-un număr de blocuri gata de utilizat, aşa cum sistemele hardware sunt asamblate folosind componente standard.

Page 51: Ingineria Programarii

51/135

Pentru ca reutilizarea componentelor software să fie realizabilă, trebuie mai întâi să rezolvăm următoarele probleme:

• Căutarea: Trebuie să căutăm componenta potrivită într-o bază de date de componente disponibile. Acest lucru este posibil numai dacă avem metode disponibile potrivite pentru descrierea componentelor. Dacă nu ştii să specifici ceea ce cauţi, există o şansă mică să găseşti acel lucru;

• Înţelegerea: Pentru a decide dacă o anume componentă este utilizabilă, este nevoie de o înţelegere precisă şi suficient de completă a ceea ce face respectiva componentă;

• Adaptarea: Componenta selectată poate să nu se potrivească fix problemei. A cârpi codul nu reprezintă o variantă satisfăcătoare şi, în orice caz, justificată numai atunci când este profund înţeleasă;

• Compunerea: Un sistem este format din mai multe componente cuplate. Se pune problema: cum conectăm componentele între ele?

Componentele hardware sunt de obicei clasificate într-o ierarhie multinivel. Având în vedere că s-au standardizat deja convenţiile de nume, se poate trece în revistă ierarhia. La cel mai de jos nivel sunt date descrieri alternative ale componentelor, ca de exemplu: descrierea limbajului, schema logică şi informaţii de sincronizare, care descriu diferite aspecte ale componentelor.

S-au făcut unele eforturi pentru a clasifica componentele software după principii ierarhice, în taxonomia lui Booch (1987), o componentă este descrisă mai întâi de nivelul de abstractizare pe care îl implementează. O parte din taxonomia sa este prezentată în tabelul următor, în al doilea rând, componentele sunt descrise de comportamentul lor în spaţiu şi timp.

monolitice şiruri de caractere, stive, cozistructuri polilitice Liste, arbori, grafuri instrumente utilităţi filtre

în ceea ce priveşte ultimele dimensiuni, Booch deosebeşte patru categorii: • Concurenţă: obiectele îşi pot păstra sau nu semantica proprie atunci când există

mai multe fire de control; • Spaţiu: obiectele sunt sau nu sunt statice ca dimensiune; • Manager de memorie: obiectele pot sau nu administra singure memoria; • Iterator: componentele pot sau nu pot pune la dispoziţie operaţii care să permită

acces la obiectele cuprinse de componentă. Tabelul următor descrie o altă taxonomie a unor bine cunoscute structuri de date. Pentru

a crea această taxonomic au fost utilizate relaţiile structurale dintre elementele unor structuri de date compuse. De exemplu, există o relaţie 1-1 între elementele unei structuri lineare, ca lista sau coada.

0-0 mulţimi 1-1 Stive,cozi, liste1-n Arbori structuri

n-m grafuri Acest fenomen funcţionează pentru ierarhii de componente în general. Dacă nu se

cunoaşte modul în care este organizată ierarhia, există doar o mică şansă să se găsească componenta căutată. Exemplele din tabelele de mai sus induc oarecum în eroare, deoarece componentele de pe nodurile frunză ale ierarhiei încorporează abstractizări foarte cunoscute, în alte domenii va fi mai puţină înţelegere reciprocă în ceea ce priveşte componentele primitive şi denumirea acestora. Prin urmare, construirea unei taxonomii utilizabile este de obicei mai greu de realizat în alte domenii.

Page 52: Ingineria Programarii

52/135

O altă observaţie care se poate face privind reutilizarea componentelor priveşte granularitatea lor. Cu cât este mai mare o componentă, cu atât va fi mai bun rezultatul reutilizării ei. Pe de altă parte, componentele mari tind să devină mai greu de reutilizat, având în vedere că gradul de reutilizare a unei componente scade o dată cu creşterea mărimii componentei. Acest lucru este datorat faptului că, în general, componentele mai mari tind să impună constrângeri mai multe mediului lor. Există aici o analogie cu teorema fundamentală a biologiei a lui Fisher: cu cât un organism este mai adaptat unui mediu dat, cu atât este mai puţin pregătit pentru un alt mediu.

În domeniul clasificării componentelor, o contribuţie semnificativă aparţine lui Prieto-Diaz. O bibliotecă de componente bazate pe schema clasificării sale ajută la localizarea componentelor potenţialelor utilizabile şi la estimarea efortului necesar pentru a adapta o componentă, atunci când aceasta a fost găsită.

Schema clasificării lui Prieto-Diaz foloseşte un număr de caracteristici, numite faţete, care descriu fiecare componentă. De exemplu, componentele într-un mediu UNIX pot fi clasificate funcţie de acţiunea pe care o încorporează, de obiectul pe care îl manipulează sau de structura de date folosită. Clasificarea unui obiect înseamnă alegerea unei n-tuple care se potriveşte cel mai bine acelei componente.

Clasificarea bazată pe faţete are anumite avantaje faţă de clasificarea bazată pe enumerare, aşa cum este utilizată în exemplele din taxonomia lui Booch. Schemele strict enumerative folosesc o ierarhie predefinită şi obligă la căutarea unui nod care se potriveşte cel mai bine componentei ce trebuie clasificată. Deşi pot fi folosite şi referinţe încrucişate cu alte noduri, reţeaua rezultată devine destul de complicată.

De obicei este greu de găsit o componentă care să se potrivească perfect cerinţelor noastre. Dar asta nu înseamnă că nu se poate găsi o componentă utilizabilă. Sistemul lui Prieto-Diaz oferă posibilitatea de a expanda întrebările deoarece ia în considerare şi componentele care sunt apropiate de cea căutată, pentru oricare din faţete. Pentru a determina apropierea de o componentă, se fac măsurări potrivite ale distanţei dintre noţiunile care compun faţetele.

O dată ce s-a găsit o mulţime de componente candidate, acestea sunt ordonate de către un sistem de evaluare după criteriul considerat al potrivirii lor. În afară de distanţa conceptuală, proprietăţi ca lungimea (o estimare subiectivă), structura (numărul de module, complexitatea) şi documentarea (o estimare subiectivă) joacă un rol în procesul de selecţie.

Un proiect de cercetare construit pe baza rezultatelor obţinute de Prieto-Diaz este descris de Burton (1987). El şi asociaţii săi au implementat un prototip de bibliotecă de software reutilizabil. În sistemul lor, descrierea fiecărei componente software este reprezentată de un număr de atribute, cum ar fi lungimea componentei, complexitatea sa, rezultatele testării, erorile raportate, calitatea documentaţiei şi readaptabilitatea. Dacă trebuie aleasă una dintre componente, reutilizatorul poate indica interactiv relevanţa fiecărui atribut. Este obţinută apoi o ordine a importanţei, bazată pe indicaţiile utilizatorului. Variind valorile diferitelor atribute, reutilizatorul poate avea o vedere de ansamblu a valorii relative a componentelor eligibile.

Perspectiva lui Prieto-Diaz a fost aplicată cu succes şi în alte programe de reutilizare. Aceste proiecte diferă considerabil în ceea ce priveşte schemele lor de clasificare şi de recuperare şi cantitatea de instrumente ajutătoare puse la dispoziţie de mediu. Schemele de clasificare variază de la un simplu cuvânt cheie într-un context până la recuperarea complet automată a unui cuvânt cheie din documentaţia existentă; pot fi de asemenea implicate până şi cunoştinţe elaborate din domeniul aplicaţiei, în ceea ce priveşte recuperarea, sistemele pot întrebuinţa tezaure extensibile pentru a lega termeni similari, sau pot oferi posibilităţi de navigare pentru a inspecta componente similare.

Page 53: Ingineria Programarii

53/135

Experienţele concrete arată însă că, din punct de vedere practic, bibliotecile de componente utile nu conţin un număr mare de componente. De exemplu, Prieto şi Diaz raportează în 1991 că numărul de articole ale bibliotecii din laboratoarele GTE a ajuns de la 190 în 1988 la 128 în 1990. Din această cauză, clasificarea şi recuperarea componentelor nu este, de cele mai multe ori, principalul impediment în calea unui program reutilizat de succes. Adevărata problemă este completarea bibliotecii cu componentele corecte.

5.2.2 Şabloane În paragrafele precedente am presupus că bibliotecile de componente sunt bucăţi de cod

gata de folosit. Aplicabilitatea unei componente poate fi mărită prin omiterea specificării anumitor detalii. Şabloanele sunt componente neterminate. Prin instanţierea lor, de exemplu prin completarea unor detalii, rezultă componente (re)utilizabile.

Un exemplu de posibil şablon este o procedură care implementează un algoritm de sortare. Detalii ca limitele mulţimii de sortat, tipul elementelor mulţimii şi operatorii relaţionali folosiţi pentru a compara elementele mulţimii nu prezintă importanţă pentru esenţa algoritmului.

Aceasta se reduce în ultimă instanţă la separarea dintre funcţionalitate şi tipurile de date folosite în implementare. Potenţiala reutilizare este puternic mărită dacă biblioteca conţine, pe lângă tipuri abstracte de date, şi şabloane.

Cu cât sunt acceptate mai multe detalii, cu atât se poate aplica un şablon mai general. Totuşi există un preţ pentru toate acestea. Costul plătit pentru a obţine o aplicaţie completă probabil va creşte proporţional cu numărul de goluri ce trebuie umplute.

Şabloanele nu trebuie constrânse numai la subrutine. Există şabloane care pot fi instanţiate într-un program complet pentru un domeniu de aplicaţie foarte precis, în acest caz, avem de-a face cu generatoare de aplicaţii.

5.2.3 Reutilizarea proiectării Reutilizarea proiectării se dovedeşte profitabilă într-un mediu în care este dezvoltat

mereu şi mereu acelaşi tip de program. În multe medii de afaceri aplicaţiile sunt considerate în mod greşit unice. Drept urmare, ele sunt proiectate şi implementate pornind de la zero de fiecare dată. Lanergan şi Grasso (1984) declarau că în programele comerciale scrise în COBOL există numai câteva operaţii de bază diferite, cum ar fi sortarea, actualizarea şi raportarea.

Reutilizarea arhitecturii, adică modalitatea în care diverse părţi a unui sistem se îmbină, este oarecum diferită de reutilizarea componentelor software sau a şabloanelor. O bibliotecă de arhitecturi software nu este chiar aşa de utilă. Pentru fiecare problemă în parte am putea mai degrabă să căutăm o arhitectură specifică. O arhitectură nepotrivită nu poate fi niciodată baza unui sistem bun. Situaţia se modifică oarecum dacă o problemă apare în mod repetat în diferite variante. Dacă există o arhitectură standard utilă pentru un anumit tip de problemă, aceasta poate fi aplicată tuturor variantelor viitoare.

Un prim domeniu din ştiinţa calculatoarelor în care se poate reutiliza arhitectura software este construcţia compilatoarelor. Marea majoritate a compilatoarelor sunt construite cu aceleaşi componente: parser, analizor lexical, analizor sintactic, tabelă de simboluri, generator de cod. Există câteva tipuri bine definite de parsere, cum ar fi parserele LL(1) sau LALR(l). Există o teorie amplă despre cum funcţionează compilatoarele, în acest mod a evoluat o arhitectură de compilatoare standard general acceptată. Evident, nu s-a demonstrat niciodată că aceasta este singura cale, sau cea mai bună cale pentru a construi compilatoare. Dar este o metodă fără cusur şi bine cunoscută de a ataca probleme într-un domeniu dificil.

Page 54: Ingineria Programarii

54/135

Un alt exemplu este domeniul mediilor de dezvoltare software, în care majoritatea întrebuinţează un depozit de informaţii centrale înconjurată de un număr mic de straturi ce conţine instrumente ce devin din ce în ce mai specifice către straturile exterioare.

Reutilizarea arhitecturii se regăseşte rareori şi în alte domenii. Principalul motiv este că un corp similar de cunoştinţe partajate şi cristalizate pur şi simplu nu există. Putem totuşi observa cum câteva tipuri de arhitecturi standard au fost implementate în alte discipline de inginerie. De exemplu, Spector (1986) descrie procedurile utilizate în construirea podurilor de autostradă din punctul de vedere al ingineriei programării. Standish (1984) observă că în fiecare disciplină de inginerie trebuie depusă o muncă enormă şi câştigată multă experienţă înainte de a realiza o astfel de standardizare.

5.2.4 Sisteme de transformare Am menţionat deja natura transformaţională a procesului de implementare software. O

specificaţie a cerinţelor descrie sistemul ce trebuie realizat într-o anume reprezentare, fie că e vorba de limbaj natural, limbaj formal sau pictural, în urma unor stadii intermediare, această descriere este transformată într-un produs final, codificat într-un limbaj de programare oarecare.

Două tipuri de transformări se repetă în timpul acestui proces: • rafinări: prin adăugarea detaliilor şi deciziilor de implementare se rafinează

descrierea produsului. • transformări lingvistice: în timpul anumitor paşi, descrierea produsului este

translatată dintr-un limbaj în altul. Evident, ambele tipuri de transformări pot fi făcute manual, ceea ce deseori se şi

întâmplă. Putem lua totuşi în considerare posibilitatea asistenţei computerizate în realizarea acestor transformări, adică, mai precis, un sistem automat care să facă transformări.

Cel mai simplu punct de pornire ne este pus la dispoziţie de transformările lingvistice ale clasei. O construcţie de forma:

If Exists i in 1..N SuchThat A[i] = x then ... este foarte clară. Din păcate, această construcţie nu există în majoritatea limbajelor de

nivel înalt folosite în prezent. Prin urmare, la un moment dat, construcţia IfExists va trebui înlocuită cu o secvenţă de cod echivalentă semantic.

Un sistem de transformare pentru un domeniu de aplicaţie larg va necesita în general îndrumare umană pentru realizarea rafinării. De exemplu, dacă o descriere de nivel înalt aminteşte de mulţimi de obiecte, aceasta se poate rafina printr-o reprezentare folosind liste sau arbori binari, în general, programatorul este cel care va decide ce structuri de date se potrivesc cel mai bine aplicaţiei, dacă sistemului de transformare îi lipsesc cunoştinţele pentru a realiza automat acest lucru.

Cea mai dificilă sarcină a unui utilizator de sistem de transformare este identificarea unor nivele intermediare consistente, bine definite care trebuie folosite în procesul de transformare. Dacă reuşim să descompunem acest proces în nişte paşi bine separaţi din punct de vedere conceptual, atunci transformările devin realizabile.

O întrebare interesantă este dacă putem formaliza toate construcţiile şi construi un compilator inteligent care să translateze o proiectare în cod executabil fără intervenţie umană. Acest lucru este într-adevăr posibil dacă restricţionăm domeniul de aplicaţie la unul foarte îngust. Pentru a putea rescrie din toate punctele de vedere o proiectare, trebuie să se introducă în sistem, într-un fel sau altul, o cantitate suficientă de cunoştinţe în domeniul aplicaţiei. Astfel de sisteme sunt generatoarele de aplicaţii.

Page 55: Ingineria Programarii

55/135

5.2.5 Generatoare de aplicaţii şi limbaje de a patra generaţie Generatoarele de aplicaţie scriu programe. Un generator de aplicaţie are un volum de

cunoştinţe despre domeniul aplicaţiei, care este de obicei destul de îngust. Pentru a obţine un program este nevoie, evident, de o specificaţie a acestuia. De îndată ce specificaţia este disponibilă, programul este generat automat.

Principul folosit este acelaşi pe care se bazează un şablon: programul efectiv de generat este deja construit în generatorul de aplicaţii. Instanţierea unui program efectiv este realizată prin completarea unui număr de detalii. Diferenţa faţă de şablon este aceea că mărimea codului livrat este mult mai mare la un generator de aplicaţii. De asemenea, detaliile sunt în general furnizate la un nivel mai înalt de abstractizare, în ceea ce priveşte conceptele şi noţiunile extrase din domeniul de aplicaţie.

Un generator de aplicaţii poate fi întrebuinţat în orice domeniu care are o astfel de structură încât operaţiile complicate din cadrul domeniului să poată fi automatizate în mare măsură. Un exemplu îl reprezintă generarea automată a rapoartelor din baze de date. Aşa numitele compilatoare de compilatoare reprezintă un alt exemplu tipic de generator de aplicaţii: având gramatica unui limbaj de programare (adică detaliile), rezultă un parser pentru acel limbaj.

Limbajele de a patra generaţie sau limbajele de nivel foarte înalt (engl. VHLL, „very high level languages") sunt deseori menţionate alături de generatoarele de aplicaţii. Limbajele de a patra generaţie oferă construcţii de programare la un nivel mai înalt decât limbajele de programare de a treia generaţie.

Expresiile dintr-un domeniu de aplicaţie dat pot fi introduse direct în limbajul de a patra generaţie corespunzător. Prin urmare, limbajul de a patra generaţie trebuie să aibă cunoştinţe despre domeniul de aplicaţie respectiv. Aceasta înseamnă că şi limbajele de a patra generaţie sunt potrivite numai pentru un domeniu specific, limitat.

Nu există nici o diferenţă fundamentală între limbajele de a patra generaţie şi generatoarele de aplicaţie. Atunci când se doreşte testarea capacităţilor generative ale unui sistem, se foloseşte de obicei generatorul de aplicaţii. Termenul „limbaj de a patra generaţie" evidenţiază construcţiile de programare de nivel înalt ce sunt oferite.

Generatoarele de aplicaţii şi limbajele de a patra generaţie oferă nişte reduceri potenţiale de cost, având în vedere că detaliile de implementare nu trebuie să ţină cont de probleme ca: nivelul de inteligibilitate al codului să fie mare, cantitatea de cod scris să fie mică, numărul de erori să fie mic, codul să fie uşor de întreţinut.

În practică, utilizatorul poate dori ceva ce nu este oferit de sistem, în acest caz, o bucată de cod scris „la mână" trebuie adăugată programului generat automat. Făcând acest lucru se pierde unul din marile avantaje ale folosirii limbajelor de a patra generaţie, şi anume scrierea de programe inteligibile la un nivel mai ridicat de abstractizare.

5.3 Reutilizarea instrumentelor şi a tehnicilor În acest paragraf vom descrie un număr de concepte, metode şi tehnici care pot avea un

impact pozitiv asupra reutilizării software, rediscutând perspectivele din secţiunile precedente.

5.3.1 Limbaje de interconectare a modulelor Relaţia dintre diferitele module ale unui sistem pot fi exprimate formal într-un Limbaj

de Interconectare a Modulelor (engl. MIL, „module interconnection language"). MIL reprezintă un instrument important atunci când se proiectează şi se întreţin sisteme mari, constituite din diverse module. O descriere MIL este o descriere formală a structurii formale a unui sistem software, care poate fi utilizată pentru a verifica automat integritatea sistemului şi pentru a verifica dacă diversele module sunt conforme cu interfeţele aprobate.

Page 56: Ingineria Programarii

56/135

Vom prezenta în continuare un mic fragment dintr-un cod MIL pentru a ilustra aspectul general a unui astfel de limbaj. Exemplul priveşte structura unui compilator cu un singur pas. Notaţia folosită este INTERCOL (Tichy, 1979), în care descrierea constă dintr-o secvenţă de module şi de familii de sisteme urmate de un set de compoziţii. Exemplul conţine o singură familie de sisteme. Un membru al acestei familii reprezintă o versiune a unui sistem. De notat că familia de module scan are implementări multiple, câte una pentru fiecare limbaj suportat. Compoziţia dată la sfârşitul descrierii selectează un număr de blocuri definite anterior. system compile provide compile module compiler provide procedure compiler require scanner, parser, postfix_generator, symtbl_funct end compiler module scan provide package scanner is type token: ALPHA function nextchar_funct return: CHARACTER procedure backup_proc end scanner require symtbl_proc, errormsg_proc implementation SCAN02 .. PL/I implementation SCAN03 .. PASCAL end scan … module errormsg provide procedura errormsg_proc implementation ERRMSG end errormsg COMPOSITION compile_A = [compile, SCAN02, PARSE.NIP, SYMBL.PL/I, POSTFIXGEN.NIP, SYSLBL, VRFY, ERRORMSG] end compile

Ideile de bază din spatele implementării MIL sunt:

• Un limbaj separat pentru proiectarea de sisteme: MIL nu reprezintă un limbaj de programare. El doar descrie proprietăţile dorite ale modulelor care vor deveni parte componentă a sistemului considerat;

• Verificări ale tipurilor statice între diferite module: Acest lucru garantează automat că diferitele module respectă interfaţa. O interfaţă poate fi modificată numai după ce a fost realizată respectiva modificare în proiectare;

• Proiectarea şi legarea modulelor într-o singură descriere: In vremurile de început ale programării sistemelor complexe, diversele module erau asamblate la mână. Folosind MIL, acest lucru se poate face automat;

• Controlul versiunilor: Pentru a evita confuziile dintre versiunile (părţilor) unui sistem în timpul implementării şi întreţinerii, este nevoie de o abordare organizată.

Page 57: Ingineria Programarii

57/135

Conceptele de bază ale unui MIL sunt: • resursele: tot ce are nume într-un limbaj de programare (constantă, tip, variabilă,

procedură) şi tot ce poate fi făcut disponibil de către un modul pentru un alt modul;

• modulele: care fac resursele disponibile sau care le utilizează; • sistemele: grupuri de module care îndeplinesc împreună o sarcină bine definită.

Pentru lumea exterioară, un sistem poate fi văzut ca un singur modul. Legătura dintre module poate fi modelată ca un graf: nodurile grafului reprezintă

module, în timp ce arcele (orientate) reprezintă dependenţele dintre module. Funcţie de gradul de complexitate al MIL, acest graf poate fi un arbore, un graf orientat aciclic sau un graf orientat fără nici o restricţie.

Toate MIL arată aceleaşi limitări: ele se implică numai în sintaxa interfeţelor. Nu se poate aprecia dacă resursele prelucrate au sens sau nu. Goguen (1986) a încercat să facă un pas mai departe pentru a asigura unui MIL o semantică limitată. Sistemul lui se numeşte Limbaj de Interconectare a Bibliotecilor (engl. LIL, „library interconnection language"). LIL conţine următoarele entităţi:

• pachete cu axiome asociate: aceste axiome spun ceva legat de operaţia pachetului. Axiomele pot fi formale (într-o logică primară) şi informale (limbaje naturale);

• teorii: seamănă cu pachetele, dar nu conţin deloc cod. Acestea arată numai că ar trebui furnizate anumite tipuri şi funcţii şi pun la dispoziţie axiomele care descriu comportarea acestor funcţii;

• vizualizările: arată cum este îndeplinită o anumită teorie. Un exemplu ar putea clarifica această descriere. Fie SORT un pachet generic pentru

sortarea unei secvenţe, încă nu am specificat tipul elementelor din secvenţă. Teoria ORDEREDSET ne spune ce este aceea o ordonare, folosind următoarele axiome:

• 1 2E E≤ (reflexivitate); • 1 2 2 3 1 3,E E E E E E≤ ≤ ⇒ ≤ (tranzitivitate); • 1 2 2 1 1 2,E E E E E E≤ ≤ ⇒ = (antisimetrie); • 1 2E E≤ sau 2 1E E≤ (ordine completă).

Mulţimea numerelor naturale cu relaţia de ordine „>" satisface aceste axiome. Dar există multe alte ordonări posibile (cum ar fi: toate numerele pare care sunt mai mari decât toate numele impare şi atât numerele pare cât şi cele impare să fie în ordinea firească). Dacă dorim să sortăm o secvenţă în ordine descrescătoare, alegem o vizualizare care are ca elemente ale ORDEREDSET numerele naturale şi relaţia „>" ca relaţie de ordine.

Construcţia de mai sus a fost numită de către Goguen compoziţie orizontală. Vizualizarea descrie o interfaţă între două componente aflate la acelaşi nivel de abstractizare. O componentă caută o teorie, cealaltă satisface axiomele, pe când vizualizarea ne transmite cum se potrivesc ele. Este de asemenea posibilă şi o compoziţie verticală, în pachetul SORT nu a fost încă definită modalitatea în care trebuie implementată o secvenţă. De aceea, SORT trebuie să fie conectat la o componentă cu un nivel mai coborât de abstractizare care implementează o secvenţă.

Este evident că axiomele care nu sunt exprimate într-o notaţie formală nu pot fi verificate automat. De aceea, Goguen propune un adevărat sistem de management, care ar trebui să poată verifica axiomele (de exemplu: aprobarea de către un demonstrator de teoreme, de către programator, executarea cu succes a câtorva teste), în caz că apare o eroare, o analiză a căii critice ar putea indica cea mai slabă legătură în lanţul demonstraţiei.

Putem constata că MIL se potriveşte bine cu sistemele de transformare şi alte forme de reutilizare în care proiectarea joacă un rol esenţial. Atunci când se caută o componentă

Page 58: Ingineria Programarii

58/135

potrivită într-o bibliotecă de module, semantica acestora este mult mai importantă decât sintaxa interfeţei. LIL reprezintă un prim pas în această direcţie, deoarece legătura semantică este una din cele mai importante probleme în demararea reutilizării.

5.3.2 Programarea orientată obiect Programarea orientată obiect a fost una din cele mai răsunătoare sintagme ale anilor '80.

Care este potenţialul limbajelor orientate obiect în ceea ce priveşte reutilizarea software? Este evident că bibliotecile de module devin mult mai utilizabile dacă conţin module orientate obiect. Dacă modulul căutat nu este găsit, nu este necesară adaptarea unui modul aproape potrivit prin cârpirea codului, acesta este întotdeauna un demers periculos. Caracteristicile dorite pot fi moştenite dintr-o clasă convenabilă deja prezentă în bibliotecă iar caracteristicile în plus pot fi adăugate unei subclase nou definite.

Moştenirea şi polimorfismul unui limbaj orientat pe obiect asigură prin ele însele oportunităţi pentru construirea părţilor reutilizabile. Acestea promovează apariţia unor clase abstracte care pot servi ca punct de plecare pentru derivarea mai multor componente specifice.

5.4 Perspectivele reutilizării software După cum spunea Johnson în 1988, „abstractizările utile sunt descoperite, nu inventate",

în orice tehnologie de reutilizare, blocurile reutilizabile, fie că sunt sub forma unor subrutine, şabloane, transformări sau soluţii ale problemelor cunoscute de proiectanţi, corespund pieselor de cunoaştere cristalizate, piese care pot fi folosite în alte situaţii decât cele pentru care au fost gândite iniţial. O întrebare centrală în toate tehnologiile de reutilizare discutate mai sus este felul în care se poate exploata o mulţime dată de blocuri reutilizabile. Aceasta are o mare importanţă în diversele proiecte din domeniul bibliotecilor de componente, unde principalul scop este de a prevedea modalităţi de extragere a componentei utilizabile pentru sarcina în lucru, în mod alternativ, putem privi reutilizarea software drept identificarea blocurilor de care avem nevoie pentru a le putea folosi în diverse aplicaţii.

Atunci când se încearcă identificarea unor componente refolosibile, principala problemă este de a se decide de ce componente avem nevoie. O componentă refolosibilă trebuie valorificată, nu din motivul evident că ne scuteşte de a implementa funcţionalitatea noi înşine, ci pentru că oferă o parte din cunoştinţele domeniului corect, adică exact funcţionalitatea de care avem nevoie, câştigată după multă experienţă şi după mai multe încercări de a găsi abstractizările corecte. Componentele ar trebui să reflecte noţiunile primitive ale domeniului aplicaţiei. Pentru a putea identifica o mulţime adecvată de primitive pentru un domeniu dat, este necesară o experienţă considerabilă în implementarea unui software pentru acel domeniu. Pe măsură ce este câştigată această experienţă, evoluează încet şi mulţimea adecvată de primitive.

Un domeniu este caracterizat printr-o colecţie de noţiuni comune care au coerenţă, în timp ce în afara domeniului, aceleaşi noţiuni nu există sau nu prezintă aceeaşi coerenţă. Domeniile pot fi mai largi sau mai restrânse, de exemplu:

• program pentru contabilitate; • program pentru contabilitate multinaţională; • program pentru contabilitate multinaţională implementat de Soft SRL.

Toate programele de contabilitate vor conţine noţiuni ca „registru" şi „balanţă". Programul pentru contabilitate multinaţională ca avea câteva noţiuni referitoare la fluxul de bani între diferite ţări, noţiuni care nu există în sistemele de contabilitate pentru aprozare sau gogoşerii. Reprezentările noţiunilor în programul dezvoltat de Soft SRL vor fi diferite de cele ale altor firme din cauza folosirii diferitelor metodologii sau convenţii.

Pentru majoritatea domeniilor, nu este evident imediat care sunt primitivele corecte. Este în ultimă instanţă o problemă de încercare şi eroare. Astfel, încet-încet se găseşte

Page 59: Ingineria Programarii

59/135

mulţimea adecvată de primitive. Pe măsură ce se dezvoltă un domeniu, deosebim un număr de etape:

• La început, nu există încă un set clar de noţiuni şi tot programul este scris de la început. Experienţa se câştigă încet, pe măsură ce se învaţă din greşelile anterioare;

• în etapa a doua, probleme similare sunt depistate şi rezolvate în moduri similare. Sunt recunoscute primele primitive semantice. După ce se încearcă şi se dă greş, se decide care sunt primitivele utile şi care sunt cele inutile;

• în a treia etapă, domeniul este gata de reutilizare. S-au implementat un număr rezonabil de programe, s-a stabilit o mulţime de concepte, s-au găsit soluţii standard pentru o serie de probleme standard;

• în sfârşit, domeniul a fost explorat în totalitate. Implementarea programelor pentru domeniu poate fi automatizată. Nu se mai programează efectiv, ci se foloseşte o interfaţă standard formată din primitivele semantice ale domeniului.

Majoritatea reutilizării apare în ultima etapă, deşi nu mai este recunoscută ca atare. Acum mult timp în urmă, calculatoarele erau programate în limbaj de asamblare, în limbaje de nivel înalt „scriem ce dorim" şi compilatorul transformă acesta într-un program „adevărat". Această acţiune nu mai este privită ca reutilizare. Un fenomen asemănător apare la tranziţia dintr-un limbaj de a treia generaţie într-un limbaj de a patra generaţie.

Din punctul de vedere al reutilizării, clasificarea de mai sus este una normală, naturală pentru evoluţia unui domeniu. Diversele etape sunt categorizate la nivele calitative diferite: •în prima etapă nu există reutilizare;

• în a doua etapă reutilizarea este ad hoc; • în a treia etapă reutilizarea este structurată, componentele existente sunt

refolosite într-un mod organizat atunci când se implementează un nou program; • în etapa a patra, reutilizarea este instituţionalizată şi automatizată, efortul uman

este restricţionat la nivele superioare de abstractizare. în cadrul unui domeniu dat este folosit un limbaj informai, în care acelaşi lucru poate fi

exprimat în mai multe moduri, folosind concepte care nu sunt bine definite. Totuşi, limbajul informai este uşor de înţeles deoarece conceptele se referă la un univers de expunere comun atât vorbitorului cât şi ascultătorului.

într-un limbaj formal, conceptele nu se referă la experienţa sau cunoştinţele de zi cu zi. Acestea au înţeles abia într-un sistem formal. Un astfel de sistem formal este o maşină virtuală iar limbajul său este de asemenea un limbaj formal.

A formaliza un domeniu înseamnă a construi un limbaj formal (pe domeniu) care mimează un limbaj informai existent. Trebuie apoi să se aleagă dintre diversele primitivele semantice care există informai. Uneori este convenabil să se adauge noi primitive, care se potrivesc bine domeniului formalizat. De exemplu, să considerăm domeniul setărilor de tehnoredactare. O parte a formatării documentelor se referă la asamblarea cuvintelor în linii şi a liniilor în paragrafe. Secvenţa de cuvinte care compune un paragraf trebuie împărţită în linii astfel încât rezultatul să fie satisfăcător din punct de vedere tipografic.

Knuth (1981) descrie această problemă folosind termenii „cutii", „lipici" şi „penalizări". Cuvintele sunt cuprinse în cutii cu o anume lăţime. Spaţiul alb dintre cuvinte este considerat lipici, care se poate mări sau micşora. Este preferat un spaţiu convenţional între cuvinte adiacente, iar acestei asocieri îi corespunde o penalizare nulă. Apropierea sau depărtarea cuvintelor atrage o penalizare pozitivă. Cu cât este mărit sau micşorat mai mult lipiciul, cu atât este mai mare penalizarea. Prin urmare, penalizarea asociată cu formatarea unui întreg paragraf este suma pedepselor asociate spaţiilor inter-cuvânt din paragraful formatat. Problema poate fi reformulată astfel: împarte paragraful în linii astfel încât penalizarea totală să fie minimă. Trebuie menţionat că penalizările pot fi asociate şi altor proprietăţi nedorite din

Page 60: Ingineria Programarii

60/135

punct de vedere tipografic, aşa cum este despărţirea cuvintelor în silabe la sfârşitul unui rând. Noţiunile „cutii", „lipici" şi „penalizări" oferă o formalizare elegantă a anumitor aspecte ale tehnoredactării. De asemenea, oferă o soluţie eficientă a problemei de mai sus, folosind o tehnică a programării dinamice.

în practică, formalizarea nu este o activitate cu un singur pas. Este mai degrabă un proces iterativ. Versiunea formalizată nu descrie tocmai exact limbajul informai, ci precizează o posibilă interpretare. Dacă îi studiem semantica, acesta poate avea câteva aspecte nedorite, în continuare este atins un compromis acceptabil între cei ce folosesc limbajul (în domeniile de nivel înalt) şi cei care îl implementează (în domeniile de nivel scăzut). Odată ce limbajul domeniului formal este hotărât, el afectează şi limbajul informai al domeniului. Persoanele care lucrează în cadrul domeniului încep să folosească primitivele limbajului formal.

Este acum clar că nu este indicată trecerea directă de la etapa întâi (fără reutilizare) la etapa a treia (reutilizare structurată). Formalizarea are un efect de solidificare asupra primitivelor semantice ale domeniului. Noţiunile privind primitivele se modifică, deoarece acestea nu mai sunt considerate ca venind din universul de discurs intuitiv, ci din formalismul care stă la baza acestuia. Răspunsul la întrebarea crucială dacă am formalizat primitivele semantice corecte devine mai greu de găsit. Etapa a doua (reutilizarea ad hoc) este foarte importantă, în această etapă avem o vedere de interior a domeniului de aplicaţie şi descoperim primitivele semantice utile. Atât în ceea ce priveşte lucrul cu primitivele, cât şi implementarea lor, se dovedeşte că această experienţă este de o importanţă vitală pentru formalizarea domeniul în mod corect. 5. Aspecte non-tehnice ale reutilizării software

Până acum am discutat numai aspectele tehnice ale reutilizării software. Ingineria programării nu este preocupată numai de aspectele tehnice, ci şi de indivizi şi de mediul în care aceştia îşi desfăşoară activitatea. Domeniul ingineriei programării este influenţat de societate. Reutilizarea software în Europa este posibil diferită de cea din Statele Unite sau Japonia. Din cauza diferenţelor culturale şi a structurilor economice diferite, nu este limpede a-priori că abordarea Toshiba poate fi copiată de europeni cu aceleaşi rezultate.

Deşi până acum discuţia noastră s-a referit la aspectele tehnice ale refolosirii software, acesta nu poate fi încheiată fără câteva cuvinte despre problemele non-tehnice, care sunt intim împletite cu cele tehnice. Diverşi cercetători ai refolosirii software au discutat aprins că tehnologia necesară refolosirii software este disponibilă, dar principalele probleme care inhibă o industrie prosperă a refolosirii sunt de natură non-tehnică. Programele refolosibile cu succes conţin următoarele caracteristice:

• Management auxiliar necondiţionat şi extensibil: Un program refolosibil necesită schimbări pe măsură ce este implementat. Implicarea managementului este esenţială pentru a face modificările să funcţioneze, în particular, construirea unei baze de articole reutilizabile necesită o investiţie în avans care s-ar putea să nu fie recuperabilă decât după un timp;

• Stabilirea unei structuri organizatorice auxiliare: Organizaţia trebuie să furnizeze iniţiativa, fondurile şi politicile pentru programul reutilizabil. Este necesar un corp separat pentru a evalua potenţialii candidaţi pentru includerea într-o bibliotecă reutlizabilă. Este necesar un bibliotecar pentru întreţinerea bibliotecii;

• Implementarea incrementală a programului: Un prim catalog cu articole potenţial reutilizabile poate fi construit cu un cost relativ mic. Experienţe pozitive cu o astfel de bibliotecă iniţială vor furniza stimulentele materiale necesare pentru a mări biblioteca, a inventa o schemă de clasificare etc.;

• Succese semnificative, atât pe plan financiar cât şi organizatoric: De exemplu, o creştere cu 50% a productivităţii pe o perioadă de câţiva ani;

Page 61: Ingineria Programarii

61/135

• Programatorii suferă de sindromul „neinventat aici": Prin crearea unui mediu care valorifică atât crearea de software reutilizabil cât şi reutilizarea software, se stabileşte o atmosferă în care reutilizarea poate deveni un succes;

• Analiza domeniului este un proces în care conceptele şi mecanismele pe care se bazează un domeniu bine înţeles sunt identificate şi transformate în resursele reutilizabile.

5.5 Economia reutilizării software

5.5.1 Economia refolosirii software „Reutilizarea este o investiţie pe termen lung." (Tracz, 1990) Reutilizarea nu este

gratuită, într-un mediu tradiţional de dezvoltare, produsele sunt realizate în funcţie de situaţie. Este probabil ca situaţii similare să necesite produse şi componente puţin diferite. Pentru ca o componentă software să devină refolosibilă, trebuie să fie generalizată pornind de la situaţia prezentă, testată şi documentată intens, încorporată într-o bibliotecă şi o schemă de clasificare şi păstrată ca o entitate separată. Aceste operaţii necesită investiţii iniţiale, care încep să fie recuperabile abia după o anumită perioadă de timp.

Recuperări imediate ale investiţiei pot fi obţinute dacă programul refolosit este mic la început şi are o bibliotecă iniţială ai cărei membri sunt extraşi din produsele existente. Dezvoltarea programului poate fi apoi justificată pe baza experienţelor pozitive anterioare. Dar chiar şi atunci trebuie alocate fonduri care nu sunt specifice proiectului.

Consecinţele economice ale reutilizării software depăşesc economiile costurilor în producţie şi întreţinere. Se modifică însăşi natura procesului de dezvoltare software. Programul devine un capital. Costurile iniţiale mari sunt legate de recuperări pe perioade mai mari de timp.

5.5.2 Reutilizarea software şi managementul Producţia de software trebuie să fie organizată astfel încât să fie promovată reutilizarea.

Modelul tradiţional cascadă tinde să împiedice reutilizarea. în modelul cascadă, accentul cade pe măsurarea şi controlarea progresului proiectului. Calitatea produsului în raport cu gradul de reutilizare este greu de măsurat. Nu există un stimulent real pentru a căuta să se realizeze gradul de reutilizare, având în vedere că principalul (şi uneori singurul) scop este de a termina proiectul curent la timp şi încadrat în buget. Nu există nici o motivare în a face următorul proiect să arate bine. în consecinţă, reutilizarea software tinde să capete o prioritate mică. Reutilizarea software trebuie să fie încorporată în procesul de dezvoltare. Articolele reutilizabile trebuie căutate în mod activ.

Chiar şi bibliotecile de articole reutilizabile trebuie administrate. Astfel, trebuie creată o infrastructură organizaţională care face accesibilă biblioteca (documentare, scheme de clasificare), evaluează candidaţii pentru includerea în bibliotecă, menţine şi actualizează biblioteca etc. Un rol organizaţional separat, bibliotecarul, poate fi creat tocmai pentru acest scop. Sarcinile sale seamănă cu cele ale unui administrator de baze de date.

Un alt tip de reutilizare este reutilizarea persoanelor. Proiectanţii experţi valorează greutatea lor în aur. Orice programator mediu este capabil să scrie un program mare, complicat. Pentru a obţine o soluţie radical nouă, mai bună, mai mică, mai elegantă pentru aceeaşi problemă, este nevoie de o persoană care are idei sclipitoare din când în când.

O problemă majoră în domeniul nostru este că administratorii sunt mai apreciaţi decât programatorii sau proiectanţii. Dacă eşti bun cu adevărat, mai devreme sau mai târziu (dar de obicei mai devreme), te vei ridica pe scara ierarhică şi vei ajunge să faci parte administraţie. După Brooks, există un singur mod de a neutraliza acest fenomen. Pentru a ne asigura că aceşti oameni sclipitori rămân proiectanţi de sistem, avem nevoie de o schemă de personal

Page 62: Ingineria Programarii

62/135

duală, în care proiectanţii buni au aceleaşi perspective în ceea ce priveşte slujba ca şi administratorii buni.

5.5.3 Aspecte psihologice ale reutilizării software „Reutilizarea codului altor persoane ar demonstra că mie nu îmi pasă de munca mea. Nu

voi mai reutiliza cod, aşa cum nici Hemingway nu foloseşte paragrafele altor scriitori." (Cox, 1990)

Reutilizarea înseamnă că programatorii trebuie să se adapteze, să încorporeze sau să „reîntinerească" codul scris de alţi programatori. Există două aspecte importante ale acestui proces:

• sunt programatorii dispuşi să facă acest lucru? • sunt capabili să facă acest lucru?

Diverse cercetări arată că programatorii folosesc anumite scheme standard în anumite situaţii standard. Programatorii experimentaţi tind să devină confuzi atunci când o problemă cunoscută a fost rezolvată folosind soluţii ne-standard. Drept consecinţă, gradul de reutilizare a componentelor este mărit dacă aceste componente conţin abstractizări familiare programatorilor. Aceleaşi probleme apar în analiza de domeniu, în care se încearcă identificarea noţiunilor familiare experţilor într-un anumit domeniu.

în alte studii apare ideea că un efect secundar al folosirii proiectărilor standard şi a componentelor standard este înţelegerea crescută a programului rezultat. De îndată ce toţi programatorii se obişnuiesc cu un anumit stil, toate programele par a fi scrise de aceeaşi echipă. Orice echipă poate înţelege şi adapta un program scris de oricare altă echipă.

5.6 Concluzii Reutilizarea software este mai degrabă un obiectiv decât un domeniu. A apărut ca o

problemă în cadrul ingineriei programării, nu din cauza fascinaţiei sale ca problemă ştiinţifică, ci datorită câştigul scontat în productivitatea dezvoltării software. Prima conferinţă despre reutilizarea software a avut loc în 1983. De atunci, acest subiect a primit o atenţie din ce în ce mai mare. în acest curs am discutat principalele perspective ale problemei reutilizării. Diversele tehnologii de reutilizare discutate pot fi divizate în două mari categorii tehnologii bazate pe compunere şi tehnologii bazate pe generare, în primul caz, se urmăreşte încorporarea componentelor existente în noul program ce va fi implementat, într-o tehnologie bazată pe generare, este mult mai dificil să se identifice explicit componentele care se reutilizează. Cunoştinţele reutilizate pot fi găsite în programe care generează alte programe. Au fost descrise în continuare caracteristicile limbajelor de interconectare a modulelor. S-au menţionat perspectivele reutilizării software şi în final s-a insistat şi asupra aspectelor non-tehnice ale reutilizării: economia, managementul şi psihologia reutilizării.

Page 63: Ingineria Programarii

63/135

6 Ingineria cerinţelor. Analiza orientată obiect

6.1 Ingineria cerinţelor Primul pas logic în dezvoltarea unui program este stabilirea precisă a cerinţelor

clientului (ceea ce vrea clientul să facă programul). Partea cea mai importantă a acestui proces o reprezintă comunicarea dintre client şi echipa de dezvoltare. Când un inginer de programe lucrează la stabilirea cerinţelor, el este numit inginer de cerinţe, analist de sistem sau analist. Dezvoltarea unui program începe de obicei cu o idee a clientului despre un nou sistem sau pentru îmbunătăţirea unui sistem existent. Clientul angajează un analist cu care va lucra împreună pentru specificarea mai exactă a cerinţelor. Ideea iniţială a clientului poate fi vagă şi prost definită, sau dimpotrivă, poate fi clară şi bine definită.

Stabilirea cerinţelor este probabil cea mai importantă activitate în dezvoltarea produselor program. Dacă un client nu ştie sau nu poate să stabilească în urma unei discuţii cu echipa de dezvoltare în mod exact ce vrea să facă produsul, este inutil să angajeze o echipă care să programeze. O echipă de programatori poate să scrie cel mai estetic program din punct de vedere al tehnicilor de programare folosite, dar dacă nimeni nu va dori să-1 folosească, proiectul va fi un eşec.

Multe programe nu se potrivesc cu cerinţele clientului nu din motive de implementare defectuoasă, ci din cauză că cerinţele nu au fost specificate corect de la început. Persoane ale căror legături cu ingineria programării sunt mai mult pretinse decât obiective consideră că nepotrivirea dintre aşteptările clientului şi programul obţinut ţin de lipsa de cultură, simţ artistic şi cunoştinţe tehnice ale programatorilor. Adevărul este că programatorii nu pot şi nu au cum să cunoască necesităţile clienţilor, mai ales dacă nu cunosc domeniul pentru care este scrisă o anumită aplicaţie.

Este responsabilitatea clientului de a veni cu cereri exacte şi pertinente. Este obligaţia inginerului de cerinţe să discute cu clientul pentru a clarifica cerinţele şi a-1 ajuta să-şi fixeze priorităţile. Stabilirea precisă a cerinţelor este primul pas în obţinerea unui program care satisface nevoile clientului. O specificaţie bună este folosită şi în fazele de validare şi verificare.

6.1.1 Cerinţa Noţiunea de cerinţă este mai veche decât dezvoltarea programelor. De exemplu, o

cerinţă pentru o locomotivă ar putea arăta astfel: pe o cale ferată uscată, locomotiva trebuie să fie capabilă să pornească un tren de 100 tone pe o pantă de maxim 5% cu o acceleraţie de cel puţin 0,5 m/s2. De remarcat că această cerinţă spune ce vrea clientul. Nu spune nimic despre cum ar putea fi realizată. Nu se specifică tipul motorului (cu aburi, diesel, electric) sau materialul din care să se confecţioneze roţile.

Apare aici o problemă controversată: este bine să facem specificarea luând în considerare felul în care sistemul va fi implementat? Fiecare variantă are avantajele şi dezavantajele ei:

• Nu folosim detalii de implementare: Nu ne aşteptăm ca un client să ştie lucruri specifice dezvoltării programelor, şi deci nu poate să fie de acord în cunoştinţă de cauză cu stipulările din specificaţii. Secretarele care folosesc Excel ştiu COM? E necesar să facem constrângeri asupra programului încă din faza de concepere?

• Folosim detalii de implementare: Câteodată nu este posibil să ignorăm o implementare existentă. Dacă facem un program pentru o bibliotecă, studiem implementarea existentă (tipul fişelor ce trebuie completate, fluxul de lucru,

Page 64: Ingineria Programarii

64/135

modul de organizare al mediilor de stocare a informaţiilor) şi o „copiem" în program. Putem verifica dacă o cerinţă este sau nu rezonabilă din punct de vedere tehnic. Pentru a putea estima timpul de dezvoltare şi preţul, trebuie luată în considerare implementarea. Există o diferenţă între a programa în Visual Basic şi a programa în Visual C++. Costurile iniţiale la programarea în Visual Basic vor fi mai mici (timp de dezvoltare mai mic, programatori mai ieftini), însă odată ce produsul necesită dezvoltarea peste o anumită limită a complexităţii, costurile vor creşte (întreţinere greoaie, probleme de performanţă).

6.1.2 Extragerea cerinţelor Presupunem că analistul şi clientul lucrează împreună, fiecare încercând să îl înţeleagă

pe celălalt. Esenţa procesului de obţinere a cerinţelor este comunicarea. Se disting trei activităţi majore care conduc la obţinerea unei specificări a cerinţelor:

• Ascultare: analistul înregistrează cerinţele clientului; • Gândire: analistul încearcă să traducă cerinţele clientului în limbaj tehnic şi să se

asigure de pertinenţa cerinţelor în acest context; • Scriere: analistul şi clientul cad de acord asupra unor formulări ale cerinţelor pe

care analistul le consideră pertinente. Aceste activităţi sunt parte a unui proces iterativ, lung şi complicat. Negocierea este

foarte importantă. Diferenţa culturală dintre client şi analist este câteodată foarte mare. Există situaţii când există diferenţe semnificative între aşteptările utilizatorilor finali şi ale clientului. Un exemplu concret al acestui tip de conflict este când un patron ce comandă un program care să fie utilizat de către angajaţii săi doreşte ca acesta să conţină module de monitorizare a muncii angajaţilor. Aceasta ridică o dilemă de natură etică pentru inginerul programator: să anunţe angajaţii că sunt spionaţi fără să ştie sau să fie loial celui care îl plăteşte. O altă problemă o reprezintă literatura SF studiată de client, înainte de angajarea echipei de dezvoltare. Clientul aşteaptă mult prea mult de la program şi de la echipa de dezvoltare, închipuindu-şi că programele se scriu ca în filme, într-o scenă de două minute, ca un amănunt dintr-un film de două ore. în concluzie, analistul trebuie:

• să extragă şi să clarifice cerinţele clientului; • să ajute la rezolvarea diferenţelor de opinie între clienţi şi utilizatori; • să sfătuiască clientul despre ce este tehnic posibil sau imposibil; • să documenteze cerinţele; • să negocieze şi să obţină o înţelegere cu clientul.

6.1.3 Metode pentru identificarea cerinţelor utilizatorilor Una sau mai multe din următoarele modele prezentate mai jos pot fi folosite pentru a

identifica cerinţele utilizatorului: • Interviuri: Interviurile trebuie astfel structurate încât să poată aborda toate

aspectele implicate de sistemul software ce va trebui dezvoltat. Când există foarte mulţi utilizatori, se va selecta un set reprezentativ dintre aceştia pentru a fi intervievaţi. Interviurile pot fi utile în a asigura:

o completitudinea cerinţelor utilizatorului; o existenţa unei acceptări generale a cerinţelor utilizatorului;

• Studiul sistemelor software existente deja: De multe ori, noul sistem software este destinat înlocuirii altui sistem existent. Investigarea caracteristicilor şi bune şi rele ale sistemului existent poate ajuta în determinarea cerinţelor pentru ceea

Page 65: Ingineria Programarii

65/135

ce se doreşte. Examinarea manualelor utilizatorilor, a documentelor cerinţelor şi a diverselor propuneri poate fi foarte folositoare;

• Studiul de fezabilitate: Studiul de fezabilitate reprezintă analiza şi proiectarea principalelor caracteristici ale sistemului în scopul de a determina dacă implementarea este posibilă;

• Prototipul: Un prototip este un model executabil al unor aspecte selectate din sistemul propus. Dacă cerinţele sunt neclare sau incomplete, ar putea fi utilă dezvoltarea unui prototip, bazat pe un set de cerinţe de probă, pentru a identifica cerinţele reale ale utilizatorilor.

6.1.4 Metode pentru specificarea cerinţelor utilizatorilor

6.1.4.1 Limbajul natural Modul evident de specificare a unei cerinţe este limbajul natural. Limbajul natural este

foarte accesibil dar inconsistent şi ambiguu. De exemplu, afirmaţia: Baza de date va conţine o adresă poate fi interpretată după cum urmează:

• Va fi doar o singură adresă • parte din baza de date va fi desemnată ca o adresă. • Va fi cel puţin o adresă în baza de date.

6.1.4.2 Formalisme matematice Formule matematice pot fi folosite pentru a clarifica o cerinţă. Toate simbolurile

folosite într-o expresie trebuie definite sau referite.

6.1.4.3 Engleza structurată Engleza structurată este un limbaj de specificare care foloseşte un vocabular şi o sintaxă

foarte limitate. Vocabularul constă doar din:

• verbe imperative ale limbii engleze; • termeni definiţi într-un glosar; • cuvinte rezervate.

Sintaxa e limitată la următoarele posibilităţi: • simple construcţii declarative; • construcţii decizionale; • construcţii repetitive.

Engleza structurată este folosită de obicei pentru a descrie procesele de bază ale sistemului.

Exemple: • construcţii declarative

GET RAW DATA REMOVE INSTRUMENT EFFECTS CALIBRATE CORRECTED DATA

• construcţii decizionale IF SAMPLE IS OF NOMINAL QUALITY THEN

CALIBRATE SAMPLE ELSE

STORE BAD SAMPLE • construcţii repetitive

FOR EACH SAMPLE

Page 66: Ingineria Programarii

66/135

GET POINTING DIRECTION AT TIME OF SAMPLE STORE POINTING DIRECTION WITH SAMPLE

Formalizând engleza structurată se poate automatiza prelucrarea cerinţelor (verificarea automată, o analiză automată, transformări şi afişări) şi se poate simplifica definirea testelor pentru faza de verificare şi validare a sistemelor.

6.1.4.4 Tabele Tabelele reprezintă o metodă de descriere concisă şi completă a cerinţelor. Această

metodă poate rezuma anumite relaţii, interacţiuni între cerinţe mai eficient decât o prezentare textuală.

6.1.4.5 Diagrame bloc ale sistemului Diagramele bloc reprezintă modul tradiţional de prezentare a proceselor dorite. Ele pot,

de asemenea, demonstra contextul în care sistemul va opera atunci când este o parte dintr-un sistem mai mare.

6.1.5 Documentul cerinţelor utilizatorului (DCU) Acesta este documentul obligatoriu, produs în faza definirii cerinţelor şi trebuie să fie

finalizat înainte de proiectarea sistemului. DCU trebuie: • să furnizeze o descriere generală ceea ce utilizatorul doreşte să execute sistemul; • să conţină toate cerinţele cunoscute, ale utilizatorilor; • să descrie toate operaţiile pe care le va executa sistemul; • să descrie toate constrângerile impuse sistemului; • să definească toate interfeţele externe sistemului sau să conţină referinţe despre

ele în alte documente. Costul modificării cerinţelor creşte cu atât mai mult cu cât proiectul înaintează în fazele

următoare. In faza de testare a sistemului, verificarea se va face pe baza DCU. Standardele ingineriei programării recomandă următoarele caracteristici ale stilului de prezentare a unui DCU:

• DCU trebuie scris folosind un limbaj, vocabular şi stil uşor de înţeles de către toţi utilizatorii. DCU trebuie să fie clar, consistent, modificabil;

• Un DCU este clar dacă orice cerinţă este neambiguă (are o singură interpretare) şi este înţeleasă de toţi participanţii la proiect;

• O cerinţă trebuie scrisă într-o singură propoziţie iar justificările trebuie separate de aceasta. Se recomandă ca cerinţele corelate între ele să fie grupate. Structurarea cerinţelor în document este foarte importantă;

• Un DCU este consistent dacă nu există conflicte între cerinţe. Folosirea mai multor termeni pentru a specifica acelaşi lucru este un exemplu de inconsistenţă;

• Un DCU este modificabil dacă orice modificare a cerinţelor poate fi documentată uşor, complet şi consistent.

6.1.5.1 Evoluţia DCU Modificările inevitabile ale DCU sunt responsabilitatea utilizatorului. E necesară

păstrarea unei istorii a modificărilor efectuate. Problema actualizării întregii documentaţii va fi rezolvată când va fi stabilită o arhitectură electronică standard pentru documente. Dacă schimbările cerinţelor sunt rezolvate direct în faza de implementare, fără a mai fi prinse în documentaţie, aceasta poate provoca serioase probleme în faza de întreţinere a sistemului. Iniţiatorul proiectului ar trebui să monitorizeze tendinţa în apariţia unor noi cerinţe ale utilizatorului. O tendinţă crescătoare va periclita succesul proiectului.

Page 67: Ingineria Programarii

67/135

6.1.5.2 Responsabilităţi Se pun în evidenţă următoarele recomandări:

• definirea clară tuturor responsabilităţilor înainte de începerea DCU; • utilizatorii reali ai sistemului sunt responsabili pentru determinarea cerinţelor de

capabilitate; • inginerii software trebuie să ia parte la formarea DCU pentru a-i ajuta pe

utilizatori; • indiferent de organizare, utilizatorii nu trebuie să dicteze soluţii iar echipa de

dezvoltare nu trebuie să dicteze cerinţe.

6.1.5.3 Conţinutul DCU Structura generală a unui DCU, recomandată de standardul ingineriei programării, este

prezentată în continuare: 1. Introducere 2. Descriere generală 3. Cerinţe specifice

Secţiunea l trebuie să descrie pe scurt scopul sistemului software, listele de definiţii pentru termeni utilizaţi în document, lista de referinţe bibliografice identificate prin autor, titlu şi date, şi o trecere în revistă a restului documentului.

Secţiunea 2 se referă la: • capabilităţile generale ale sistemului şi de ce sunt ele necesare; • constrângerile generale şi motivul pentru care au fost impuse; • caracteristicile generale ale utilizatorilor sistemului (nivelul educaţiei lor,

limbajul, experienţa lor tehnică pot impune importante constrângeri asupra software-ului) din fazele de operare şi întreţinere ale sistemului. Este importantă clasificarea acestor utilizatori şi estimarea numărului lor, în fiecare categorie;

• mediul extern în care sistemul va opera, prin diagrame de context pentru a evidenţia interfeţele externe şi prin diagrame bloc pentru a evidenţia activităţile din întregul sistem;

• situaţiile de risc evidenţiate în urma analizei riscurilor. Secţiunea 3 este partea principală a DCU, prezentând toate cerinţele de capabilitate şi

cerinţele restrictive ale sistemului. Validitatea sistemului software se va face pe baza acestor cerinţe. Se recomandă următoarele caracteristici:

• Fiecare cerinţă trebuie unic identificată; • Fiecare cerinţă trebuie marcată funcţie de importanţa sa (unele pot fi extrem de

importante, altele inacceptabile, altele suspendate până la obţinerea unor resurse necesare, altele sunt prioritare, instabile);

• Fiecare cerinţă trebuie să fie verificabilă. O afirmaţie trebuie să conţină o singură cerinţă. O cerinţă e verificabilă dacă există o metodă ce demonstrează obiectiv că ea este asigurată de sistem. (Afirmaţii ca: „interfaţa utilizator va fi prietenoasă", „sistemul va merge bine", nu sunt verificabile deoarece termenii „bine", „prietenos" nu au interpretări obiective). Dacă nu există o metodă pentru verificarea acestei cerinţe, aceasta este invalidă.

6.2 Analiza orientată obiect. Metode de analiză orientată obiect Acesta este numele unei clase de metode de analiză a unei probleme prin studiul

obiectelor domeniului problemei respective. Pe măsură ce industria de software continuă să se confrunte cu probleme ca slaba productivitate şi calitate a produselor software, companiile de software din întreaga lume caută mereu noi soluţii, sub formă de utilitare, metode, tehnici.

Page 68: Ingineria Programarii

68/135

Tehnicile structurate au fost considerate soluţia salvatoare a anilor 1970-1980. Apoi s-a sugerat că orientarea obiect ar fi o astfel de soluţie. Fiecare nouă paradigmă a fost adoptată cu un oarecare fanatism. Totuşi, în 1988, într-un eseu intitulat No Silver Bullet, Brooks argumenta că nu există nici o soluţie magică pentru dificilele probleme fundamentale ale dezvoltării sistemelor software. Nu există nici un panaceu, nici un miracol care să crească productivitatea şi în acelaşi timp să elimine toate erorile sistemului. Deci, nu e cazul să ne facem iluzii că tehnicile OO ar putea fi cheia tuturor problemelor. Nu există nici o garanţie că ea ar putea preveni dezastrul unui sistem software.

Un analist fără experienţă, intervievând comunitatea utilizatorilor, ar putea să nu descopere obiectele relevante din sistem. Un utilizator recalcitrant sau necooperant ar putea să nu furnizeze anumite informaţii, servicii, atribute ale sistemului. Şi, bineînţeles, orice proiect poate suferi, indiferent de utilitarele şi metodele folosite, din cauza modului de coordonare a proiectului, sau a incompetenţei personalului.

Obiectul reprezintă o încapsulare a valorilor unor atribute şi a serviciilor lor exclusive. O clasă descrie un set de obiecte cu atribute şi servicii comune. Un obiect este o instanţă a unei clase si crearea unui obiect se numeşte instanţiere. Clasa poate fi descompusă în subclase. Subclasele au în comun atributele caracteristice unei familii de clase, moştenind operaţiile şi atributele claselor-părinţi.

Iniţiatorii acestei metode argumentează că modul de a privi un sistem din perspectiva obiectelor este mult mai natural decât analiza sistemelor din punct de vedere funcţional. Specificaţiile bazate pe obiecte sunt mai adaptabile decât cele bazate pe funcţii.

Cele mai importante şi recunoscute metode AOO sunt: • Coad-Yourdon; • Rumbaugh-Object Modelling Technique (OMT); • Shlaer-Mellor; • Booch (cărţile lui Booch privind metodele OO au fost prezentate de Stroustrup,

inventatorul limbajului C++, ca fiind singurele care merită să fie studiate în acest domeniu datorită practicilor aprofundate de analiză şi proiectare expuse în lucrările sale. Din păcate însă, notaţiile utilizate de Booch sunt complicate şi există prea puţine utilitare care să suporte această metodă.)

în momentul de faţă, analiza orientată obiect evoluează iar analiştii, de obicei, combină tehnicile diferitelor metode în faza de analiză a problemei.

Ecuaţia utilizată pentru a recunoaşte o metodă OO este: OO = clase şi obiecte + moştenire + comunicare prin mesaje In acest fel se poate spune dacă un limbaj sau un mediu este sau nu OO. Analiza

orientată obiect nu se utilizează pentru sisteme care au foarte puţine funcţionalităţi sau pentru sisteme cu 1-2 clase şi obiecte.

6.2.1 Principiile analizei orientate obiect Principiile utilizate de AOO se bazează în primul rând pe trăsăturile esenţiale ale

programării orientate obiect: • Abstractizare (procedurală şi de date); • Moştenire. Pe lângă acestea, alte principii importante sunt: • Comunicarea prin mesaj e; • Utilizarea metodelor de organizare: • obiecte şi atribute; • obiecte şi părţile sale; o clase şi membrii.

Page 69: Ingineria Programarii

69/135

6.2.2 Abstractizarea Principiul abstractizării presupune ignorarea acelor aspecte ale unui subiect care nu sunt

relevante pentru obiectivul curent. Aceasta este o tehnică importantă pentru a coordona complexitatea. Aşadar, abstractizarea este un mecanism care permite reprezentarea unei situaţii complexe a lumii reale printr-un model simplificat. De exemplu, abstractizarea unei culori din lumea reală poate fi realizată prin modelul RGB (red, green, blue).

Abstractizarea procedurală este principiul conform căruia orice operaţie poate fi tratată ca o singură entitate în ciuda faptului că ea presupune realizarea mai multor operaţii de pe nivelul următor de detaliu. Această formă de abstractizare joacă un rol foarte important în definirea serviciilor sistemului.

Abstractizarea datelor reprezintă un mecanism mai puternic de abstractizare conform căruia tipurile de date sunt definite în termenii operaţiilor ce se aplică obiectelor de acel tip, cu restricţia că valorile acelor obiecte pot fi observate şi modificate doar prin intermediul operaţiilor menţionate.

Prin aplicarea acestui principiu, un analist defineşte atributele şi serviciile care manipulează aceste atribute. Singurul mod de a accesa un atribut este prin intermediul serviciilor. Atributele şi serviciile pot fi văzute ca formând un întreg.

Abstracţiile sunt încapsulate în obiecte. Stările şi comportamentele comune ale obiectelor sunt încorporate în clase. Implementarea internă propriu-zisă este ascunsă de restul sistemului. De exemplu, dacă o culoare este văzută ca o valoare RGB, reprezentarea internă poate folosi modelul HSV (hue, saturation, value) dacă acesta este mai potrivit, fără ca acest fapt să afecteze restul sistemului.

6.2.3 Moştenirea Moştenirea reprezintă mecanismul prin care se exprimă similarităţile dintre clase,

simplificând definirea claselor prin utilizarea unor clase anterior definite. Acest principiu pune în evidenţă generalizarea şi specializarea, făcând explicite atributele şi serviciile comune, printr-o ierarhie de clase. Principiul moştenirii permite analistului să specifice atributele şi serviciile comune doar o singură dată, sau să specializeze şi să extindă anumite atribute şi servicii.

Polimorfismul adaugă o nouă dimensiune la separarea dintre interfaţă şi implementare, dintre ce şi cum. El permite crearea de programe flexibile şi extensibile, în care noi trăsături pot fi adăugate cu uşurinţă nu numai în momentul creării proiectului, dar şi în etapele de dezvoltare ulterioare. Polimorfismul rămâne totuşi un detaliu de implementare, care poate fi ignorat în faza de analiză.

6.2.4 Comunicarea prin mesaje Se referă la interacţiunea prin mesaje între obiecte, interacţiune care corepunde modului

imperativ al limbajelor (comandă, cerere). 2.5. Metodele de organizare Strategia analizei poate fi construită pe baza a trei metode de organizare:

• iferenţierea între obiecte şi atributele lor (de ex.: a distinge între un copac şi înălţimea sa);

• diferenţierea dintre obiectul ca întreg şi părţile sale componente (de exemplu: a distinge între un copac şi ramurile sale);

• diferenţierea diverselor clase de obiecte (de exemplu: clasa pietrelor şi clasa arborilor).

6.3 Metoda de analiză Coad-Yourdon Coad şi Yourdon descriu o metodă AOO bazată pe cinci activităţi majore:

Page 70: Ingineria Programarii

70/135

• identificarea claselor şi obiectelor; • identificarea structurilor; • identificarea atributelor; • identificarea serviciilor; • identificarea subiectelor.

Puterea metodei constă în descrierea scurtă, concisă şi folosirea unor texte generale ca surse de definiţii. Un mare avantaj este menţinerea aceleiaşi notaţii atât în faza de analiză cât şi în cea de proiectare.

6.3.1 Activitatea I: Identificarea claselor şi obiectelor Există mai multe motive principale pentru care trebuie identificate clasele şi obiectele:

• găsirea unei reprezentări tehnice a sistemului mai aproape de modul în care concepem lumea înconjurătoare;

• se creează astfel o bază stabilă pentru analiză şi specificaţii: clasele şi obiectele pentru un sistem de control al traficului aerian pot fi aceleaşi şi peste cinci ani, doar serviciile şi atributele se pot modifica radical. Clasele şi obiectele sunt relativ stabile de-a lungul timpului, urmând ca, la apariţia modificărilor ulterioare, ele să constituie o bază pentru reutilizare;

• evitarea schimbării reprezentării de bază în momentul trecerii de la faza de analiză la cea de proiectare. Această problemă se rezolvă prin utilizarea unei reprezentări orientate obiect atât în faza de analiză cât şi în fazele de proiectare şi implementare.

Notaţii: O clasă de bază, fără obiecte instanţiate, va fi reprezentată astfel:

Figura 6-1 Clasă fără instanţe

O clasă cu obiecte instanţiate va fi reprezentată astfel:

Figura 6-2 Clasă cu instanţe

Numele clasei: • este o construcţie substantivală (engl. „noun phrase", un singur substantiv sau

substantiv şi adjectiv); • se alege ţinând cont de vocabularul standard al domeniului problemei. Folosirea

unei terminologii nefamiliare clientului îl face să se simtă frustrat şi stânjenit; • se referă doar la un singur obiect al clasei; • trebuie să fie sugestiv;

Page 71: Ingineria Programarii

71/135

• se recomandă să se scrie folosind litere mari şi mici pentru a face mai uşoară citirea sa.

Numărul de clase în modelul AOO depinde de complexitatea domeniului problemei: 35 este media; 110 clase deja sunt prea multe, în acest caz, se recomandă împărţirea problemei în subdomenii. De exemplu, dacă avem nevoie de 100 de clase, domeniul poate fi împărţit în 3-4 subdomenii, fiecare cu 25-35 de clase.

Odată identificate clasele „candidate", ele trebuie examinate pentru a se decide dacă vor fi în final incluse în modelul orientat obiect. Există o serie de criterii pentru această decizie:

• Sunt toate aspectele identificate relevante pentru sistem? Poate fi descris orice obiect al unei clase? Care sunt atributele lui potenţiale? De exemplu, atributele potenţiale pentru un funcţionar sunt: nume, parolă, autorizaţie. Altele, precum înălţimea, semne particulare, greutate, nu sunt relevante pentru sistem;

• Este absolut necesar ca un obiect să execute anumite activităţi? • Este caracterizată o clasă prin mai multe atribute? O clasă cu un singur atribut

este considerată suspectă; • Este caracterizată o clasă prin mai multe obiecte? O clasă cu un singur obiect

este considerată suspectă; • Se poate identifica un set de atribute aplicabile întotdeauna tuturor obiectelor

clasei? Toate clasele identificate trebuie să se concentreze asupra informaţiilor şi serviciilor

cerute de sistem. Nu se fac referiri la detalii de proiectare, deşi e bine ca acestea să fie reţinute, notate, încă din această fază. E posibil ca echipa de proiectare să adauge noi clase şi obiecte care să realizeze procesarea cerută de proiectare. Se recomandă realizarea analizei şi specificaţiilor neţinând cont de o arhitectură specifică software sau hardware a sistemului, chiar dacă clientul garantează că ea nu se schimbă niciodată.

Trebuie evitată reţinerea unor informaţii care rezultă din altele. De exemplu, nu are rost să se reţină vârsta persoanei dacă sistemul deja a înregistrat data naşterii. Trebuie luate în considerare acele atribute şi servicii din care apoi se pot obţine rezultate derivate.

6.3.2 Activitatea a Il-a: Identificarea structurilor Această activitate se referă la evidenţierea structurilor de tip generalizare-specializare

(„gen-spec") şi întreg-părţi: • Structura gen-spec poate fi văzută ca aspect de diferenţiere între clase,

asemănător definiţiilor utilizate de gândirea umană pentru organizarea cunoştinţelor, cu gen proxim şi diferenţe specifice. De exemplu: generalizarea limbaj de programare şi specializarea C+ + . Mai puţin formal, corectitudinea acestei structuri poate fi testată prin construcţii de forma „este un (o)" sau „este un fel de". Pentru exemplul de mai sus, verificarea va fi: „C++ este un limbaj de programare"'. în cadrul structurii gen-spec se aplică principiul moştenirii;

• Structura întreg-părţi evidenţiază componenţa unui obiect. De exemplu: întregul limbaj de programare şi partea sintaxă. Corectitudinea acestei structuri poate fi testată prin construcţii de forma „are un (o)": un limbaj de programare are o sintaxă, în cadrul structurii întreg-părţi se aplică mecanismul agregării. Din punct de vedere al implementării, corespondentul este compunerea claselor.

6.3.2.1 Structura gen-spec Notaţia pentru structura gen-spec este următoarea:

Page 72: Ingineria Programarii

72/135

Figura 6-3 Structura gen-spec

În vârf se află o clasă de generalizare, iar pe nivelele următoare clase de specializare (liniile unesc clasele şi nu obiectele). Punctul de ramificare a liniilor spre specializări se reprezintă printr-un semicerc. Fiecare clasă-specializare este denumită astfel încât să aibă un înţeles de sine stătător. Numele cel mai potrivit este format din numele clasei generalizatoare urmat de numele naturii specializării. De exemplu, pentru generalizarea Senzor, sunt preferate specializările SenzorCritic şi SenzorStandard faţă de numele Critic şi Standard.

în clasele specializate se notează numai atributele şi serviciile caracteristice. Nu este necesară menţionarea atributelor şi serviciilor moştenite din clasa de bază. Dacă sunt posibile mai multe specializări, este utilă considerarea mai întâi a celei mai simple şi celei mai complicate specializări, şi apoi găsirea unei variante intermediare. Exemplu:

Considerăm clasa Avion ca o generalizare. Ea poate fi specializată în mai multe moduri: • AvioaneCuReacţie şi AvioaneCuElice; • AvioaneCivile şi AvioaneMilitare; • AvioaneCuAripiFixe şi AvioaneCuAripiMobile; • AvioaneComerciale şi AvioaneP articular e.

Să considerăm prima specializare (AvioaneCuReacţie şi AvioaneCuElice). Are sens această specializare sens pentru domeniul problemei? Dacă nu, atunci nu este necesară. Are sistemul nevoie să recunoască diferenţa dintre avioanele cu reacţie şi cele fără reacţie? Sunt responsabilităţile sistemului diferite pentru fiecare din aceste două specializări? Dacă nu, atunci acestea nu sunt necesare.

Există într-adevăr moştenire, adică anumite atribute şi servicii ale clasei Avion se aplică tuturor avioanelor şi apoi se specializează în atribute şi servicii care se aplică doar celor cu reacţie şi cu elice? Dacă nu, specializarea nu are rost.

Dacă singura diferenţă între AvionCuReacţie şi AvioaneCuElice este tipul avionului, atunci se foloseşte doar clasa Avion cu un atribut pentru tipul avionului, putând lua diverse valori. Nu este necesară o structură gen-spec în acest caz.

Unul din criteriile construirii unei structuri gen-spec este reflectarea unei generalizări-specializări în domeniul problemei. Aceste structuri nu trebuie construite doar pentru a extrage atribute comune. Un exemplu eronat de structură gen-spec este următorul:

Page 73: Ingineria Programarii

73/135

Localizare

Avion 1 Aeroport

Figura 6-4 Structură gen-spec eronată

6.3.2.2 Structura întreg-parte Notaţia pentru structura întreg-parte este următoarea:

Intreg

Parte 1 Parte 2

c,d

a, b e ,f

g ,h

Figura 6-5Structură întreg-parte

In partea superioară este reprezentat obiectul întreg al clasei, iar în partea inferioară părţi ale acestui obiect. Triunghiul face distincţia dintre întreg şi părţi. Fiecare capăt al liniei este marcat cu un interval, indicând numărul de părţi pe care un întreg le poate avea şi numărul de întregi corespunzători unei părţi, la orice moment de timp .

Exemple:

Page 74: Ingineria Programarii

74/135

Motor

0,1

Avion

0,4

Figura 6-6 Structură întreg-parte:

Avion-Motor în acest exemplu, un Avion este un ansamblu: • de O motoare (planor) • de cel mult 4 motoare

iar un Motor este o parte din: • nu neapărat dintr-un avion • cel mult un avion

Functionar

1

Organizatie

0,m

Figura 6-7 Structură întreg-parte: Organizaţie-Funcţionar

Aici, o organizaţie este o colecţie: • posibil, fără funcţionari • de cel mult m funcţionari

Page 75: Ingineria Programarii

75/135

iar un funcţionar este membru al unei singure organizaţii.

6.3.2.2.1 Strategii de identificare a structurilor întreg-parte Pentru identificarea potenţialelor structuri întreg-parte, se consideră aspectele:

• ansamblu-părţi • container-conţinut • colecţie-membri

În plus, se recomandă studiul rezultatelor AOO precedente, pentru evidenţierea eventualelor structuri de acest tip, care ar putea fi refolosite. Exemple:

Motor

0,1

Avion

0,4

Figura 6-8 Structură întreg-parte: ansamblu-părţi

Pilot

0,n

Avion

0,m

Figura 6-9 Structură întreg-parte: container-conţinut

Page 76: Ingineria Programarii

76/135

In acest exemplu, avionul este considerat un container, pilotul găsindu-se înăuntru. Totuşi, pilotul nu aparţine avionului, nu este o parte a sa, precum motorul. Dacă domeniul problemei şi responsabilităţile sistemului trebuie să asigneze un pilot unui avion, atunci o clasă Pilot este necesară. Exemplu:

Functionar

1

Organizatie

0,m

Figura 6-10Structură întreg-parte: colecţie-membri

O organizaţie poate fi considerată o colecţie de funcţionari.

6.3.2.2.2 Verificarea structurilor întreg-parte Pentru fiecare obiect considerat întreg se verifică, pentru fiecare din părţile sale, dacă:

• acea parte aparţine domeniului problemei şi interesează sistemul din punct de vedere al serviciilor. De exemplu, dacă domeniul problemei se referă la Serviciul meselor, atunci partea Motor pentru întregul Avion nu are sens. Dacă însă domeniul problemei este Transportul aerian, atunci ea are sens;

• acea parte capturează mai mult decât o singură valoare de stare. Dacă responsabilităţile sistemului includ doar cunoştinţe despre starea avionului (funcţional sau nefuncţional) sau starea motorului, atunci nu e necesară o clasă Motor. Este suficient să se prevadă un atribut Stare în clasa Avion. Dacă însă sistemul trebuie să ştie mai mult despre motor (model, număr de serie, data fabricaţiei etc.), atunci este necesară o clasă Motor.

Considerând apoi fiecare obiect ca o parte potenţială dintr-un întreg, se verifică utilitatea prezenţei lui în acelaşi mod.

6.3.3 Activitatea a IlI-a: Identificarea atributelor Un atribut este o proprietate, calitate sau caracteristică pentru care fiecare obiect al unei

clase are o anumită valoare. Atributele adaugă detalii abstractizărilor de tip clasă sau structură. Ele descriu valori încapsulate în obiect şi vor fi manipulate exclusiv de către serviciile acelui obiect. Atributele şi serviciile obiectului sunt tratate ca un întreg. Dacă o altă parte a sistemului doreşte să acceseze atributele unui obiect, o poate face doar prin specificarea unui mesaj de conectare, corespunzător serviciului definit de obiect. De-a lungul

Page 77: Ingineria Programarii

77/135

timpului, domeniul claselor rămâne relativ stabil, în schimb, atributele sunt mult mai susceptibile de a se modifica. Se recomandă definirea atributelor pe baza următoarei strategii:

• identificarea atributelor; • poziţionarea atributelor; • identificarea conexiunii instanţelor; • verificarea cazurilor speciale; • specificarea atributelor.

6.3.3.1 Identificarea atributelor Pentru fiecare clasă, urmărim:

• cum sunt descrise obiectele în general; • cum sunt descrise obiectele în domeniul problemei; • cum sunt descrise obiectele în contextul responsabilităţilor sistemului; • ce anume trebuie memorat în timp; • care sunt stările în care se poate afla obiectul; • studiul rezultatelor anterioare ale AOO în probleme similare pentru reutilizarea

atributelor. Fiecare atribut trebuie să captureze un concept atomic (o singură valoare sau un grup de

valori, având acelaşi înţeles, ca de exemplu nume, format din nume şi prenume, sau adresă, compusă din stradă, număr, cod poştal, oraş, ţară).

6.3.3.2 Poziţionarea atributelor Fiecare atribut va fi pus în clasa pe care o descrie mai bine. De exemplu, în sistemul de

înregistrare a unui vehicul, culoarea acestuia este foarte importantă. Ea este reţinută de sistem la momentul înregistrării vehiculului. Este deci culoare un atribut al clasei Evenimentînregistrare sau al clasei Vehiculă (Răspunsul corect este: Vehicul.)

Pentru clasele care prezintă o structură gen-spec, atributul se poziţionează în cel mai de sus punct al structurii în care rămâne aplicabil pentru toate specializările sale. Dacă un atribut se aplică pe un nivel întreg de specializări, atunci este mutat pe nivelul generalizator corespunzător.

6.3.3.3 Identificarea conexiunilor instanţelor O conexiune a instanţelor e un model al mapărilor între domeniile problemelor

obiectelor. Este în strânsă legătură cu structurile întreg-parte. Notaţie

10,m

Figura 6-11 Conexiunea instanţelor

Conexiunea instanţelor e reprezentată printr-o linie care uneşte obiectele. Fiecare obiect are o cantitate (m) sau interval (m,n), marcate pe conexiune, reflectând restricţiile conexiunilor cu alte obiecte. Limitele intervalului au următoarele semnificaţii:

• Limita inferioară

Page 78: Ingineria Programarii

78/135

o O, dacă este o conexiune opţională; o > l, dacă e o conexiune obligatorie; • Limita superioară

o l, dacă este o singură conexiune; o > l, dacă sunt conexiuni multiple.

6.3.3.4 Verificarea cazurilor speciale Pentru fiecare atribut se verifică dacă există cazuri când nu este aplicabil. De exemplu,

clasa Vehicul poate avea atributul Tracţiune cu valorile: benzină, motorină, electric etc. Dar pentru anumite vehicule, care nu au motor, acest atribut poate avea valoarea „Inaplicabil". Dacă există un astfel de caz, trebuie reanalizată strategia gen-spec, verificând dacă nu mai sunt necesare structuri gen-spec care n-au fost cuprinse în model.

în continuare, se verifică clasele care au un singur atribut, în acest caz, fie clasa are un singur atribut pentru că aşa impun responsabilităţile sistemului (figura 12), fie atributul nu este bine poziţionat, el aparţinând unei alte clase (figura 13). Verificând domeniul problemei, se constată că în loc de două clase (Garanţie şi Adresă) se poate folosi doar una singură (Garanţie).

FunctionarNume

Figura 6-12 Plasarea unui singur atribut într-o clasă pe care o descrie

GarantieNume

Cantitate

AdresaAdresa

GarantieNume

CantitateAdresa

Figura 6-13 Plasarea greşită a unui atribut

6.3.3.5 Specificarea atributelor Pentru specificarea atributelor, există următoarele recomandări:

• Numele atributelor trebuie să fie sugestive, din domeniul problemei; • Specificarea unui atribut trebuie însoţită de o sumară descriere a sa; • Specificarea unui atribut trebuie însoţită de eventuale restricţii:

o unitate de măsură, intervale; o precizie; o valoare implicită;

Page 79: Ingineria Programarii

79/135

o în ce condiţii sunt permise serviciile de creare şi acces; o în ce măsură este afectat de valorile altor atribute;

Exemplu de specificare a atributelor: Clasa Senzor Atribut Model: numărul modelului Atribut Secvenţalnit: secvenţa de iniţializare Atribut Conversie: constă din factori de scalare şi unităţi de măsură Atribut Interval: intervalul de valori pentru senzor Atribut Adresă: adresa acestui senzor Atribut Prag: valoare de prag la care se semnalează alarma Atribut Stare: starea senzorului (on, off, standby) Atribut Valoare: cea mai recentă valoare a senzorului Unele atribute îşi schimbă foarte rar valoarea (Model, Secvenţelnit, Conversie), altele

şi-o schimbă mai des (Interval, Adresă, Prag, Stare), iar altele sunt dinamice (Valoare). Atributul Valoare este rezultatul citirii unei valori în unităţi fizice (de exemplu volţi) şi al conversiei sale în unităţi standard de măsură. Poate fi tratată ca o valoare recalculabilă şi în acest caz nu e nevoie de un atribut Valoare. Dar e posibil ca sistemul să necesite cunoaşterea acestei valori în orice moment, indiferent de starea senzorului (chiar dacă senzorul este în starea standby, în care valoarea nu poate fi citită şi deci calculată). Este posibil deci, ca atributul Valoare să nu poată fi recalculat de fiecare dată şi atunci se justifică prezenţa sa.

6.3.4 Activitatea a IV-a: Identificarea serviciilor în analiza orientată obiect se recomandă ca identificarea serviciilor să se realizeze după

identificarea claselor, structurilor şi atributelor. Serviciul este definit ca fiind o operaţie specifică unui obiect. Pe lângă evidenţierea acestor servicii, se mai pune problema definirii comunicaţiilor necesare între obiecte. Strategia definirii serviciilor constă în următoarele etape. Mai întâi se identifică stările posibile ale obiectelor, date de valorile atributelor lor. Pentru a identifica starea unui obiect:

• se examinează valorile potenţiale ale atributelor; • se determină dacă sistemul are comportări diferite pentru aceste valori; • se examinează rezultatele AOO anterioare.

Exemplu: care din atributele sistemului Senzor reflectă o schimbare în starea obiectului? Atributele sistemului sunt cele prezentate mai sus. Pentru acest sistem valorile atributelor Model, Secvenţalnit, Conversie, Interval, Adresă, Prag nu implică o modificare a comportării sistemului, însă atributul Stare reflectă o schimbare în comportarea sistemului. Valorile sale sunt: on, off şi standby. Diagramele stărilor obiectelor prezintă diferitele stări ale sistemului în timp.

Notaţia pentru diagramele stărilor obiectelor:

Figura 6-14 Diagrama stărilor obiectelor

Exemplu:

Page 80: Ingineria Programarii

80/135

Stare = off

Stare = standby

Stare = on Figura 6-15 Diagrama stărilor obiectului pentru sistemul Senzor

în acest exemplu, săgeata din vârf indică starea iniţială. Sunt prezentate doar stările şi tranziţiile legale.

Apoi se trece la identificarea serviciilor propriu-zise. Există două tipuri de servicii: • servicii algoritmic-simple; • servicii algoritmic-complexe.

Serviciile algoritmic-simple se aplică fiecărei clase, sunt implicite şi nu sunt reprezentate pe stratul serviciilor. Exemple tipice de astfel de servicii sunt: crearea obiectelor (constructorii), accesul la obiecte (operaţii get/set) şi distrugerea obiectelor (destructorul). Majoritatea operaţiunilor unui sistem (80% - 95%) se concentrează în serviciile algoritmic-simple.

Serviciile algoritmic-complexe sunt şi ele de două tipuri: de calcul (calculează un rezultat folosind valorile unor atribute) şi de monitorizare (supraveghează un sistem sau dispozitiv extern, tratează intrările şi ieşirile acestuia, achiziţionează şi controlează date).

Tot în cadrul identificării serviciilor trebuie precizate şi conexiunile prin mesaje între obiecte, adică modurile în care un obiect transmiţător trimite un mesaj către un obiect receptor pentru ca acesta din urmă să execute o anumită prelucrare. Prelucrarea este denumită în specificarea serviciilor transmiţătorului şi definită m specificarea serviciilor receptorului.

Notaţie:

Transmitator Receptor

Figura 6-16 Conexiune prin mesaje

Receptorul primeşte mesajul, execută operaţiunea corespunzătoare şi returnează un rezultat transmiţătorului.

Pentru conexiuni prin mesaje de la un obiect spre mai multe obiecte, există următoarea notaţie:

Transmitator

Figura 6-17 Conexiune prin mesaje spre mai multe obiecte

Interacţiunile dintre factorul uman şi sistem sunt şi ele reprezentate în modelul AOO:

Page 81: Ingineria Programarii

81/135

Figura 6-18 Interacţiune cu factorul uman

Pentru a evidenţia conexiunile prin mesaje între obiecte, se pun următoarele întrebări pentru fiecare obiect:

• de serviciile căror alte obiecte are nevoie? Se trasează câte o săgeată către fiecare din aceste obiecte;

• ce alte obiecte au nevoie de serviciile lui ? Se trasează câte o săgeată dinspre fiecare din aceste obiecte;

• se urmăreşte conexiunea prin mesaj spre obiectul destinatar şi se repetă aceleaşi întrebări;

• se examinează rezultatele AOO anterioare.

6.3.5 Activitatea a V-a: Identificarea subiectelor In AOO, termenul de subiect este un mecanism pentru ghidarea cititorului (analist,

manager, expert, utilizator, client) în înţelegerea unui model de analiză foarte mare şi complex. Subiectele oferă o imagine de perspectivă asupra unui model complex.

Fiecare clasă din vârful fiecărei structuri se asignează unui subiect. Fiecare clasă care nu face parte din nici o structură se asignează, de asemenea, unui subiect. Se recomandă studiul rezultatelor anterioare ale AOO pentru probleme similare pentru a se utiliza subiecte identificate deja.

Subiectele se rafinează apoi utilizând subdomeniile problemei. De fapt, se aplică principiul întreg-parte pe domeniul problemei. Rafinarea ia în consideraţie interdependenţele şi interacţiunile minimale între subiecte. Interdependenţele sunt exprimate de structuri atribute, iar interacţiunile sunt exprimate de servicii.

Un subiect se reprezintă printr-un dreptunghi, fiind etichetat în interior cu un nume şi un număr şi conţinând şi lista claselor care fac parte din acel subiect. Practic, pentru modele foarte complexe, subiectele se pot reprezenta în trei moduri:

• Subiecte ne-expandate: doar dreptunghiul cu numele şi numărul lor:

Figura 6-19 : Subiect ne-expandat

• Subiecte parţial expandate, conţinând lista claselor componente:

Page 82: Ingineria Programarii

82/135

1. Subiectul 1

Clasa 1Clasa 2

2. Subiectul 2

Clasa 3Clasa 4

Figura 6-20 Subiect parţial expandat

Subiecte total expandate, când se reprezintă împreună cu alte straturi ale modelului AOO, prin zone partiţionate şi numerotate. In interiorul dreptunghiurilor numerotate vor fi incluse clasele

cu notaţia completă: nume, atribute, servicii, eventual şi cu legăturile dintre ele:

Figura 6-21 Subiect total expandat

O clasă poate face parte din mai multe subiecte, când acest lucru este necesar pentru ghidarea cititorului. Subiectele pot conţine la rândul lor alte subiecte, furnizând astfel o hartă multinivel. în general, subiectele sunt necesare pentru modele relativ mari, având aproximativ 30-40 de clase. Identificarea subiectelor se va face abia după ce clasele au fost identificate şi bine înţelese.

6.4 Concluzii În acest curs au fost prezentate mai întâi chestiuni legate de ingineria cerinţelor:

definirea cerinţei, extragerea, identificarea şi specificarea cerinţelor. Apoi a fost descrisă problematica generală a analizei orientate obiect, insistându-se pe metoda de analiză Coad-Yourdon şi pe activităţile ei specifice: identificarea claselor şi obiectelor, a structurilor, atributelor, serviciilor şi subiectelor.

Page 83: Ingineria Programarii

83/135

7 Proiectarea orientată obiect

7.1 Procesul proiectării sistemelor software Proiectarea este un proces creativ, care necesită o oarecare experienţă practică,

acumulată în timp. Acest proces implică o serie de paşi: • studiul şi înţelegerea problemei; • identificarea mai multor soluţii posibile şi evaluarea fiecăreia din ele. Alegerea

ei depinde de experienţa proiectantului, simplitatea acesteia, valabilitatea componentelor reutilizabile;

• descrierea fiecărei abstractizări a fiecărei soluţii, înainte de crearea documentaţiei formale, ar putea fi necesar ca proiectantul să pregătească o descriere informativă a proiectului pentru a fi examinată în detaliu, în felul acesta, omisiunile şi erorile posibile ar putea fi eliminate înainte ca proiectul să fie documentat.

Activităţile esenţiale în cursul proiectării sunt următoarele: • Proiectarea arhitecturală: subsistemele întregului sistem sunt identificate şi

documentate; • Specificarea abstractă: pentru fiecare subsistem, se prezintă o specificare

abstractă a serviciilor şi a constrângerilor sub care acestea operează; • Proiectarea interfeţei: pentru fiecare subsistem, interfaţa cu celelalte

subsisteme este proiectată şi documentată; • Proiectarea componentelor: serviciile furnizate de un subsistem sunt

partiţionate între componentele acelui subsistem; • Proiectarea structurilor de date: structurile de date utilizate în implementarea

sistemului sunt proiectate în detaliu şi specificate. • Proiectarea algoritmilor: algoritmii utilizaţi pentru a furniza servicii sunt

proiectaţi în detaliu şi specificaţi. Acest proces se repetă pentru fiecare subsistem până când componentele identificate pot fi mapate direct în componentele limbajului de programare.

7.2 Caracteristicile unei proiectări corecte Rezultatul principal a fazei de proiectare este un model al codului care arată cum este

implementat sistemul şi o diagramă a dependenţelor dintre module, care arată cum va fi sistemul divizat în module şi cum interacţionează acestea. Pentru module mai dificile, se includ specificaţii speciale.

Ce înseamnă o proiectare corectă? Desigur, nu există o modalitate simplă şi obiectivă care să spună că o proiectare este mai bună decât alta. Dar există unele proprietăţi cheie care pot fi utilizate pentru a măsura calitatea proiectării, în mod ideal, o proiectare ar trebui să aibă toate aceste caracteristici. In practică, trebuie făcute compromisuri între ele. Aceste caracteristici sunt extensibilitatea, siguranţa şi eficienţa.

7.2.1 Extensibilitatea Proiectarea trebuie să poată suporta noi funcţii. Un sistem perfect din toate celelalte

puncte de vedere, dar în care nu poate fi integrată nici o schimbare sau îmbunătăţire, nu este de prea mare folos. Chiar dacă nu există cerinţe pentru trăsături suplimentare, probabil vor exista schimbări în domeniul problemei care vor necesita schimbări în program.

Suficienţa modelului: Modelul problemei trebuie să reflecte caracteristicile suficiente ale problemei. Un obstacol des întâlnit atunci când se doreşte extinderea unui sistem este

Page 84: Ingineria Programarii

84/135

faptul că anumite noţiuni nu sunt exprimate în cod şi deci funcţionalităţile corespunzătoare care trebuie adăugate nu-şi găsesc locul în program. Un exemplu în acest sens poate fi văzut în Microsoft Word. Acesta a fost proiectat plecând de la premisa că structura fundamentală de organizare a unui document este paragraful. De aceea, structurile ierarhice pot fi introduse cu greutate şi, ca efect, divizarea documentului în secţiuni nu se face foarte eficient. Optimizarea modelului nu trebuie să elimine substructurile care numai par nefolositoare. Abstractizările nu trebuie să înlocuiască structurile concrete decât după o analiză serioasă.

Localizarea: Chiar dacă programul cuprinde noţiuni suficiente pe baza cărora să poată fi adăugate noi funcţionalităţi, acestea trebuie introduse fără a modifica tot codul. De aceea, proiectarea trebuie să fie localizată, adică problemele diferite trebuie separate în regiuni distincte ale codului. Modulele trebuie decuplate cât mai mult, astfel încât o schimbare să nu antreneze modificări în cascadă.

7.2.2 Siguranţa Sistemul trebuie să aibă un comportament sigur, care nu presupune doar lipsa

„prăbuşirilor" sau a pierderii datelor, ci şi faptul că trebuie să ruleze corect, aşa cum se aşteaptă utilizatorul. Nu e suficient ca sistemul să îndeplinească o specificaţie obscură, ci trebuie să îndeplinească o specificaţie care poate fi înţeleasă uşor de utilizator, astfel încât acesta să poată prezice comportamentul sistemului. Pentru sistemele distribuite, este importantă disponibilitatea. Pentru sisteme de timp real, este importantă sincronizarea. Criteriile de stabilire a siguranţei variază în funcţie de tipul aplicaţiei. Conexiunile telefonice automate trebuie să fie în permanenţă disponibile şi să fie foarte sigure, uneori pot fi supraîncărcate, iar alte ori mai au loc rutări eronate de mesaje, întârzieri mici într-un program de trimitere a email-urilor nu prea contează, însă aceleaşi întârzieri se pot dovedi catastrofale pentru un controler de reactor nuclear.

Modelarea atentă. Siguranţa nu poate fi introdusă uşor într-un sistem existent. Cheia realizării de produse sigure este modelarea şi dezvoltarea atentă. Problemele serioase în sistemele critice în general nu apar din erori de programare, ci din erori de analiză a problemei. Programatorii pur şi simplu nu iau în calcul o anumită proprietate a mediului în care este plasat sistemul. De exemplu, incidentul din 1993 când un avion Airbus a încercat să aterizeze pe timp de furtună pe aeroportul din Varşovia, însă frânele nu au funcţionat, avionul a ieşit de pe pistă şi a luat foc.

Analiza şi testarea. Oricât de atent ar fi cineva, este foarte probabil că va face până la urmă greşeli. Din punct de vedere al costului, cea mai bună soluţie este verificarea codului de către altă persoană. O testare mai detaliată şi care poate descoperi erori mai subtile poate fi realizată cu un instrument automat.

7.2.3 Eficienţa Sistemul trebuie să consume resurse în limite rezonabile. Eficienţa depinde de context.

O aplicaţie care rulează pe un telefon celular nu are disponibilă la fel de multă memorie ca una care rulează pe un PC. Pentru determinarea eficienţei unui program, se analizează în general timpul de execuţie şi spaţiul necesar. Pe lângă timpul de execuţie, este la fel de important şi timpul necesar dezvoltării, care este legat de costul aplicaţiei. O proiectare care poate fi implementată mai economic poate fi preferabilă uneia care îndeplineşte toate metricile de calitate dar este mai scumpă.

Modelul obiectului. Alegerea modelului obiectelor din cod este crucială, deoarece este greu de schimbat. De aceea, ţintele de performanţă trebuie considerate încă de la început în faza de proiectare.

Evitarea tendenţiozităţii. Când se dezvoltă modelul obiectelor pentru problemă, trebuie excluse orice preocupări legate de implementare. Un model care conţine detalii de

Page 85: Ingineria Programarii

85/135

implementare este tendenţios (engl. „biased"), deoarece favorizează o anumită implementare. Rezultatul este excluderea unor posibilităţi de implementare care se pot dovedi în final mai eficiente.

Optimizarea, în general, „optimizare" este o denumire greşită, deoarece înseamnă creşterea performanţelor în detrimentul altor calităţi, cum ar fi claritatea structurii. Dacă optimizarea nu este tratată cu atenţie, putem risca să ajungem la un sistem mai prost din toate punctele de vedere. Sunt recomandate numai optimizările care vor avea efecte foarte puternice, de exemplu o modificare care reduce complexitatea de timp de la O(n) la timp constant. Optimizările unor aspecte minore trebuie evitate dacă presupun o scădere în claritatea proiectării.

7.3 Proiectarea orientată obiect După cum ştim, faza de proiectare se află situată între analiză şi implementare. Toate

aceste faze fac parte din procesul de dezvoltare orientată obiect. Aşa cum am văzut în cursul precedent, analiza orientată obiect se referă la dezvoltarea unui model orientat obiect al domeniului aplicaţiei. Obiectele identificate reflectă entităţi şi operaţii asociate cu problema care trebuie rezolvată. Proiectarea orientată obiect priveşte dezvoltarea unui model orientat obiect al sistemului software care trebuie să implementeze cerinţele identificate. Programarea orientată obiect urmăreşte implementarea (punerea în practică a) proiectării software folosind un anumit limbaj de programare orientat obiect.

Continuând faza de analiză orientată obiect, proiectarea orientată obiect este o strategie în care sistemul se gândeşte în termeni de „obiecte", în loc de operaţii şi funcţii. Programul nu este proiectat ca o mulţime de funcţii care schimbă date prin parametri şi memorie comună (variabile globale), ci ca o mulţime de obiecte care interacţionează. Obiectele îşi păstrează starea internă şi îşi definesc operaţiile pe baza acesteia. Prin ascunderea informaţiilor despre reprezentarea stării, ei limitează accesul exterior la starea internă. Aşadar, caracteristicile proiectării orientate obiect sunt următoarei e:

a) sistemul este proiectat ca o mulţime de obiecte care interacţionează, îşi gestionează propria stare internă şi oferă servicii altor obiecte;

b) obiectele sunt create prin instanţierea unei clase care defineşte atributele şi operaţiile asociate obiectului; mai multe obiecte ale aceleiaşi clase pot coexista în acelaşi program;

c) clasele sunt abstractizări ale lumii reale sau entităţi care încapsulează informaţii de stare şi care definesc un număr de servicii care creează, accesează sau modifică starea;

d) obiectele sunt entităţi independente în care reprezentarea stării poate fi modificată fără a afecta alte clase din sistem;

e) funcţionalitatea sistemului este dată de serviciile asociate fiecărui obiect; obiectele interacţionează prin apelarea serviciilor definite de alte obiecte;

f) nu există zone de memorie comună; obiectele comunică prin apeluri de servicii şi nu prin variabile partajate; nu există posibilitatea ca o componentă să fie afectată de schimbarea unei informaţii partajate şi deci modificările sunt mai uşor de implementat;

g) obiectele pot fi distribuite şi pot fi executate secvenţial sau paralel; deciziile asupra paralelismului nu trebuie luate încă de la începutul procesului de proiectare.

7.4 Etapele proiectării orientate obiect Procesul general al proiectării orientate obiect cuprinde mai multe etape:

Page 86: Ingineria Programarii

86/135

• înţelegerea şi definirea contextului în care va evolua sistemul şi a modului în care acesta va fi utilizat;

• proiectarea arhitecturii sistemului; • identificarea principalelor obiectelor din sistem; • dezvoltarea modelelor de proiectare; • specificarea interfeţelor obiectelor.

Aceste etape nu trebuie gândite într-o ordine strictă, ele se întrepătrund şi se influenţează reciproc. Pe măsură ce sunt realizate modelele obiect, activităţile de mai sus modifică treptat arhitectura sistemului. Proiectarea nu este un proces simplu şi bine structurat. In realitate, se propun şi se rafinează soluţii pe măsură ce se adună mai multe informaţii, se revine asupra unor decizii dacă acestea se dovedesc greşite; uneori se explorează în detaliu toate opţiunile, alteori detaliile sunt ignorate pentru a fi considerate mai târziu în cadrul procesului.

7.4.1 Contextul sistemului şi modelele de utilizare Prima etapă a procesului de proiectare software este înţelegerea relaţiilor dintre sistemul

proiectat şi mediul său extern. Aceasta va determina alegerea funcţionalităţilor necesare şi structurarea lor astfel încât sistemul să poată comunica eficient cu mediul. Contextul sistemului şi modelul de utilizare a sistemului reprezintă două modele complementare ale relaţiei dintre sistem şi mediu:

• contextul sistemului este un model static care descrie celelalte sisteme din mediu;

• modelul de utilizare a sistemului este un model dinamic care descrie interacţiunea dintre sistem şi mediu.

7.4.2 Proiectarea arhitecturii După ce au fost definite interacţiunile dintre sistem şi mediu, aceste informaţii pot fi

utilizate ca bază pentru proiectarea arhitecturii sistemului. Desigur, informaţiile din etapa precedentă trebuie combinate cu unele cunoştinţe generale despre principiile proiectării arhitecturii şi cu unele cunoştinţe detaliate despre domeniul problemei.

O regulă euristică spune că într-un model de arhitectură nu trebuie incluse mai mult de şapte entităţi fundamentale. Fiecare din aceste entităţi poate fi apoi descrisă separat, dar, bineînţeles, depinde de proiectant dacă vrea să prezinte în arhitectură structura tuturor entităţilor sistemului.

De exemplu, arhitectura unui agent de corectare lexicală (spell-checker) poate fi următoarea:

Figura 7-1 Exemplu de arhitectură: agent spell-checker

Page 87: Ingineria Programarii

87/135

7.4.3 Identificarea obiectelor In acest stadiu al proiectării, deja s-au conturat unele idei despre obiectele esenţiale

pentru sistem. Obiectele şi clasele identificate în faza de analiză sunt verificate şi rafinate pentru a se potrivi în programul care urmează a fi realizat.

O tehnică de identificare a obiectelor este utilizarea unei abordări comportamentale. Proiectantul încearcă să înţeleagă mai întâi comportamentul general al sistemului. Comportamentelor diferite le corespund părţi diferite din sistem. Mai trebuie văzut cine iniţiază şi participă în aceste comportamente. Participanţii cu rol semnificativ sunt recunoscuţi drept obiecte.

Altă tehnică se bazează pe analiza scenariilor. Diferitele scenarii ale utilizării sistemului sunt identificate şi analizate pe rând. Pentru fiecare scenariu se identifică obiectele necesare, împreună cu atributele şi serviciile acestora.

7.4.4 Modele de proiectare Modelele de proiectare arată clasele şi obiectele în cadrul sistemului, precum şi relaţiile

dintre acestea. Ele reprezintă puntea de legătură între cerinţele sistemului şi implementarea sistemului. De cele mai multe ori, există cerinţe conflictuale pentru modele. Ele trebuie să fie abstracte pentru ca detaliile nesemnificative să nu ascundă relaţiile cu cerinţele sistemului. Totuşi, ele trebuie de asemenea să includă suficiente detalii pentru ca programatorii să poată lua deciziile de implementare corespunzătoare.

O soluţie a acestei probleme este dezvoltarea de modele diferite la diferite nivele de detaliu. Când există legături strânse între inginerii de cerinţe, proiectanţi şi programatori, modelele abstracte sunt suficiente. Decizii specifice de proiectare pot fi făcute pe măsură ce sistemul este implementat. Când legăturile dintre echipele implicate sunt indirecte (de exemplu, când sistemul este proiectat de o parte a unei organizaţii şi implementat în altă parte), sunt necesare modele mai detaliate. Detalierea modelului depinde şi de tipul de sistem dezvoltat. Un sistem simplu de prelucrare secvenţială a datelor va fi proiectat într-un mod diferit faţă de un sistem integrat de timp real şi deci vor fi necesare modele de proiectare diferite. In general, există două tipuri de modele de proiectare care descriu o proiectare orientată obiect:

• modelele statice, care descriu structura statică a sistemului în termenii claselor sistemului şi ai relaţiilor dintre ele; relaţiile importante care trebuie documentate în această etapă sunt: relaţiile de generalizare, relaţiile foloseşte/este folosit de, relaţiile de compunere etc.;

• modelele dinamice, care descriu structura dinamică a sistemului, cu interacţiunile dintre obiectele sistemului (nu dintre clase); interacţiunile care trebuie documentate aici sunt serviciile solicitate de obiecte şi modul în care starea sistemului este influenţată de acestea.

7.4.5 Specificarea interfeţelor obiectelor O parte importantă a procesului de proiectare este specificarea interfeţelor dintre

diferitele componente ale proiectării. Ea este foarte importantă şi deoarece permite posibilitatea ca obiectele şi celelalte componente să fie proiectate în paralel. Odată ce a fost specificată interfaţa, cei care dezvoltă celelalte obiecte pot considera că aceasta este interfaţa care va fi implementată.

Proiectanţii trebuie să evite informaţiile privind reprezentarea interfeţei. Reprezentarea trebuie să fie ascunsă, iar pentru accesarea şi actualizarea datelor trebuie furnizate servicii ale obiectelor. După cum ştim, dacă reprezentarea este ascunsă, ea poate fi schimbată fără a afecta obiectele care folosesc atributele respective, în acest fel, design-ul devine mai uşor de

Page 88: Ingineria Programarii

88/135

întreţinut. De exemplu, reprezentarea unei stive ca vector poate fi schimbată cu o reprezentare de tip listă fără să afecteze celelalte obiecte care folosesc stiva.

Intre obiecte şi interfeţe nu există în mod necesar o relaţie simplă 1:1. Acelaşi obiect poate avea mai multe interfeţe care semnifică perspective diferite asupra serviciilor pe care le furnizează. De exemplu, în Java sau C#, interfeţele sunt declarate separat de obiecte iar obiectele „implementează" interfeţele. De asemenea, un grup de obiecte poate fi accesat printr-o singură interfaţă.

Proiectarea interfeţelor obiectelor trebuie să specifice semnăturile şi semantica serviciilor asigurate de un obiect sau de către un grup de obiecte.

7.5 Metoda de proiectare Coad-Yourdon Metoda de proiectare orientată obiect Coad-Yourdon constă în 4 componente:

• componenta domeniului problemei; • componenta interacţiunii cu factorul uman; • componenta coordonării task-urilor; • componenta coordonării datelor.

Fiecare componentă este construită din clase şi obiecte. Componenta domeniului problemei se bazează pe modelul logic construit în timpul analizei orientate obiect. Dacă sistemul va fi implementat într-un limbaj de programare orientat obiect, atunci va exista o corespondenţă de l la l între clasele şi obiectele domeniului problemei. Componenta interacţiunii cu factorul uman coordonează mesajele dinspre şi înspre utilizator. Componenta coordonării task-urilor se referă la multiplele fire de execuţie existente într-un sistem. Componenta coordonării datelor furnizează infrastructura pentru înregistrarea şi regăsirea datelor. Corespunzătoare acestor patru componente, metoda identifică patru activităţi:

• proiectarea componentei domeniului problemei; • proiectarea componentei interacţiunii cu factorul uman; • proiectarea componentei coordonării task-urilor; • proiectarea componentei coordonării datelor.

7.5.1 Activitatea I: Proiectarea componentei domeniului problemei în această primă activitate, componentele domeniului rezultate din analiză sunt utilizate

şi suplimentate. Deoarece se bazează pe reutilizarea claselor identificate de faza de analiză, modificările trebuie menţinute pe cât posibil la un nivel redus.

Apoi clasele specifice domeniului problemei sunt grupate împreună prin adăugarea unei clase rădăcină. Vizând direct limbajul de programare care va fi utilizat pentru implementare, poate fi necesară modificarea structurilor gen-spec pentru a se potrivi nivelului de moştenire oferit de limbajul ales.

Dacă structurile gen-spec ale modelului analizei orientate obiect includ moşteniri multiple, trebuie făcute câteva modificări ale acestora atunci când se va utiliza un limbaj de programare orientat obiect care nu suportă mecanismul moştenirii sau care nu suportă decât moştenirea simplă. Două tipuri de moştenire multiplă sunt prezentate în figurile 2 şi 3.

Page 89: Ingineria Programarii

89/135

Figura 7-2 Diamantul mic

Radacina

Clasa A

Clasa AB1

Clasa B

Clasa AB2 Clasa AB1B2

Clasa B2Clasa B1

Figura 7-3 Diamantul mare

Chiar dacă limbajul permite moştenire multiplă (de exemplu C++), se recomandă evitarea acesteia datorită complexităţii pe care o presupune. In cazul limbajelor care suportă

Page 90: Ingineria Programarii

90/135

doar moştenire simplă (Java, C#) se pot aplica metode de a transforma o moştenire multiplă într-o moştenire simplă:

Figura 7-4 Formarea unor ierarhii separate, mapate între ele prin structuri întreg-parte

Ierarhia multiplă poate fi transformată direct într-o ierarhie simplă, caz în care anumite atribute şi servicii vor fi repetate în clasele specializate.

Clasa A

Clasa AB1 Clasa AB2 Clasa AB1B2

Figura 7-5 Acomodarea cu limbaje care nu suportă moştenire multiplă

Dacă limbajul nu suportă mecanismul de moştenire, fiecare structură gen-spec se va descompune în clasele componente.

Clasa AB1 Clasa AB2 Clasa AB1B2

Figura 7-6 Acomodarea cu limbaje care nu suportă moştenire

Page 91: Ingineria Programarii

91/135

Următorul pas în schimbarea componentelor domeniului problemei este îmbunătăţirea aspectelor de performanţă. Viteza poate fi îmbunătăţită prin măsurarea vitezei efective a codului şi prin optimizarea acestuia. Creşterea vitezei e necesară de obicei când între obiecte există un trafic prea mare de mesaje. In acest caz, se combină două sau mai multe clase. Singurul mod de a şti dacă aceste modificări contribuie la creşterea vitezei, este prin măsurare şi observare. Viteza percepută de utilizator poate fi mărită prin memorarea rezultatelor intermediare (engl. „caching").

Tot în această activitate se ia în calcul suportul pentru componenta de coordonare a datelor. Fiecare obiect se poate stoca singur sau poate fi salvat de componenta de coordonare a datelor. In ambele abordări, la componenta domeniului problemei trebuie adăugate atribute şi servicii specifice.

Pentru facilitarea proiectării şi programării, componentele de nivel scăzut pot fi izolate în clase separate. Ultimul pas în proiectarea componentei domeniului problemei este verificarea completărilor aduse la rezultatele fazei de analiză orientată obiect.

7.5.2 Activitatea a Il-a: Proiectarea componentei interacţiunii cu factorul uman

Pentru această activitate se recomandă prototipizarea. Activitatea începe cu clasificarea persoanelor care vor utiliza sistemul, pe baza unor criterii precum:

• nivel de pregătire; • nivel de organizare; • apartenenţa la diferite grupuri (de exemplu: funcţionar sau client). Fiecare

categorie definită trebuie descrisă, incluzându-se un scenariu de task-uri. Pentru fiecare categorie, trebuie notate următoarele:

• ce este; • scopul; • caracteristici (de exemplu: vârstă, educaţie etc.); • factori critici de succes (necesităţi/dorinţe, preferinţe/antipatii/tendinţe

subiective); • nivel de pregătire; • scenarii ale task-urilor.

Apoi trebuie proiectată o ierarhie de comenzi pentru sistem prin studierea sistemelor existente de interacţiune cu factorul uman. Pentru rafinarea ierarhiei, trebuie considerate următoarele:

• ordonarea serviciilor; • partiţionarea modelelor întreg-parte şi verificarea dimensiunilor structurii de

comenzi (lăţime: numărul de categorii de opţiuni, adâncime: numărul de nivele pentru fiecare categorie de opţiuni);

• minimizarea numărului de paşi necesari pentru efectuarea unui serviciu. • în continuare se proiectează detaliat interacţiunea, după următoarele criterii: • consistenţa; • minimizarea numărului de paşi; • acordarea de feedback prompt şi semnificativ utilizatorilor; • asigurarea funcţiilor „undo"; • minimizarea rolului capacităţii de memorare a utilizatorilor pentru reamintirea

opţiunilor; • minimizarea timpului de învăţare şi a efortului; • proiectarea estetică a sistemului (este important ca utilizatorilor să le placă

sistemul).

Page 92: Ingineria Programarii

92/135

După verificarea utilităţii interacţiunilor, sunt proiectate clasele de interacţiune cu factorul uman: ferestre, câmpuri, grafice etc. De câte ori e posibil, trebuie utilizate interfeţele grafice standard cu utilizatorul (GUI).

7.5.3 Activitatea a IlI-a: Proiectarea componentei coordonării task-urilor Mai întâi trebuie văzut dacă e nevoie de task-uri în sistem. Următoarele tipuri de

sisteme necesită task-uri: • sisteme de achiziţii de date şi de control al dispozitivelor locale; • interacţiuni cu factorul uman cu ferestre multiple care rulează simultan; • sisteme multi-user; • sisteme mono-procesor care necesită comunicare şi coordonare între task-uri; • sisteme multi-procesor; • sisteme care comunică cu alte sisteme.

Dacă nu sunt necesare, task-urile nu trebuie proiectate întrucât măresc complexitatea. Există câteva tipuri de task-uri care pot fi identificate:

• task-uri declanşate de un eveniment (de exemplu: apăsarea unui buton al mouse-ului);

• task-uri declanşate la un anumit interval de timp; • task-uri prioritare şi critice.

Când sunt necesare trei sau mai multe task-uri, se recomandă adăugarea unui task special de coordonare. După identificarea task-urilor, necesitatea lor trebuie reverificată. Pentru fiecare task, trebuie specificat:

• ce este task-ul; • cum este coordonat task-ul; • cum comunică task-ul.

7.5.4 Activitatea a IV-a: Proiectarea componentei coordonării datelor Mai întâi trebuie selectată o abordare pentru coordonarea datelor: fişiere simple sau un

sistem de gestionare a bazelor de date, pe baza unor criterii potrivite. Apoi, în funcţie de abordarea aleasă, trebuie proiectată componenta de coordonare a datelor, adică a formatului de date şi a serviciilor corespunzătoare.

7.6 Concluzii Scopul acestui curs este de a realiza o introducere în procesul de proiectare a sistemelor

software. Au fost amintite mai întâi caracteristicile unei proiectări corecte: extensibilitatea, siguranţa si eficienţa, împreună cu modalităţile prin care pot fi atinse aceste deziderate. S-a definit proiectarea orientată obiect şi s-au detaliat etapele acestei faze de dezvoltare a produselor software: determinarea contextului sistemului şi a modelelor de utilizare, proiectarea arhitecturii, identificarea obiectelor, modelele de proiectare şi specificarea interfeţelor obiectelor, în final, s-a concretizat această fază prin descrierea metodei de proiectare Coad-Yourdon, cu cele patru activităţi specifice: proiectarea componentei domeniului problemei, proiectarea componentei interacţiunii cu factorul uman, proiectarea componentei coordonării task-urilor şi proiectarea componentei coordonării datelor.

Page 93: Ingineria Programarii

93/135

8 Limbaje de modelare. UML

8.1 Limbaje de modelare Problema principală care apare în dezvoltarea programelor este complexitatea. Folosirea

de modele poate înlesni abordarea unor astfel de probleme. Un model este o simplificare a unui anumit sistem, care permite analizarea unora dintre proprietăţile acestuia. Lucrul cu modelele poate facilita o mai bună înţelegere a sistemului modelat, datorită dificultăţii intrinseci de înţelegere a sistemului în întregul său. Folosirea tehnicii „divide et impera" permite înţelegerea părţilor componente ale unui sistem, iar ansambul sistemului ca o interacţiune între părţile acestuia. Un model reuşit reţine caracteristicile importante ale obiectului modelat (caracteristicile necesare) şi le ignoră pe cele irelevante. De remarcat că noţiunile de important/irelevant sunt relative, ele depinzând de scopul pentru care se face modelarea. Astfel apare plauzibilă construirea mai multor modele pentru un anumit obiect, fiecare surprinzând anumite aspecte ale acestuia.

Orice metodologie de dezvoltare a programelor abordează problema comunicării dintre membrii echipei. Este posibil să apară situaţii în care modelul construit de proiectant să nu fie înţeles exact de cel ce scrie cod, fie din cauza lipsei de precizie a modului în care este prezentat modelul, fie datorită incompletitudinei acestuia; adesea o serie de amănunte subtile, evidente pentru proiectant, nu sunt transmise explicit. O rezolvare a problemei comunicării ar fi ca aceeaşi persoană să fie implicată direct în toate fazele dezvoltării. Chiar şi aşa, apare des situaţia în care o persoană trebuie să continue munca alteia.

Se impune aşadar existenţa unui limbaj formal de comunicare a cerinţelor. Termenul „formal" este esenţial. De obicei, chiar şi în proiecte de dimensiuni reduse se face modelare, însă într-un limbaj specific celui care modelează, determinat de viziunea sa asupra problemei şi de pregătirea acestuia (de exemplu, un matematician va fi înclinat să utilizeze o notaţie algebrică, un arhitect o notaţie preponderent grafică etc.) Folosirea unui limbaj „intern" nu trebuie considerată negativă, ea având un rol esenţial în gândire în general şi în modelare în particular. Aşa cum formalismul raţionamentului matematic poate fi agentul de transmisie al adevărului matematic (care, odată transmis, este „tradus" în limbajul intern al receptorului), formalismul limbajului de modelare constă în stabilirea unor elemente cu o semantică asupra căreia se convine şi cu ajutorul cărora se pot transmite idei într-un mod cât mai eficient şi fără ambiguităţi.

Deseori, atunci când ne gândim la un obiect într-un context oarecare, ignorăm caracteristicile care nu interesează. De exemplu, la un microprocesor nu ne interesează culoarea sau greutatea. Din punct de vedere practic, este probabil ineficient să considerăm aspectul greutăţii microprocesorului de fiecare dată când utilizăm un calculator şi de aceea mintea umană dispune probabil de un mecanism de eliminare a caracteristicilor irelevante.

Există însă obiecte mult mai complexe decât un microprocesor. O rachetă cosmică este probabil un artefact ce depăşeşte capacitatea de înţelegere a unui singur individ. Motivul pentru care există totuşi microprocesoare şi navete cosmice este simplu: se folosesc modele. Modelul este o simplificare a realităţii. Această simplificare reţine caracteristicile relevante ale unui sistem, în timp ce le ignoră pe celelalte.

Să considerăm analiza comportamentului unui corp sub acţiunea unei forţe externe. Pe baza experienţei în domeniu, ştim că singurele atribute care influenţează analiza în acest caz sunt masa corpului şi forţa rezultantă care acţionează asupra corpului. Un model uzual de reprezentare a acestei probleme este următorul:

Page 94: Ingineria Programarii

94/135

F

Figura 8-1 Modelare în fizică

Acest model face câteva simplificări evidente. Forma corpului este desenată ca un pătrat. Diverse alte caracteristici sau interacţiuni cu mediul sunt ignorate. Pe de altă parte, viteza şi forţa sunt reprezentate prin vectori. Viteza este notată cu v, forţa cu F, masa cu m. Toate aceste elemente formează un model. Pentru un iniţiat, desenul de mai sus este consistent, expresiv şi compact. O descriere alternativă, sub forma de text, deşi posibil mai exactă, ar fi mai greoaie: „fie un corp de masă m asupra căruia acţionează o forţă F cu punctul de aplicaţie în centrul său de greutate. Corpul se deplasează cu o viteză v perpendiculară pe direcţia forţei".

Limbajul natural pare să fie cel mai la îndemână limbaj de modelare. Experienţa arată însă că folosirea sa induce adesea neclarităţi şi inconsistenţe. Apare astfel necesitatea definirii unui limbaj neambiguu pentru specificarea modelelor. Se convine asupra unui set de elemente ale limbajului precum şi asupra semanticii acestora. Evident, descrierea elementelor şi a semanticii se face în ultimă instanţă în limbaj natural, deci pot apărea aici unele ambiguităţi, în acest caz însă, limbajul natural este folosit numai într-o mică parte a sistemului iar problemele de semantică pot fi localizate. Eliminarea ambiguităţilor din modele poate fi făcută îmbunătăţind precizia limbajului de modelare şi nu căutând erori de semantică în întreaga descriere a modelului.

8.2 Ce este UML? Limbajul unificat de modelare (engl. „Unified Modeling Language"), UML, este un

limbaj pentru specificarea, vizualizarea, construirea şi documentarea elementelor sistemelor software, însă poate fi folosit şi pentru alte sisteme, cum ar fi cele de modelare a afacerilor. UML reprezintă o colecţie de practici inginereşti optime, care au fost încununate de succes în modelarea sistemelor mari şi complexe. Dezvoltarea unui model pentru sisteme software industriale înainte de începerea construcţiei efective este esenţială. Modelele bune sunt absolut necesare pentru comunicarea dintre echipele care lucrează la acelaşi proiect şi pentru asigurarea solidităţii arhitecturale. Odată cu creşterea complexităţii sistemului, creşte şi importanţa unor tehnici potrivite de modelare. Există mulţi factori suplimentari pentru succesul unui proiect, dar un factor esenţial este respectarea riguroasă a standardelor cu ajutorul unui limbaj de modelare.

UML nu garantează succesul proiectului, dar perfecţionează multe lucruri. De exemplu, scade în mod semnificativ costul instruirii în cazul schimbărilor legate de proiecte sau organizaţii. Limbajul asigură posibilitatea integrării instrumentelor, proceselor şi domeniilor, însă mai important este faptul că asigură dezvoltatorilor un mod general de rezolvare a problemelor de concepţie şi planificare.

Mai înainte de UML, nu exista un limbaj clar şi standardizat de modelare. Utilizatorii erau nevoiţi să aleagă unul dintre multele limbaje similare, cu diferenţe minore asupra puterii de expresie. Cele mai multe limbaje împărtăşeau o serie de concepte unanim recunoscute, care erau exprimate uşor diferit prin diverse notaţii. Aceste diferenţe au fragmentat industria

Page 95: Ingineria Programarii

95/135

orientată obiect şi au descurajat noii utilizatori să înveţe modelarea vizuală. De fapt, utilizatorii doreau un limbaj standardizat, o „lingua franca” a modelării.

Intre 1989 şi 1994 erau folosite mai mult de 50 de limbaje de modelare software, fiecare cu propriile notaţii. Fiecare limbaje avea elemente de sintaxă specifice şi în acelaşi timp elemente comune cu alte limbaje. Mai mult, nici un limbaj nu era complet, inginerii software apelând deseori la mai multe limbaje.

La mijlocul anilor '90 trei metode s-au dovedit mai eficiente: • Booch: potrivită mai ales pentru proiectare şi implementare, cu dezavantajul

unor notaţii complicate; • OMT (Object Modeling Technique): potrivită pentru analiză şi sisteme

informaţionale cu multe date; • OOSE (Object Oriented Software Engineering): această metodă a propus aşa-

numitele cazuri de utilizare, care ajutau la înţelegerea comportamentului întregului sistem.

In 1994, Jim Rumbaugh, creatorul OMT, a uimit întreaga comunitate software când a părăsit General Electric, alăturându-se lui Grady Booch la Raţional Corp. Scopul parteneriatului era combinarea ambelor perspective într-o metodă unificată, în 1995 şi Ivar Jacobson, creatorul OOSE, a venit la Raţional, iar ideile lui (în special conceptul de cazuri de utilizare) au fost adăugate „Metodei unificate"; metoda rezultantă a fost numită „Limbajul de modelare unificată" - UML. în ciuda disputelor iniţiale, noua metodă a început să aibă din ce în ce mai mulţi susţinători în industria software, formându-se un consorţiu UML din care făceau parte giganţi precum Hewlett-Packard, Microsoft şi Oracle.

UML 1.0 a fost propus spre standardizare în cadrul OMG (Object Management Group) în ianuarie 1997. Până la sfârşitul anului 1997 echipa care lucra la UML s-a extins, urmând o perioadă în care UML a primit o specificare formală mai riguroasă. Versiunea UML l. l a fost adoptată ca standard de către OMG în noiembrie 1997. în martie 2003 a fost publicată versiunea 1.5. în momentul de faţă se lucrează la versiunea 2.0.

În UML există numeroase diagrame (modele), aceasta favorizând existenţa mai multor puncte de vedere privind sistemul. După cum am văzut, procesul de dezvoltare software are multe componente, fiecare cu propria sa perspectivă: analişti, proiectanţi, programatori, testeri, echipe de asigurarea calităţii, autori ai documentaţiei, clienţi. Fiecare dintre aceşti este interesat de un alt aspect al sistemului, la un nivel diferit de detaliu. De exemplu, programatorul trebuie să înţeleagă arhitectura sistemului pentru a o converti în cod de nivel scăzut. Dimpotrivă, autorul documentaţiei trebuie să înţeleagă comportamentul global al sistemului pentru a şti cum funcţionează produsul. UML încearcă să rezolve problema modelării la toate aceste nivele de detaliu.

8.3 Modelarea cazurilor de utilizare Un instrument UML foarte puternic este reprezentarea cazurilor de utilizare, adică

descrierea mulţimii de interacţiuni dintre utilizator şi sistem. Prin construirea unei colecţii de cazuri de utilizare, putem descrie întregul sistem într-o manieră clară şi concisă. Cazurile de utilizare sunt denumite de obicei printr-o combinaţie substantivală-verbală, de exemplu: Plăteşte factura, Creează cont etc. Notaţia pentru un caz de utilizare este prezentată în figura următoare:

Figura 8-2 Caz de utilizare

Page 96: Ingineria Programarii

96/135

Un caz de utilizare trebuie să aibă un iniţiator al acţiunii, numit actor. In cazul unui sistem bancar, retragerea banilor este făcută de clienţi, astfel încât clientul devine unul din actori:

Figura 8-3 Caz de utilizare cu actor

Actorii nu sunt numai oameni, ci orice cauză externă care iniţiază un caz de utilizare, de exemplu un alt sistem de calcul sau un concept mai abstract, precum timpul sau o anumită dată calendaristică: în ultima zi a lunii se actualizează registrele de salarii. Pentru majoritatea sistemelor, un anumit actor poate interacţiona cu mai multe cazuri de utilizare, iar un anumit caz de utilizare poate fi iniţiat de actori diferiţi:

Porneste

OperatorInchide

Produce raport

Vede starea comenzilor

Sistem de comanda

Figura 8-4 Cazuri de utilizare cu actori multipli

Deşi par foarte simple, ignorarea cazurilor de utilizare este o mare greşeală. Acestea sunt foarte importante deoarece:

• definesc domeniul sistemului, permiţând vizualizarea dimensiunii şi sferei de acţiune a întregului proces de dezvoltare;

• sunt similare cerinţelor, dar cazurile de utilizare sunt mai clare şi mai precise datorită structurii riguroase de notaţie;

• suma cazurilor de utilizare este sistemul ca întreg; ceea ce nu este acoperit de un caz de utilizare se situează în afara sistemului de construit;

• permit comunicarea dintre client şi dezvoltatori, de vreme ce diagrama este foarte simplă şi poate fi înţeleasă de oricine;

• ghidează echipele de dezvoltare în procesul de dezvoltare; • ajută echipele de testare şi autorii manualelor de utilizare.

Se pune problema definirii granularităţii cazurilor de utilizare: într-un anumit scenariu, fiecare interacţiune utilizator-sistem trebuie să fie un caz de utilizare sau un singur caz de utilizare poate încapsula toate interacţiunile. Pentru un bancomat, scenariul presupune mai multe interacţiuni: introducerea card-ului, introducerea codului PIN, selectarea sumei dorite,

Page 97: Ingineria Programarii

97/135

confirmarea ei, scoaterea card-ului, preluarea chitanţei. Fiecare din aceşti paşi trebuie să fie câte un caz de utilizare?

Figura 8-5 Cazuri de utilizare incorecte

Dacă pentru un sistem suficient de complex am urma această strategie, ar rezulta un număr imens de cazuri de utilizare, care nu şi-ar mai servi practic scopul de descriere a comportamentului sistemului la nivel înalt.

Când se ia decizia includerii unui nou caz de utilizare, trebuie să se respecte următoarea regulă euristică: un caz de utilizare trebuie să satisfacă un scop pentru actor.

În exemplul anterior, se poate pune problema: preluarea chitanţei este un scop al clientului? Nu neapărat. La fel se procedează şi pentru celelalte cazuri de utilizare. Nici unul nu descrie suficient de corect scopul clientului, care este de fapt retragerea unei sume de bani.

Figura 8-6 Caz de utilizare corect

8.4 Modelarea conceptuală. Diagrama de clase Modelarea conceptuală (numită şi „modelarea domeniului") este activitatea de

identificare a conceptelor importante pentru sistem, în tehnica de programare orientată pe obiect, modelarea conceptuală se realizează prin diagrama claselor, întrucât clasele reprezintă concepte. Diagrama claselor furnizează structura codului care va fi scris. Problema principală este identificarea conceptelor. Regula de urmat aici este: dacă clientul nu înţelege conceptul, probabil că nu este un concept.

O clasă se reprezintă printr-o căsuţă împărţită în trei. In partea de sus este notat numele clasei, în partea mediană atributele iar în partea de jos operaţiile sale.

Page 98: Ingineria Programarii

98/135

Figura 8-7 Clasa în notaţie UML

8.4.1 Asocierea Următorul pas este definirea relaţiilor dintre concepte. Să presupunem următoarele două

concepte:

ManagerNume

MasinaNumar inmatriculareCapacitate motorCuloare

Figura 8-8 Concepte înrudite funcţional

Dacă fiecare manager conduce o maşină în compania respectivă, între aceste concepte există o relaţie:

Figura 8-9 Asocierea în notaţie UML

Linia simplă în UML are rolul de asociere. Numerele descriu cardinalitatea asocierii, adică ne spun câte instanţe sunt permise din fiecare concept. Următoarea figură prezintă câteva cardinalităţi posibile, deşi din punct de vedere al notaţiei nu există restricţii asupra cardinalităţilor care pot fi specificate.

Page 99: Ingineria Programarii

99/135

Concept A

*

Concept B

Concept A

1..*

Concept B

Concept A

1..8

Concept B

Concept A

18

Concept B

Concept A

1,3,5,7,11

Concept B

Oricat de multe

Una sau mai multe

Intre 1 si 8

Exact 18

Multime specificata

Figura 8-10 Cardinalitatea asocierii

O greşeală care poate fi făcută în această fază este să decidem că există o relaţie între două concepte, să trasăm o linie între ele, dar să nu notăm tipul de asociere. După ce vom trasa toate liniile nu vom mai şti ce înseamnă fiecare şi va trebui să o luăm de la început.

În figura următoare este prezentat un exemplu de asociere între clase:

Page 100: Ingineria Programarii

100/135

Masina conduce

1

Manager

Pensie contribuie la

1..*

Angajat

Curs de pregatire

Zi libera

Vacantage

stio

neaz

apoate participa la

poate

lua

poate lua

1

1

1

1..*

0..*

0..*

0..*

0..*

0..*

0..*

Figura 8-11 Asociere complexă

8.4.2 Agregarea Un aspect important al proiectării orientate obiect este agregarea, ideea că un obiect

poate fi construit din altele. De exemplu, un calculator este o agregare între procesor, placă video, placă de sunet etc.

1..*Procesor

Placa video

Placa sunet

Calculator 1..*

1..*

Figura 8-12 Agregarea în notaţie UML

Page 101: Ingineria Programarii

101/135

8.4.3 Compunerea Compunerea este un concept similar cu agregarea, însă mai puternic deoarece implică

faptul că un întregul nu poate exista fără părţi. In exemplul de agregare de mai sus, dacă se înlătură placa de sunet, calculatorul rămâne calculator, însă o carte nu poate exista fără pagini; o carte este compusă din pagini. Notaţia este asemănătoare, dar rombul este plin:

Figura 8-13 Compunerea în notaţie UML

8.4.4 Vizibilitatea atributelor şi operaţiilor In figura următoare sunt prezentate notaţiile pentru vizibilitatea atributelor şi operaţiilor

(privat, protejat, public), în mod text şi în mod grafic.

Notaţie Descriere Notaţie Descriere Spaţiu de nume Metodă sau funcţie Clasa Operator Interfaţă Proprietate Structură Câmp sau variabilă Uniune Eveniment Enumerare Constantă Definiţie de tip Element de enumerare Modul Element de hartă Intrinsec Declaraţie externă Delegat Macro Excepţie Template Harta Necunoscut sau eroare Global

Notaţie semnal Descriere Explicaţii

<No Signal Icon> Public Accesibil de oriunde si prin orice componentă care îl refer Protejat Accesibil numai din clasa sau tipul care îl conţine sau din

clasele sau tipurile derivate din acestea. Privat Accesibil numai din clasa sau tipul care îl conţine. Intern Accesibil numai din această componentă Prieten Accesibil numai din proiect. Referinţă O referinţă la un obiect.

Page 102: Ingineria Programarii

102/135

Figura 8-14 Vizibilitatea atributelor şi operaţiilor

8.4.5 Moştenirea

De multe ori, mai multe clase din arhitectură au atribute şi operaţii comune. Acestea pot fi introduce într-o singură clasă şi moştenite în celelalte. De exemplu:

Caine- varsta+ mananca()+ doarme()+ latra()+ se joaca()

Om- nume- varsta+ mananca()+ doarme()+ munceste()+ vorbeste()+ se joaca()

Figura 8-15 Clase cu atribute şi operaţii comune

Dacă am mai vrea să adăugăm o clasă Pisică, ar trebui să repetăm atributele şi operaţiile comune. Soluţia este moştenirea dintr-o clasă mai generală. Notaţia UML pentru moştenire este următoarea:

Animal- varsta+ mananca()+ doarme()+ se joaca()

Caine

+ latra()

Om- nume+ munceste()+ vorbeste()

Figura 8-16 Moştenirea în notaţie UML

Trebuie să subliniem că atributul vârstă a fost transformat din privat în protejat, pentru a putea fi moştenit în clasele derivate.

Page 103: Ingineria Programarii

103/135

O greşeală frecventă în proiectarea orientată obiect este utilizarea abuzivă a moştenirii, ceea ce conduce la probleme în întreţinerea programului. Dacă mai multe clase sunt legate de una singură, schimbările în clasa de bază vor afecta şi clasele derivate. De asemenea, când derivăm o clasă trebuie să parcurgem întreaga ierarhie pentru a vedea ce face implicit clasa respectivă. Această problemă este cunoscută sub denumirea de proliferarea claselor.

Moştenirea nu trebuie folosită decât ca mecanism de generalizare, adică se foloseşte numai dacă clasele derivate sunt specializări ale clasei de bază.

De asemenea, toate definiţiile clasei de bază trebuie să se aplice tuturor claselor derivate. Aceasta este regula 100%. Dacă nu se aplică această regulă, clasele derivate nu sunt specializări ale clasei de bază. Un exemplu de derivare greşită este următorul:

Animal# varsta+ mananca()+ doarme()+ se joaca()+ zboara()

Om- nume+ munceste()+ vorbeste()

Figura 8-17 Moştenire incorectă

Se poate observa că operaţia zboară nu se aplică şi clasei Om.

8.4.6 Polimorfismul Clasele derivate pot redefini implementarea unei metode. In exemplul următor, clasele

Pian şi Vioară sunt derivate din Instrument. Totuşi, fiecare implementează metoda cântă în felul său specific. Notaţia în acest caz este repetarea numelui metodei în fiecare clasă.

Figura 8-18 Polimorfismul în notaţie UML

Page 104: Ingineria Programarii

104/135

De multe ori avem nevoie să lăsăm o metodă neimplementată într-o clasă (metodă abstractă sau virtuală), pe care să o implementăm pe un nivel mai de jos al ierarhiei. O metodă abstractă se notează cu italice.

Figura 8-19 Clasă de bază cu o metodă abstractă (virtuală)

8.4.7 Interfeţe Dacă o clasă implementează o interfaţă, între cele două există o relaţie de realizare. Să

presupunem că Instrument din exemplul precedent e acum o interfaţă iar clasele Pian şi Vioară trebuie să implementeze metoda cântă. Notaţia este asemănătoare celei de la moştenire, dar cu linie punctată, iar interfaţa e declarată explicit. Cuvintele introduse între „«" şi „»" se numesc stereotipuri, în figura 20 se poate observa stereotipul UML «interface».

Figura 8-20 Notaţia UML pentru interfeţe

O notaţie alternativă este următoarea:

Instrument+ canta()

Pian Vioara

Page 105: Ingineria Programarii

105/135

Figura 8-21 Notaţie alternativă pentru interfeţe

8.4.8 Metode statice În notaţia UML, metodele statice se subliniază:

Math

+ Abs(val : double) : double+ Sin(angle : double) : double+ Exp(val : double) : double

Figura 8-22 Clasă cu metode statice

8.5 Diagrame de interacţiune Descrierea comportamentului implică două aspecte: descrierea structurală a

participanţilor şi descrierea modelelor de comunicaţie. Modelul de comunicaţie al instanţelor care joacă un rol pentru îndeplinirea unui anumit scop se numeşte interacţiune. Diagramele de interacţiune au două forme, bazate pe aceleaşi informaţii de bază, dar care se concentrează fiecare pe un alt aspect al interacţiunii: diagramele de secvenţă şi diagramele de colaborare.

8.5.1 Diagrama de secvenţe Diagrama de secvenţe pune accentul pe aspectul temporal (ordonarea în timp a

mesajelor), fiind potrivită specificaţiilor de timp real şi scenariilor complexe. Notaţia grafică este un tabel care are pe axa X obiecte, iar pe axa Y mesaje ordonate crescător în timp. Axa Y arată pentru fiecare obiect timpul ca o linie verticală punctată („linia vieţii" unui obiect, engl. „lifeline") şi perioada în care obiectul preia controlul execuţiei (reprezentată printr-un dreptunghi) şi efectuează o acţiune, direct sau prin intermediul procedurilor subordonate.

In figura următoare este descrisă interacţiunea simplificată între un client şi un server. De remarcat că în diagrama de secvenţe utilizăm obiecte, nu clase, într-un program pot exista mai multe instanţe ale aceleiaşi clase care au roluri diferite în sistem. Un obiect este identificat de numele său şi numele clasei pe care o instanţiază. Numele obiectului poate să lipsească dacă nu este semnificativ pentru înţelegerea comportamentului sistemului. Liniile orizontale continue semnifică mesaje iniţiate obiecte, iar liniile orizontale punctate reprezintă mesaje-răspuns.

Page 106: Ingineria Programarii

106/135

Figura 8-23 Diagramă de secvenţe

8.5.2 Diagrama de colaborare Diagrama de colaborare se concentrează pe rolurile instanţelor şi relaţiile dintre ele. Ea

nu conţine timpul ca o dimensiune separată, de aceea secvenţa de comunicaţii şi firele de execuţie concurente trebuie numerotate.

Figura 8-24 Diagramă de colaborare în exemplul de mai sus, asteriscul semnifică posibilitatea mai multor

interogări SQL şi a mai multor rezultate.

Page 107: Ingineria Programarii

107/135

8.6 Diagrame de activităţi Diagramele de activităţi sunt folosite pentru modelarea proceselor sau a algoritmilor din

spatele unui anumit caz de utilizare. Din multe puncte de vedere, diagrama de activităţi din UML este echivalentul orientat pe obiect al diagramei fluxurilor de date din dezvoltarea structurată.

Notaţia este următoarea: • nod iniţial: un cerc plin este punctul de start al diagramei; deşi nu este

obligatoriu, prezenţa sa face diagrama mai lizibilă; • nod final: un cerc plin înconjurat de un alt cerc; o diagramă poate avea 0, l sau

mai multe noduri finale; • activitate: dreptunghiurile rotunjite reprezintă activităţile care au loc; • fluxuri: săgeţile diagramei; • punct final al fluxului: un cerc cu un X în interior; indică faptul că procesul se

opreşte în acest punct; • ramificaţie (engl. „fork"): o bară neagră cu un flux de intrare şi mai multe fluxuri

de ieşire; denotă începutul unor activităţi desfăşurate în paralel; • reunire (engl. „join"): o bară neagră cu mai multe fluxuri de intrare şi un flux de

ieşire; denotă sfârşitul prelucrărilor paralele; • condiţie: text asociat unui flux, care defineşte o condiţie care trebuie să fie

adevărată pentru traversarea nodului; • decizie: un romb cu un flux de intrare şi mai multe fluxuri de ieşire; fluxurile de

ieşire includ condiţii; • îmbinare (engl. „merge"): un romb cu mai multe fluxuri de intrare şi un flux de

ieşire; toate fluxurile de intrare trebuie să atingă acest punct pentru ca procesul să continue;

• partiţie (engl. „swimlanes"): o parte a diagramei care indică cine/ce îndeplineşte activităţile;

• notă: o specificaţie suplimentară sub formă de text. În Figura 8-25 este prezentată o diagramă de activităţi cu decizii. „Candidatul trebuie să

fie admis" este o notă asociată unei decizii.

Figura 8-25 Diagramă de activităţi cu decizii

Page 108: Ingineria Programarii

108/135

În Figura 8-26 este prezentată o altă diagramă de activităţi, cu partiţii şi ramificaţii.

Figura 8-26 Diagramă de activităţi cu partiţii şi ramificaţii

Page 109: Ingineria Programarii

109/135

8.7 Diagrame de stări Obiectele au atât comportament, cât şi stare internă, cu alte cuvinte, îndeplinesc acţiuni

şi deţin informaţii. Unele obiecte au comportamente foarte complexe, care depind foarte mult de starea internă. Pentru a le înţelege, dezvoltatorii utilizează diagramele de stări, care descriu modul de funcţionare a instanţelor.

Diagramele de stări UML descriu diferitele stări în care se poate găsi un obiect şi tranziţiile dintre aceste stări. O stare reprezintă o etapă în modelul comportamental al unui obiect şi, le fel ca în cazul diagramelor de activităţi, este posibil să avem stări iniţiale şi stări finale. O stare iniţială este cea în care se găseşte obiectul când este creat. O stare finală este o stare din care nu mai există tranziţii. Tranziţia reprezintă schimbarea stării, trecerea dintr-o stare în alta, şi poate fi determinată de un eveniment extern sau intern.

Figura următoare prezintă un exemplu de diagramă de stare pentru înscrierea la un curs opţional propus cu un număr limitat de studenţi. Dreptunghiurile rotunjite reprezintă stări: instanţele clasei Curs pot fi în următoarele stări: Propus, Planificat, Disponibil pentru înscrieri, Ocupat, închis pentru înscrieri. Starea iniţială este notată tot printr-un cerc plin, iar starea finală printr-un cerc plin înconjurat de alt cerc, la fel ca în diagramele de activităţi (dovadă a consistenţei modelului).

Figura 8-27 Diagramă de stări

În Figura 8-28 putem observa cum stările din Figura 8-27 se grupează într-o aşa numită superstare, pentru a putea descrie un sistem complex la un nivel superior de abstractizare.

Page 110: Ingineria Programarii

110/135

Figura 8-28 Diagramă de stări: superstare

8.8 Diagrama pachetelor Entităţile UML pot fi grupate în pachete - containere logice în care pot fi plasate

elemente înrudite, ca şi directoarele din sistemele de operare. Deşi orice entitate UML poate fi introdusă într-un pachet, de obicei rolul pachetelor este de a grupa clase şi uneori cazuri de utilizare înrudite.

Într-un pachet UML numele elementelor trebuie să fie unice. Totuşi, un avantaj important al pachetelor este că mai multe clase pot avea acelaşi nume dacă aparţin unor pachete diferite. Dacă două echipei şi B lucrează în paralel, echipai nu va trebui să se preocupe de conţinutul pachetului echipei 5, cel puţin din punctul de vedere al denumirilor. Aşadar, utilitatea pachetelor apare deoarece: elementele sistemelor mari pot fi grupate în subsisteme mai mici şi este permisă dezvoltarea iterativă în paralel.

La formarea pachetelor trebuie să se ţină seama de următoarele deziderate. Un pachet trebuie să aibă o funcţionalitate bine precizată, el nu trebuie să îndeplinească funcţii multiple, deoarece devine greu de înţeles. De asemenea, dependenţele dintre pachete trebuie să fie minime.

Page 111: Ingineria Programarii

111/135

Figura 8-29 Diagramă de pachete

La rândul său, un pachet poate fi explicitat:

Figura 8-30 Pachet explicitat

8.9 Diagrame de implementare

8.9.1 Diagrama componentelor Diagrama componentelor este asemănătoare cu diagrama pachetelor, permiţând

vizualizarea modului în care sistemul este divizat şi a dependenţelor dintre module. Diagrama componentelor pune însă accentul pe elementele software fizice (fişiere, biblioteci, executabile) şi nu pe elementele logice, ca în cazul pachetelor.

Figura 8-31 Diagramă de componente

Page 112: Ingineria Programarii

112/135

8.9.2 Diagrama de lansare Diagramele de lansare (engl. „deployment diagrams") descriu configuraţia elementelor

de prelucrare la run-time şi componentele software, procesele şi obiectele care se execută pe ele. Aceste diagrame sunt grafuri de noduri conectate de asociaţii de comunicare. Nodurile pot conţine instanţe ale componentelor, indicând faptul că acea componentă rulează sau se execută în nodul respectiv. Nodurile sunt reprezentate prin paralelipipede. Cercurile reprezintă interfeţe, în figura 32 este prezentat un exemplu de diagramă de lansare pentru o aplicaţie browser care utilizează protocolul TCP/IP pentru accesarea unui server.

Figura 8-32 Diagramă de lansare

8.10 Concluzii În aceste cursuri a fost mai întâi argumentată necesitatea unui sistem de notaţie

standardizat pentru etapele dezvoltării unui proiect software. Apoi au fost prezentate notaţiile principale ale limbajului UML: diagramele de clase, de interacţiune, de activităţi, de stări, de pachete şi de implementare.

Page 113: Ingineria Programarii

113/135

9 Implementarea

9.1 Introducere Implementarea este faza în care este produs codul corespunzător proiectului furnizat de

faza anterioară, îndeplinind restricţiile de resurse, acurateţe şi performanţă indicate de specificaţii.

Procesul implementării este cel mai dificil de descris, nefiind riguros definit. Implementarea este procesul transformării abstractizării prezentate în proiect într-o realizare fizică utilizând limbajul arhitecturii ţintă. Este o fază concretizată într-o stare confuză, deseori haotică şi instabilă pentru sistemele software complexe, în care coordonarea este dificilă.

O problemă majoră care cauzează instabilitatea constă în dificultatea translatării proiectării în cod sursă. In primul rând, de cele mai multe ori proiectarea nu va realiza o mapare de l la l către implementare. De aceea, oricât de bun ar fi proiectul, este necesar un oarecare efort pentru a scrie codul corespunzător, ori aceasta este o sursă de erori.

În al doilea rând, procesul de transformare proiect-implementare este şi mai dificil când proiectul nu este complet, consistent sau nu comunică exact şi inteligibil ceea ce se doreşte din partea sistemului. Erorile de proiectare determină pierderea timpului programatorilor în a rezolva probleme greşite puse. Acestea sunt erorile de logică şi sunt cele mai frecvente. De aceea este foarte importantă utilizarea unor metode riguroase pentru prezentarea proiectării.

In al treilea rând, unele aspecte sunt în afara domeniului proiectantului. Efectele exacte ale utilizării unui anumit sistem de operare sau limbaj de programare sunt în afara scopului proiectantului dar reprezintă o importantă decizie a programatorului.

În cele din urmă, implementarea însăşi este predispusă către erori, fiind un proces creator uman. Limbajul de programare poate fi folosit incorect, aceasta însemnând că un anumit timp şi efort se vor consuma pentru corectarea acestor erori. Din păcate, corectarea erorilor nu este o sarcină uşoară. S-a constatat că un programator are 50% şanse să descopere eroarea într-un interval de 5-10 linii de cod şi numai 20% şanse să o descopere într-un domeniu de 40-50 linii.

Documentele de bază produse în această fază sunt: • codul sursă şi obiect comentate într-o formă standard sau respectând anumite

convenţii; • pliante (dosare) ale software-ului, prezentând modulele software individuale; • manualul de utilizare a produsului software, prezentând convenţiile utilizate în

programe, o prezentare generală a implementării, descrierea particularităţilor; planul pentru coordonarea configuraţiei;

• planul pentru testarea produsului. Un aspect important în managementul fazei de implementare este cel al

managementului configuraţiei sistemului software. Motivul constă în faptul că produsul se găseşte în diverse faze pe măsură ce echipele de programatori implementează diferite părţi ale sale şi nu există un produs „întreg" până la integrarea tuturor modulelor. De aceea, la anumite intervale de timp, toate modulele vor fi reunite formând o anumită versiune a produsului, baza de la care programatorii vor lucra în continuare. Aceste aspecte au fost deja detaliate într-un curs anterior.

9.2 Limbaje de programare În faza de implementare se extinde mai întâi proiectul din faza anterioară la

componentele primitive ale sistemului. Se vor folosi aceleaşi metode din faza de proiectare (proiectarea structurată, proiectarea orientată obiect, metode formale). Următorul pas este a

Page 114: Ingineria Programarii

114/135

defini prelucrarea fiecărui modul prin metode ca: hărţi de fluxuri (engl. „flowcharts"), rafinarea pas cu pas, limbaje de proiectare a programelor, pseudocodul, etc.

Implementarea implică scrierea codului într-un limbaj de programare, verificarea şi integrarea sa cu alte programe pentru obţinerea unui sistem final.

O decizie foarte importantă este alegerea unui limbaj potrivit de programare. Din punct de vedere al semanticii, următoarele două clase mari de limbaje sunt larg recunoscute:

• limbaje imperative (numite uneori şi procedurale): Fortran, Basic, Pascal, C/C++, Java, C#

• limbaje declarative: Lisp, Prolog, Clips.

9.2.1 Limbaje imperative Într-un limbaj imperativ, programatorul controlează exact execuţia programului, el

trebuie să definească modul cum se execută prelucrările, în general, aceste limbaje suportă caracteristicile programării structurate:

• secvenţa: permite specificarea ordinii execuţiei instrucţiunilor; • selecţia: permite evaluarea unei condiţii şi luarea unei decizii; • iteraţia: permite existenţa structurilor repetitive.

O altă caracteristică importantă este diviziunea în module, care permite descompunerea funcţională. Limbajele imperative moderne permit realizarea de programe orientate obiect, în acest caz, programarea structurată poate fi aplicată doar în interiorul metodelor

Alte caracteristici ale unor limbaje imperative sunt: • structurarea în blocuri: impune ca un modul să aibă un singur punct de intrare şi

eventual un singur punct de ieşire; • tipizarea puternică: impune ca tipul fiecărei date să fie declarat. Acest lucru

previne aplicarea operatorilor asupra obiectelor incompatibile din punct de vedere al tipului şi ajută compilatorul în evidenţierea erorilor şi în compilarea eficientă;

• recursivitatea: permite unui modul să se autoapeleze. Limbajele orientate obiect suportă toate caracteristicile limbajelor de programare structurată şi, în plus:

• moştenirea: este tehnica prin care modulele pot prelua funcţionalităţi de la modulele de nivel superior;

• polimorfismul: este abilitatea unui program de a lucra cu diferite tipuri de date sau de a realiza acţiuni diferite pentru instanţe ale unor clase diferite. Ideal ar fi ca un limbaj OO să fie complet polimorf, astfel încât să nu mai fie necesare porţiuni de cod pentru fiecare tip de dată. Polimorfismul implică suportul pentru legare dinamică (engl. „dynamic binding"), adică legarea metodelor obiectelor de selectarea mesajului în momentul execuţiei şi nu al compilării;

• mesajele: mesajele sunt utilizate pentru implementarea interfeţelor. Un mesaj conţine detaliile acţiunii care trebuie realizată şi este trimis de către un obiect către un alt obiect pentru a invoca un serviciu al celui din urmă.

9.2.2 Limbaje declarative Limbajele declarative pornesc cu o concepţie diferită de limbajele imperative.

Programatorul nu mai controlează explicit execuţia programului, ci specifică ce trebuie realizat, care sunt regulile care pot rezolva problema, însă paşii efectivi de rezolvare a problemei şi ordinea acestora sunt decişi de program, nu de programator. Construcţia secvenţă îşi pierde din importanţă deoarece programul nu mai poate fi văzut ca o secvenţă de instrucţiuni de la început până la sfârşit. Datorită diferenţei de abordare, programele scrise în limbaje declarative apar diferite faţă de cele scrise în limbaje imperative.

Page 115: Ingineria Programarii

115/135

9.3 Analiza unor limbaje de programare

9.3.1 C/C++ C/C++ este folosit pe scară largă pentru programarea profesionistă. Acesta combină

virtuţile unui limbaj de nivel înalt cu eficienţa limbajului de asamblare. C a fost dezvoltat de Dennis Ritchie în 1972 la AT&T's Bell Laboratories şi a fost folosit pentru scrierea sistemului de operare UNIX. Datorită legilor antitrust, Laboratoarelor Bell li s-au interzis drepturile de autor asupra C-ului şi UNIX-ului. De aceea, compilatoarele de C sunt în domeniul public şi au fost adoptate de majoritatea universităţilor. Limbajul C a fost standardizat (ANSI C) în 1990. în 1981, Bjarne Stroustrup a propus C++. C-ul originar este utilizat foarte rar, de aceea mulţi programatori se referă la C++ cu termenul de „C". C++ conţine toate elementele de bază ale C-ului, la care s-au adăugat numeroase trăsături de programare orientată obiect. Şi C++ a fost standardizat (ISO C++) în 1997.

Avantaje: • Eficienţă: C/C++ pot crea programe mai rapide şi cu dimensiuni mai mici decât

aproape orice alt limbaj de programare, cu excepţia limbajului de asamblare; • Portabilitate: Un program scris în C/C++ poate fi uşor copiat şi compilat pe alt

calculator, cu unele modificări. Pentru majoritatea sistemelor de operare există compilatoare de C/C++;

• Flexibilitate: Pentru un programator experimentat, conversia liberă a tipurilor de date este un avantaj. Pentru începători, totuşi, acest lucru poate genera confuzie şi erori;

• Număr mare de programatori: Acest limbaj este cunoscut de foarte mulţi programatori, care ar putea modifica mai târziu un program existent. Dezavantaje:

• Dificil de stăpânit: C/C++ este unul din cele mai dificile limbaje de programare. In timpul necesar învăţării complete a C/C++, un program poate fi deja terminat în alt limbaj;

• Complexitate: C/C++ are puterea de a manipula direct memoria şi hardware-ul calculatorului. Acest lucru sporeşte şansele apariţiei unei erori şi timpul necesar pentru debug;

• Dificil de citit şi înţeles: Programele sunt create o dată şi modificate de multe alte ori. Datorită naturii criptice a C/C++, înţelegerea unui program poate ridica probleme.

Pentru a exemplifica ultima afirmaţie, oricât ar părea de ciudat, următorul program C este corect sintactic: #include <stdio.h> #define O (b=b?b-1:(p++,5),*p&1<<b) #define o O?O char*p,j=2,b,c;e(n){for(p="|'8I0>+@{=#_P0-]PV.]F>TM!YK'?? |T\"Z8}aE<&D-!:-T'\"\O<~cG5$,<2'#;/UI.0{d^HV6817-2F95-T7X|c^/1XB]*)3WHG0/0}dN>G RMZB.12.P] ~hM^J\\[\<R^ (7;)R9A78{gU!:N)E5OPUR><29A6|e&9V;E[Q:,S1.P] }eES.$Z):B.*O+$G_ ~fWU8)75?I#\75?WHN0{jE=]<V*1]JI#5VK)R9A6~J5X9X#69/+VX4 =S%!X-[)OE #1XRZ\"?~%^-#Dz&M\\RST|%\G66*~&^HV0> {%^-8_P}%N>FO(}'M^JQ=z&U!:O(J{%&9G4|%ERO(~(WU8)G4{'E=]^G4",b=n;*p++<122||--b;);c=*p;while(--c>31&&c!=79)putchar(44+(o?o?o?-34:68:O?60:74:O?64:o?o?2:54:O?23:63:77:O?55:o?76:15:35:-12:o?61:O?56:65:O?66:53:o?o?O?75:58:0:70:57:o?71:o?73:1:67:O?72:59));c>32?e(n-1):0;}main(){while(++j<15)e(1),e(13+j),e(15),e(j-(j<4));}

şi, mai mult, afişează următorul text:

Page 116: Ingineria Programarii

116/135

On the first day of Christmas my true love gave to me a partridge in a pear tree. On the second day of Christmas my true love gave to me two turtle doves and a partridge in a pear tree. On the third day of Christmas my true love gave to me three french hens, two turtle doves and a partridge in a pear tree. On the fourth day of Christmas my true love gave to me four calling birds, three french hens, two turtle doves and a partridge in a pear tree. On the fifth day of Christmas my true love gave to me five golden rings; four calling birds, three french hens, two turtle doves and a partridge in a pear tree. On the sixth day of Christmas my true love gave to me six geese a-laying, five golden rings; four calling birds, three french hens, two turtle doves and a partridge in a pear tree. On the seventh day of Christmas my true love gave to me seven swans a-swimming, six geese a-laying, five golden rings; four calling birds, three french hens, two turtle doves and a partridge in a pear tree. On the eighth day of Christmas my true love gave to me eight maids a-milking, seven swans a-swimming, six geese a-laying, five golden rings; four calling birds, three french hens, two turtle doves and a partridge in a pear tree. On the ninth day of Christmas my true love gave to me nine ladies dancing, eight maids a-milking, seven swans a-swimming, six geese a-laying, five golden rings; four calling birds, three french hens, two turtle doves and a partridge in a pear tree. On the tenth day of Christmas my true love gave to me ten lords a-leaping, nine ladies dancing, eight maids a-milking, seven swans a-swimming, six geese a-laying, five golden rings; four calling birds, three french hens, two turtle doves and a partridge in a pear tree.

Page 117: Ingineria Programarii

117/135

On the eleventh day of Christmas my true love gave to me eleven pipers piping, ten lords a-leaping, nine ladies dancing, eight maids a-milking, seven swans a-swimming, six geese a-laying, five golden rings; four calling birds, three french hens, two turtle doves and a partridge in a pear tree. On the twelfth day of Christmas my true love gave to me twelve drummers drumming, eleven pipers piping, ten lords a-leaping, nine ladies dancing, eight maids a-milking, seven swans a-swimming, six geese a-laying, five golden rings; four calling birds, three french hens, two turtle doves and a partridge in a pear tree.

9.3.2 Basic Basic (Beginner's AII Purpose Symbolic Instruction Code) a fost dezvoltat la Dartmouth

College în 1964 sub coordonarea lui J. Kemeny şi a lui T. Kurtz. Ideea era crearea unui limbaj foarte simplu de învăţat care să servească drept treaptă intermediară pentru studenţii care învăţau Fortran şi Algol. Basic a fost primul produs vândut de Microsoft şi primul caz major de piraterie software: a fost copiat şi distribuit pe scară largă încă înainte de a fi lansat (Bill Gates a pierdut o copie în timpul unei demonstraţii publice).

Fiind un limbaj de nivel înalt uşor de folosit, este foarte nimerit pentru a preda fundamentele programării începătorilor şi a devenit un limbaj utilizat de mulţi amatori pentru realizarea de programe simple. La început necesita un interpretor, astfel încât începătorii să poată crea programul într-o manieră interactivă, să-1 ruleze, testeze şi corecteze. Limbajele interpretate favorizează învăţarea programării, dar rulează mult mai lent decât programele compilate. De aceea, programatorii profesionişti le evită în general. Noile versiuni de Basic posedă compilatoare, cum ar fi Visual Basic, în care pot fi create programe de calitate comercială. Şi Basic-ul a fost standardizat: ANSI Minimal Basic (1978) şi ANSI Full Basic (1987).

Avantaje: • Uşor de învăţat: Poate fi învăţat şi folosit mai repede decât majoritatea celorlalte

limbaje; • Permite prototipizare rapidă: în Visual Basic, prototipurile se pot crea repede.

Apoi acestea pot fi transformate în programe reale funcţionale. Alte limbaje, precum C/C++, sunt prea greu de utilizat pentru a crea un prototip.

• Dezavantaje: • Lent: Programele scrise în Visual Basic în general rulează mult mai încet decât

programele echivalente în C/C++. Dacă viteza este o condiţie importantă pentru program, Visual Basic nu este o alegere potrivită;

• Claritate redusă: Datorită sintaxei limbajului, programele de mari dimensiuni devin greu de citit şi înţeles;

• Inflexibil: Visual Basic este uşor de învăţat, însă ascunde detaliile tehnice ale programării. In acelaşi timp, împiedică programatorul să controleze total calculatorul, ceea ce limitează puterea programelor;

• Portabilitate limitată: Visual Basic rulează numai pe platforme Windows.

Page 118: Ingineria Programarii

118/135

9.3.3 Pascal Este un limbaj de nivel înalt care încurajează programarea modulară, bine structurată.

Pascal este acceptat pe scară largă ca limbaj educaţional şi de dezvoltare a aplicaţiilor. Este mai puţin flexibil decât C/C++, compilatorul face mai multe verificări, însă în acest fel scade riscul apariţiei erorilor. Având la început scop didactic, conversiile de tip sunt mult mai stricte decât în C, ceea ce scade riscul apariţiilor erorilor, dar în acelaşi timp scade şi flexibilitatea, libertatea şi imaginaţia pe care o pot pune în practică programatorii. Limbajul a apărut în 1971, creat de Nicklaus Wirth. Un pas înainte important faţă de celelalte limbaje existente la momentul respectiv a fost faptul că suporta recursivitatea.

In prezent, Pascal stă la baza mai multor medii de dezvoltare a aplicaţiilor comerciale, cu suport orientat obiect. Borland Delphi (sub Windows) şi CodeWarrior Pascal şi THINK Pascal (sub Macintosh) sunt cele mai utilizate.

9.3.4 Java Limbajul Java este rezultatul „Stealth Project" al Sun Microsystem, care avea ca scop

cercetarea în domeniul aplicabilităţii calculatoarelor pe piaţa produselor electronice în vederea creării de produse electronice inteligente care să poată fi controlate şi programate centralizat, printr-un dispozitiv asemănător cu o telecomandă. Aceleaşi cerinţe de stabilitate şi independenţă în sisteme eterogene existau şi pentru Internet. De aceea, deşi proiectat iniţial în alte scopuri, Java s-a potrivit perfect aplicaţiilor world-wide-web.

Sun a prezentat formal Java în 1995. In curând, Netscape Inc. a anunţat că va încorpora suport pentru Java în browser-ul lor. Mai târziu, şi Microsoft a făcut acelaşi lucru, întărind rolul limbajului Java în zona Internet.

Java aparţine unei noi generaţii de limbaje de programare, care şi-a câştigat în ultima perioadă o mare popularitate. In Java se pot crea programe complexe sau mini-programe (applet-uri) care să ruleze pe Internet. Deoarece Java este încă la început, Sun Microsystems, creatorul său, încearcă permanent să îmbunătăţească limbajul. Din acelaşi motiv, multe companii au încă o atitudine de expectativă în legătură cu Java. Totuşi, dacă portabilitatea este importantă, Java este alegerea cea mai bună.

O caracteristică a limbajului este maşina virtuală (sau interpretorul) care trebuie să existe pe calculatorul client. Fiecare platformă are propria maşină virtuală, care execută de fapt programul pe sistemul de operare respectiv. Când un program este compilat, rezultă un fişier de „byte-codes", foarte asemănătoare cu instrucţiunile maşină, fără însă a fi specifice unui anumit procesor. Procesul transformării „byte-codes" în cod maşină este foarte simplu şi se face la runtime. Din acest motiv însă, există clare diferenţe de performanţă între un program Java interpretat şi un program C, compilat în cod nativ. Bruce Eckel, în „Thinking in Java", consideră că, în medie, un program Java este de aproximativ 20 de ori mai lent decât un program C echivalent.

Un alt avantaj îl constituie „garbage collector"-ul, care scuteşte programatorul de sarcina dezalocării memoriei alocate dinamic. Această trăsătură face programarea mai uşoară şi elimină o întreagă clasă de erori.

Avantaje: • Portabilitate deplină: Orice program scris în Java poate rula (teoretic) pe toate

sistemele de operare importante (Windows, Linux, Macintosh) fără modificări suplimentare;

• Siguranţă: Java a preluat trăsăturile pozitive ale C/C++ şi a evitat multe din neajunsuri. Neavând pointeri, programele Java au mai puţine şanse de eroare la accesarea memoriei;

• Bazat pe C/C++: Deoarece Java e derivat din C/C++, oricine ştie C/C++ poate învăţa rapid Java.

Page 119: Ingineria Programarii

119/135

Dezavantaje: • Lent şi mai puţin eficient: Deoarece este un limbaj interpretat de către maşina

virtuală, programele Java rulează mai lent decât programele echivalente în cod nativ;

• Dificil de învăţat: Java arată ca C/C++, deci este la fel de greu de învăţat ca şi acesta.

9.3.5 C# C# este cel mai nou limbaj important de programare, dezvoltat de Anders Hejlsberg la

Microsoft. C# este primul limbaj proiectat de la început pentru Internet. Este un limbaj modern care combină cele mai bune caracteristici ale celor mai folosite limbaje de programare. Odată cu C#, Microsoft a lansat şi platforma .NET, care permite compilarea şi interfaţarea de programe scrise în limbaje diferite.

C# este foarte asemănător în ceea ce priveşte sintaxa cu Java, însă păstrează o apropiere mai mare de C++. Atât Java cât şi C# compilează mai întâi într-un limbaj intermediar: Java byte-code, respectiv Microsoft Intermediate Language (MSIL). În C#, compilarea codului intermediar în cod nativ este însă mai eficientă. C# are de asemenea un garbage collector, care s-a demonstrat matematic că este foarte aproape de optimul posibil. Ca şi Java, C# a renunţat la moştenirile multiple, în favoarea unui model de moştenire simplă extins de moştenirea multiplă a interfeţelor. Spre deosebire de Java, care a renunţat total la pointeri, C# permite folosirea acestora, dar numai în cazuri speciale, marcate „unsafe".

Un alt avantaj îl constituie posibilitatea generării automate a documentaţiei pe baza codului sursă.

9.4 Comparaţie între unele limbaje de programare Pentru a exemplifica, să considerăm implementarea unui algoritm de căutare a

maximului dintr-un şir de numere în timpul introducerii şirului respectiv. Limbajul C:

#include <stdio.h> #include <stdlib.h> void main() { int dim, i; float *vector; // float vector[10]; float max; printf("Dimensiunea vectorului: "); scanf("%d", &dim); vector = (float*)calloc(dim, sizeof(float)); for (i=0; i<dim; i++) { printf("Vector[%d]: ", i+1); scanf("%f", &vector[i]); if (i==0) max = vector[i]; else { if (max < vector[i]) max = vector[i]; } } printf("Maximul: %f", max); free(vector); }

Page 120: Ingineria Programarii

120/135

Limbajul C++

#include <iostream.h> void main() { int dim; cout << "Dimensiunea vectorului: "; cin >> dim; float *vector; vector = new float[dim]; float max; for (int i=0; i<dim; i++) { cout << "Vector[" << (i+1) << "]: "; cin >> vector[i]; if (i==0) max = vector[i]; else { if (max < vector[i]) max = vector[i]; } } cout << "Maximul: " << max; delete []vector; }

Limbajul Basic 10 INPUT "Dimensiunea vectorului: ", dimvect 20 DIM vector(dimvect) 30 LET max = O 40 FOR i=l TO dimvect 50 INPUT ("Vector ("; i ; "): "); vector(i) 60 IF i = l THEN LET max = vector(i): GOTO 80 70 IF max < vector(i) THEN max = vector(i) 80 NEXT i 90 PRINT "Maximul: "; max

O versiune adaptată pentru Visual Basic este:

Private Sub Max() Dim max As Double Dim dimens As Integer Dim vector(dimens) As Double For i = 1 To dimens If i = 1 Then max = vector(i) Else If max < vector(i) Then max = vector(i) Next i End Sub

Limbajul Pascal

program simplu; { Pascal nu este un limbaj case sensitive } uses crt; var max: real;

Page 121: Ingineria Programarii

121/135

dim,i: integer; vector:array[1..10] of real; begin

writeln('Dimensiunea vectorului: '); readln(dim); for i:=1 to dim do begin

writeln('Vector[', i, ']: '); readln(vector[i]); if i=1 then

max := vector[i] else begin

if max < vector[i] then max := vector[i];

end; end; writeln('Maximul: ', max);

end. Limbajul Java

import java.io.*; public class Simple { public static void main(String[] args) { System.out.println("Dimensiunea vectorului: "); String s = System.in.readLine(); int dim = Integer.parseInt(s); float[] vector = new float[dim]; float max = 0; for (int i=0; i<dim; i++) { System.out.println("Vector[" + (i+1) + "]: "); s = System.in.readLine(); vector[i] = Float.parseFloat(s); if (i==0) max = vector[i]; else { if (max < vector[i]) max = vector[i]; } } System.out.println("Maximul: " + max); } }

Limbajul C#

using System; class Simple { [STAThread] static void Main(string[] args) { Console.Write("Dimensiunea vectorului: "); string s = Console.ReadLine(); int dim = int.Parse(s); float[] vector = new float[dim];

Page 122: Ingineria Programarii

122/135

float max = 0; for (int i=0; i<dim; i++) { Console.Write("Vector[{0}]: ", i+1); s = Console.ReadLine(); vector[i] = float.Parse(s); if (i==0) max = vector[i]; else { if (max < vector[i]) max = vector[i]; } } Console.WriteLine("Maximul: {0}", max); } }

Limbajul Clips (declarativ)

(defrule introd_dim =>

(printout t "Dimensiunea vectorului: ") (assert (dim (read)))

) (defrule init_max

(dim ?) =>

(printout t "Vector(1): ") (bind ?val (read)) (assert (vector ?val)) (assert (max ?val)) (assert (i 2))

) (defrule continue

(dim ?d) ?f1 <- (i ?i) (test (<= ?i ?d)) ?f2 <- (vector $?vect) ?f3 <- (max ?max)

=> (printout t "Vector(" ?i "): ") (bind ?val (read)) (retract ?f1 ?f2) (assert (i (+ ?i 1))) (assert (vector $?vect ?val)) (if (< ?max ?val) then

(retract ?f3) (assert (max ?val))

) ) (defrule print_max

(dim ?d) (i ?i) (test (> ?i ?d)) (max ?max)

=> (printout t "Maximul: " ?max crlf)

)

Page 123: Ingineria Programarii

123/135

Notă: găsirea maximului într-un şir se poate realiza mult mai uşor în Clips: (defrule max

(vector $? ?v1 $?) (forall (vector $? ?v2 $?)

(test (<= ?v2 ?v1)) )

=> (printout t "Maximul: " ?v1 crlf)

) În primul program s-a încercat însă implementarea algoritmului secvenţial. Se vede

astfel diferenţa de concepţie între abordările imperativă şi declarativă.

9.5 Utilitare pentru implementare şi testare O gamă largă de utilitare sunt valabile pentru dezvoltarea programelor, depanarea şi

testarea acestora: • Utilitare de modelare (engl. „modeling tools"): Generează nucleul („scheletul")

modulelor; generează automat declaraţii pentru constante, variabile, tipuri pentru includerea în codul sursă al fiecărui modul. Unele utilitare de modelare pot transforma diagramele reprezentând apelurile modulelor în apeluri de funcţii sau proceduri complet comentate, deşi fără a avea valorile parametrilor actuali. Dacă asemenea utilitare sunt folosite, scrierea codului începe prin a completa „scheletul" unui astfel de modul: se completează toate apelurile; se introduc construcţiile iterative (while, repeat, loop, etc); se introduc construcţiile alternative (if, case, etc.) şi se adaugă în final detaliile de bază ca operaţii aritmetice, operaţii de intrare-ieşire şi alte apeluri sistem;

• Generatoare de cod: Transformă relaţii formale în cod sursă. Se utilizează în domenii ca gestiunea bazelor de date şi interacţiunea cu factorul uman, domenii caracterizate de un cod repetitiv şi de necesitatea de a executa numeroase operaţii de rutină dar esenţiale. Deoarece generatoarele de cod sunt din ce în ce mai mult integrate în metodele de proiectare, va fi posibil de a genera automat codul sursă pentru porţiuni din ce în ce mai mari ale componentelor unui sistem. Chiar dacă pentru părţi ale sistemului codul va fi scris manual, utilizarea generatoarelor de cod rămâne în continuare avantajoasă. Modificări în cerinţele software pot rezulta în schimbări automate în declaraţiile datelor şi modulelor, păstrând şi verificând consistenţa de-a lungul ciclului de viaţă al produsului;

• Editoare: Creează şi modifică codul sursă şi documentaţia. • Editoare senzitive la limbaj: Creează cod sursă corect din punct de vedere

sintactic. Aceste editoare conţin un interpretor care ajută în scrierea unui cod sursă corect din punct de vedere sintactic. Cele mai simple astfel de editoare recunosc parantezele şi realizează o aliniere automată care fac programul mai uşor de înţeles şi reduc erorile. E posibil, de asemenea, de a furniza scheme (tipare) pentru construirea programelor, conţinând headere standard, secţiuni obligatorii pentru declararea constantelor şi tipurilor de date. Acestea pot fi generate automat de utilitarele de modelare. Editoarele care recunosc aceste scheme pot reduce timpul de dezvoltare şi pot preveni erorile;

• Analizoare statice: Examinează codul sursă. Analiza statică este procesul de scanare a textului unui program pentru detectarea unor erori:

o identifică variabile neutilizate sau utilizate înainte de a fi asignate; o verifică dacă valoarea variabilei este în intervalul admis; o furnizează o prezentare a structurii aplicaţiei;

Page 124: Ingineria Programarii

124/135

o măsoară complexitatea codului în termenii unei metrici; o transformă codul sursă într-un limbaj intermediar pentru verificare

formală; o măsoară anumite atribute ale codului cum ar fi numărul de linii de cod şi

nivelul maxim de imbricare. Cele mai multe compilatoare furnizează unele din caracteristicile analizoarelor statice

(cum ar fi prima caracteristică). Analizoarele statice dedicate de obicei furnizează funcţii de analiză statică avansate, cum ar fi analiza structurii codului;

• Compilatoare: Transformă codul sursă în cod obiect. Acestea variază în viteză, completitudinea verificărilor, uşurinţa utilizării, folosirea sintaxei standard, calitatea codului şi afişărilor şi prezenţa caracteristicilor de programare. Alegerea compilatorului este de importanţă crucială. Viteza compilatorului afectează costul produsului şi uşurinţa în dezvoltarea, depanarea şi întreţinerea produsului, în timp ce calitatea codului afectează performanţele produsului în timpul execuţiei. Compilatoarele ar trebui comparate ţinând cont de viteza lor, de timpul de execuţie al programului şi dimensiunea codului. Dimensiunile stivei şi a memoriei heap pot fi, de asemenea, importante. Compilatoarele variază mult şi funcţie de caracteristicile care suportă programarea:

o listare completă; o cross-referenţierea; o dimensiunea datelor şi modulelor; o diagnosticare; o verificare completă; o verificarea limitelor vectorilor.

Cele mai avansate compilatoare execută anumite optimizări pentru maşini secvenţiale sau paralele, încercând să descopere şi să elimine deficienţele codului sursă. Aceste optimizări pot fi impuse prin switch-uri, de exemplu prin directive în codul sursă. Utilizatorii ar trebui să verifice dacă optimizările pe care le doresc sunt implementate în compilatoarele candidat.

• Linkeditoare: Reunesc modulele obiect în programe executabile. Acestea sunt furnizate de maşină, sistemul de operare sau compilator. De aceea, utilizatorul are în foarte mică măsură controlul asupra alegerii acestora. Este util ca linkeditorul să determine automat bibliotecile şi directoarele pe care trebuie să le utilizeze şi care sunt modulele sau componentele care trebuie linkeditate. Cele mai multe linkeditoare pot fi controlate de parametri creaţi de utilitare build sau make;

• Depanatoarele: Localizează erori în timpul execuţiei programului. Utilizarea depanatoarelor simbolice interactive este puternic încurajată, mai ales pentru verificare. Un depanator bun este integrat cu editorul şi compilatorul/interpretorul şi permite o gamă de moduri de investigare: pas cu pas, trasare prin breakpoint, vizualizarea valorilor variabilelor, setarea unor condiţii;

• Analizoarele dinamice: Examinează programele în curs de execuţie. Analiza dinamică este procesul de măsurare a resurselor (timp CPU, timp intrare-ieşire, memorie) consumate de fiecare modul şi linie de cod. în contrast cu analizoarele statice, cele dinamice se folosesc pentru programe în curs de execuţie. Analizoarele dinamice se mai numesc şi profilers. Ele mai pot fi folosite şi pentru a determina dacă toate instrucţiunile programului au fost executate în timpul testului (test de acoperire). Unele analizoare dinamice verifică dacă programul utilizează corect memoria, de exemplu, verifică dacă apelurile pentru alocarea memoriei au corespondent în apeluri pentru dezalocare, determinând

Page 125: Ingineria Programarii

125/135

astfel scăpările de memorie (engl. „memory leaks"). Analizoarele dinamice pot localiza părţile sistemului care cauzează slabe performanţe ale acestuia şi pot detecta erori de programare (exemplu: iniţializări inutile);

• Utilitare de test: Testează module şi programe. Acestea suportă una sau mai multe din următoarele funcţii: generarea şi gestiunea datelor de test, verificarea automată a rezultatelor, diagnosticarea erorilor şi depanarea. Utilitarele generale de test pot genera un volum mare de date de intrare;

• Procesoare de text: Sunt utilizate pentru crearea documentelor; • Generatoare de documentaţie: Generează documentaţie utilizator din codul

sursă. Menţin consistenţa dintre cod şi documentaţie şi fac procesul de documentare concurent cu cel de codare. Generatoarele de cod pot include utilitare pentru generarea automată a documentaţiei;

• Utilitare pentru managementul configuraţiei: înregistrează versiunile modulelor şi fişierelor. Sunt organizate în baze de date şi controlează dezvoltarea sistemelor când multe module pot exista în diferite versiuni. Unele utilitare permit specificarea unei configuraţii (m module în n versiuni), compilare automată, linkeditare şi arhivare a acestora. Este recomandată folosirea utilitarelor pentru managementul configuraţiei când numărul de module sau de versiuni devine foarte mare.

9.6 Concluzii In concluzie, procesul de implementare este dificil de caracterizat şi de descris. Aici se

regăsesc aspecte ale analizei cerinţelor, ale specificaţiilor şi proiectării. Programatorul va trebui să facă un număr de importante compromisuri între siguranţa produsului, costul, eficienţa, timpul de execuţie, posibilitatea lui de întreţinere, etc. Sarcina cea mai dificilă a programatorului este de a realiza această pluralitate a scopurilor. Este dificil de a spune care va fi rezultatul acestor compromisuri atât timp cât produsul nu este complet şi testat. Odată ce produsul a fost finalizat, în sensul că toate părţile sale componente au fost reunite şi sistemul poate funcţiona, începe etapa următoare a procesului de dezvoltare şi anume faza testării.

Page 126: Ingineria Programarii

126/135

10 Psihologia si etica programării

10.1 Programarea ca activitate umană „ Considerăm cu prea multă uşurinţă programele ca fiind doar pentru compilare. Ar

trebui să le luam în calcul şi ca mijloace de comunicare intre noi şi ceilalţi, ca mijloace de exprimare a propriilor gânduri către nouă înşine. " (Green, 1990)

In prezent se recunoaşte importanţa nevoilor utilizatorului în momentul conceperii sistemelor interactive dar, până în 1980, aceste nevoi nu prea au fost luate în calcul. De ce ar fi de dorit introducerea comentariilor în programe? De ce ar fi mai bine să nu se folosească „go to"-uri? De ce conceptul X ar fi mai bun decât conceptul 7? De ce utilizatorii unui anumit sistem se simt pierduţi şi se enervează, în timp ce utilizatorii unui sistem similar, dar cu o interfaţă grafică diferită, dimpotrivă, lucrează cu plăcere?

Cele mai multe cărţi de programare şi inginerie software dau propriile răspunsuri acestor întrebări. De cele mai multe ori răspunsurile nu sunt deloc fondate. Argumentele aduse se bazează mai mult pe intuiţie şi presupusa experienţă a cititorului. Totuşi intuiţia nu este suficientă. Este nevoie de teorii susţinute de dovezi empirice solide şi de experimente bine puse la punct pentru a se putea găsi răspunsuri fondate la întrebări ca cele de mai sus.

Tom Love a introdus termenul de psihologie a programării pentru a arăta „comuniunea" dintre psihologie şi informatică. Psihologia programării prezintă factorii umani legaţi de conceperea şi utilizarea programelor, cum ar fi:

•uşurinţa cu care programatorii manipulează diverse construcţii ale limbajelor de programare;

•probleme legate de capacitatea de învăţare a programării; •predispoziţia spre erori si robusteţea construcţiilor unui limbaj; •tipurile de erori făcute de programatori; •uşurinţa de a utiliza aplicaţii software, de exemplu procesoarele de text, de către

neiniţiaţi; •rolul help-ului on-line. Primele cercetări empirice despre maniera în care lucrează programatorii au fost sever

criticate; nevoia unei metodologii solide pentru aceste experimente a fost subliniată în mod repetat, în ultimii ani calitatea experimentelor legate de factorii umani a crescut şi au fost formulate câteva teorii utile.

In momentul când se fac experimente, o atenţie sporită trebuie acordată aplicabilităţii ecologice (adică a relevanţei rezultatelor în cazul generalizării în situaţii reale). Aplicabilitatea ecologică trebuie luată în calcul atât atunci când e vorba de utilizarea practică a interfeţei grafice cât şi atunci când se fac studii experimentale asupra comportamentului programatorului. Factorii care trebuie luaţi în considerare sunt:

•Mulţimea subiecţilor: Diferenţele între indivizi influenţează rezultatele obţinute. Multe dintre testele aplicate programatorilor au avut drept subiect programatori fără experienţă, de exemplu studenţi. Alte experimente au arătat că aceleaşi rezultate nu sunt neapărat valabile şi în cazul programatorilor profesionişti. Asemenea diferenţe între experţi şi începători apar şi atunci când se evaluează interfeţele grafice. Diferenţele de motivare pot avea, de asemenea, un rol foarte important. Utilizatorii reali sunt, de obicei, influenţaţi de producţie. De exemplu, ei nu citesc manualele de utilizare; au lucruri mai bune de făcut;

•Contextul sistemului: Pentru a testa o anumită construcţie dintr-un limbaj, experimentatorii folosesc limbaje simplificare („limbaje jucărie", engl. „toy languages"). Intr-un caz real, caracteristicile limbajului interacţionează. Interfeţele utilizator sunt deseori testate izolat, astfel încât nu este luată în calcul consistenţa cu alte aplicaţii. Nu este foarte clar dacă rezultatele astfel obţinute pot fi transferate direct în practicile reale;

Page 127: Ingineria Programarii

127/135

•Dimensiunea problemei: Din motive practice, majoritatea testelor folosesc aplicaţii mici, iar problemele care apar sunt clare. Şi în acest caz generalizarea la aplicaţii mari şi la probleme vagi se poate fi pusă sub semnul întrebării. Această situaţie este cunoscută sub numele de „eroarea examenului şcolar";

•Contextul de lucru: In realitate oamenii sunt deseori confruntaţi cu suprapunearea mai multor sarcini (în timp ce încearcă să găsească o eroare, sună telefonul).

Programatorii scriu programe. Ei exprimă algoritmi folosind construcţii proprii unui limbaj de programare. Pentru aceasta, trebuie să cunoască sintaxa şi semantica acestor construcţii. Mai trebuie să ştie cum sa construiască programe logice din blocurile de construcţie pe care le au la dispoziţie. Cu cât ştim mai bine să folosim un limbaj de programare, cu atât mai bine vom reuşi să îndeplinim aceste sarcini.

De obicei, simpla scriere de cod nu este singura sarcina pe care trebuie să o îndeplinească programatorul. Acesta trebuie să conceapă programul, să-1 testeze, documenteze şi să ofere suport. Programatorul trebuie să înţeleagă şi programele scrise de altcineva. De multe ori programele sunt mai mult citite decât folosite practic.

Un model descriptiv despre modul în care trebuie să-şi desfăşoare lucrul programatorii ar trebui să includă toate aceste activităţi prezentate mai sus.

Un model cognitiv al memorie umane distinge trei tipuri de memorie: memoria senzorială, de scurtă durată şi de lungă durată. Memoria senzorială reţine pentru un timp foarte scurt informaţiile oferite de sistemul perceptiv, în vederea prelucrării acestora. Informaţiile cu un anumit grad de relevanţă sunt păstrate apoi în memoria de scurtă durată. Aceasta are o capacitate limitată. Miller (1956) estimează dimensiunea ei la aproximativ 7 unităţi. Nu cunoaştem cu precizie dimensiunea exactă, dar este important de reţinut este că are o capacitate limitată.

Entităţile din memoria de scurtă durată nu necesită conexiuni cu informaţii elementare. Oamenii combină informaţiile în unităţi cât mai mari. Acest proces este numit chunking, iar unităţile de informaţie sunt numite chunks (bucăţi, porţiuni). De exemplu, secvenţa de cifre „234567" poate ocupa 5 intrări în memoria de scurtă durată. Totuşi, dacă este recunoscută ca fiind un număr de telefon, este codată şi ocupă o singură intrare. Astfel intrările în memorie de scurtă durată pot fi văzute ca etichete pentru o anumită informaţie, ca o cifră, un număr de telefon sau o rutină de sortare. Când informaţia este prelucrată şi manipulată pentru executarea unor operaţii, memoria de scurtă durată se comportă ca o memorie activă, ca şi registrele unui calculator.

Informaţiile relevante pe termen lung sunt păstrate în memoria de lungă durată. Având la bază acest model, apar două întrebări importante: ce tip de cunoştinţe posedă

programatorul în memoria de lungă durată şi ce tip de procese cognitive apar în vederea găsirii soluţiei în memoria sa activă? Programatorii profesionişti deţin un vast şi complex volum de informaţii în memoria de lungă durată. Acestea reprezintă concepte generale, nefiind direct legate de un anumit limbaj de programare şi privesc atât concepte de nivel scăzut (de exemplu atribuirea unei variabile), cât şi concepte de nivel înalt (de exemplu un algoritm de sortare). Acestea sunt cunoştinţe semantice.

Memoria de lungă durată mai conţine şi cunoştinţe sintactice, ca de exemplu formatul construcţiei „while" din C. Cunoştinţele sintactice sunt arbitrare şi deci uşor de uitat. E mai uşor de învăţat o structură sintactică atunci când structura semantică corespunzătoare este deja cunoscută. Aşa se explică faptul că învăţarea unui prim limbaj de programare se dovedeşte mai dificilă decât învăţarea următorului (sau următoarelor). După un timp este necesară numai stăpânirea unei sintaxe noi. Desigur, acest lucru nu se întâmplă dacă noul limbaj are o structură semantică diferită (de exemplu un limbaj declarativ cum ar fi CLIPS faţă de un limbaj procedural ca C-ul).

Page 128: Ingineria Programarii

128/135

Cunoaşterea semantică trebuie învăţată şi asimilată. Cunoştinţele sintactice pot fi stăpânite prin simple exerciţii. Aceste două tipuri de cunoştinţe nu sunt integrate împreună în memorie. De exemplu, înţelegerea sensului unei sarcini nu este legată neapărat de modul în care aceasta este transpusă într-un anumit limbaj de programare.

Şi informaţiile din memorie de lungă durată pot fi descompuse din punctul de vedere al domeniilor la care se referă. Pe lângă cunoştinţe informatice, un programator cunoaşte, de asemenea, unul sau mai multe domenii de aplicabilitate. Când avem de-a face cu probleme concrete, cunoştinţele din diferite domenii sunt folosite pentru a obţine un rezultat.

Pentru a finaliza acest model, trebuie să investigăm procesul rezolvării problemelor asociat cu sarcina programatorului. Deosebim aici patru etape în rezolvarea unei probleme:

•înţelegerea problemei; •realizarea unui plan, descoperirea unei strategii de obţinere a soluţiei; •executarea planului; •verificarea rezultatelor. Dacă unui programator i se dă o problemă, presupunem că acesta foloseşte mai întâi

memoria de scurtă durată pentru a reţine specificaţiile. Când problema este analizată, sunt folosite şi informaţii generale din memoria de lungă durată. Aceşti sunt primii paşi spre obţinerea soluţiei.

In a doua etapă se utilizează tehnici de rafinare pas cu pas şi proiectarea top-down sunt susţinute. Folosind rafinarea pas cu pas, este formulată mai întâi o primă soluţie, pe plan general. Apoi, problema este împărţită în una sau mai multe subprobleme, pentru care se caută soluţii.

Devierea de la proiectarea top-down este o consecinţă a proiectării defectuoase. Totuşi, o descompunere top-down absolută este doar un caz particular, atunci când soluţia se cunoaşte precis. In practică, programatorii trec repede de la un nivel de abstractizare la altul. Dacă recunosc o soluţie parţială a unei părţi a problemei, ei încep un fază de proiectare detaliată pentru implementarea soluţiei respective. Invers, dacă dezvoltarea unei soluţii conduce la descoperirea unor cerinţe suplimentare, acestea pot deveni centrul atenţiei.

O teorie interesantă (Soloway, 1986) susţine că programatorii rezolvă problemele folosind planuri de programare, fragmente de program care corespund unor acţiuni stereotipe şi reguli care descriu convenţii de programare. De exemplu, pentru a calcula suma unui şir de numere, progrmatorul utilizează un contor-sumă care este iniţializat cu O şi la care, în cadrul buclei, se adună următoarea val orare din şir. Planurile de programare sunt legate de conceptul de jalon. Jaloanele sunt cunoştinţe care indică prezenţa unei anumite structuri sau operaţii. De exemplu, o idee fundamentală în rutinele de sortare este interschimbarea a două valori. Dacă ni se prezintă un program care conţine operaţii de interschimbare, imediat ne gândim că ar putea fi un program de sortare.

Experimentele efectuate (Wiedenbeck, 1991) au arătat că programele de sortare care conţineau operaţii de interschimbare explicite sunt mult mai uşor recunoscute ca atare decât programele în care interschimbarea este mascată. S-a constatat că prezenţa jaloanelor false induc în eroare programatorii. Programatorii experimentaţi se bazează mai mult pe prezenţa jaloanelor decât începătorii. Acest fapt reflectă asocierea mai puternică a jalonului cu un anumit tip de program, datorită experienţei. De asemenea, experţii încearcă să „recunoască" părţi mari din program, să înţeleagă ideea din program, fără să ia în calcul detaliile.

10.1.1 Scrierea programelor Aproape toate textele despre programare conţin sfaturi menite să crească înţelegerea

programelor. Aceste sfaturi privesc probleme ca: adăugarea comentariilor, numele sugestive ale variabilelor, alinierea etc. Ne putem întreba în ce circumstanţe aceste sfaturi vor produce rezultate mai bune. Ce ne interesează este efectul acestor indicaţii perceptive în înţelegerea

Page 129: Ingineria Programarii

129/135

programelor. Când studiem codul unui program, construim o structură semantică internă care să corespundă programului. Elementele menţionate mai sus uşurează procesul.

10.1.1.1 Comentariile Experţii nu sunt întru totul de acord cu felul si volumul de comentarii ce trebuie incluse

în codul sursă. Orice carte de programare ne va spune să documentăm programele create, deşi acestea pot avea şi efecte negative. De multe ori comentariile nu sunt rescrise în momentul modificării codului. Astfel, comentariile mai vechi pot să inducă în eroare cititorul.

Shneiderman (1980) prezintă un experiment în care 62 de studenţi au studiat un program FORTRAN lung de 26 de linii. Un grup (H) a primit programul având comentarii de nivel înalt la începutul codului, care descriau funcţionalitatea programului. Cel de-al doilea grup (L) a primit un cod având comentarii detaliate pentru instrucţiunile folosite. Ambelor grupuri li s-a cerut să facă mici schimbări asupra programului. De asemenea, li s-a mai dat şi un test de memorie: cât de multe linii pot fi reproduse. La ambele teste, grupul H a obţinut punctaj mai mare decât L.

Woodfield (1981) a studiat legătura între modularitate şi comentarii. Pentru acest experiment au fost testaţi, în mare parte, programatori experimentaţi. Woodfield a folosit patru variante ale unui program de 100 de linii: o variantă monolitică formată dintr-un singur modul, două variante folosind descompunere funcţională, şi o variantă bazată pe abstractizare. Toate versiunile având comentarii au obţinut un scor mai bun decât cele fără. Totuşi, pentru versiunea monolitică, diferenţele au fost mici. Versiunea abstractă a obţinut performanţele cele mai bune.

Comentariile nu sunt stocate în structura semantică internă construită. Ele doar conduc la o obţinere mai uşoară a acesteia. Programatorii începători sunt mai atenţi la comentarii decât cei experimentaţi. Pentru programe scurte, profesioniştii nu au nevoie de comentarii, mai ales dacă structura programului poate fi obţinută prin alte mijloace. De exemplu, se pot folosi nume mnemonice, care sunt suficiente pentru a determina structura semantică a programului. Când numele nu au înţeles, comentariile reprezintă singurul ajutor. Combinaţia între nume fără sens şi programe monolitice poate explica dificultăţile pe care le întâmpină programatorii.

Comentariile care explică funcţionalitatea sunt preferate comentariilor de nivel scăzut. Comentariile nu trebuie să imite codul, ca în exemplul următor:

x = O; // x devine O Comentariile corecte ajută la construirea structurii semantice interne. Când scriem

comentarii, trebuie să folosim terminologia domeniului, pentru a micşora diferenţa dintre problemă şi domeniul programului. De exemplu, este mai bine să folosim

// Caută studentul cu media cea mai mare decât // Caută cea mai mare valoare din tabel

10.1.1.2 Numele variabilelor Dacă ne confruntăm cu un program în care variabilele sunt numite P, Q, şi R, vom

întâmpina dificultăţi în a înţelege semnificaţia lor. Pe de altă parte, mnemonice de forma cont sau factură reflectă un anumit rol semantic şi determină o legătură directă către ceea ce reprezintă ele. Aşadar, mnemonicele facilitează procesul de înţelegere. Totuşi, dacă cel care citeşte programul cunoaşte deja algoritmul, numele variabilelor nu mai prezintă o importanţă deosebită.

Page 130: Ingineria Programarii

130/135

10.1.1.3 Indentaţia Indentaţia (engl. spaţiere, zimţuire) se referă la determinarea distanţei faţă de margine

(în caractere albe) a liniilor de program, astfel încât să poată fi puse în evidenţă anumite structuri ale programului.

Scopul indentaţiei este creşterea lizibilităţii codului. Stilul indentaţiei poate diferi considerabil, chiar dacă este folosit acelaşi limbaj de programare. Mulţi programatori şi-au dezvoltat propriul stil de aliniere. Ei folosesc acest stil când citesc programe sau când îşi construiesc structura internă corespunzătoare. Când sunt confruntaţi cu un program cu o indentaţie diferită, forma vizuală diferită poate stânjeni, cel puţin la început, înţelegerea codului.

In prezent există şi alte mijloace de punere în evidenţă a structurii unui program. De exemplu, cuvintele cheie pot fi scrise cu litere aldine iar comentariile cu litere cursive.

10.1.2 Concepte ale limbajelor de programare în paragraful anterior am discutat câteva probleme de notaţie independente de limbajul

de progrmare folosit. Acum vom vorbi despre concepte specifice limbajelor de programare. O ipoteză din lingvistică afirmă că modul nostru de gândire este îngrădit de limbajul în

care ne exprimăm. Reciproc, modul nostru de a gândi limitează utilizarea limbajului. Construcţiile limbajului ar trebui, de asemenea, să fie adaptate la sarcinile care trebuie îndeplinite de către programator. De exemplu primele limbaje de programare ofereau doar o singură construcţie pentru iteraţie. Limbajele recente oferă mai multe variante: „for", „while". Totuşi resimţim în continuare nevoia de a ieşi dintr-o buclă de la mijloc, deoarece acest lucru corespunde aproape perfect modului nostru de gândire. Limbajele de programare ar trebui concepute în aşa fel încât să faciliteze o exprimare cât mai naturală a algoritmilor.

Cercetările experimentale referitoare la problemele de notaţie s-au ocupat şi de structurile decizionale. Ele au arătat că sunt preferate construcţiile „if-then-else" faţă de construcţiile „if-goto", deoarece sunt mai uşor de folosit. Construcţiile din prima categorie sunt şi mai uşor de indentat, şi astfel codul devine mai uşor de citit şi înţeles. De asemenea, fiind construcţii de nivel mai înalt, ele sunt mai puţin complexe.

Când se pune problema depanării codului, prezenţa instrucţiunilor „goto" creşte dificultatea procesului şi a timpul necesar realizării sale. Un experiment realizat de Benander (1990) a evidenţiat următoarele concluzii:

•Programele cu rezultate eronate aveau de două ori mai multe „goto"-uri decât programele cu rezultate corecte.

•Programele cu „goto"-uri aveau o structură cu mult mai proastă decât cele fără „goto"; •Timpul mediu pentru depanarea programelor cu „goto"-uri era mai mare decât cel al

programelor fără „goto". Acest tip de experiment demonstrează că alegerea structurilor de control, structurate sau

nestructurate, are o importanţă majoră. Această experienţă susţine anecdota care spune că numărul de erori pe care le are un program este direct proporţional cu numărul de „goto"-uri. Totuşi, un program fără goto-uri nu este obligatoriu fără erori.

Un alt rezultat important este faptul că utilizarea structurilor şi claselor creşte gradul de abstractizare al programului, cu efecte benefice asupra înţelegerii. Astfel se explică şi tendinţa de înlocuire a programării structurate cu programarea orientată obiect.

în timpul proiectării, problema este descompusă în module. Pentru programe de mici dimensiuni, avantajul modularizării nu este evident. Totuşi, atunci când dimensiunea programelor creşte, tehnica modularizării devine foarte importantă, iar modificările sunt mult mai rapide decât în cazul programelor monolitice echivalente.

Page 131: Ingineria Programarii

131/135

10.1.3 Interfaţa cu utilizatorul Un sistem în care interacţiunile apar la un nivel inteligibil pentru utilizator va fi acceptat

mai repede decât un sistem în care acest lucru nu se întâmplă. Dacă sistemul este disponibil la intervale neregulate sau dă mesaje de eroare incomprehensibile, este foarte posibil ca utilizatorii (mai ales nespecialişti) să nu-1 accepte. De aceea, în multe cazuri, interfaţa cu utilizatorul necesită mai mult de 30% din cod, deoarece este un factor critic pentru succesul sau eşecul sistemului respectiv.

Există trei factori care influenţează interacţiunea dintre utilizatorul uman şi calculator: •Modelul mental al utilizatorului: Este modelul maşinii pe care îl creează utilizatorul, pe

baza educaţiei şi cunoştinţelor despre sistem sau domeniul aplicaţiei. In timpul interacţiunii cu sistemul, acest model este utilizat pentru a planifica acţiunile, pentru a prezice şi interpreta reacţiile sistemului. Modelul mental reflectă înţelegerea utilizatorului asupra a ceea ce conţine sistemul, cum funcţionează acesta şi de ce. Modelul este determinat iniţial de meta-comunicare, adică instruire şi documentare. El evoluează în timp, pe măsură ce utilizatorul dobândeşte o înţelegere din ce în ce mai exactă asupra sistemului;

•Imaginea sistemului: Include toate elementele sistemului cu care vine în contact utilizatorul. Modelul mental al utilizatorului se bazează în cea mai mare parte pe această imagine. Ea poate include stilul de interacţiune, forma şi conţinutul schimbului informaţional, chiar şi aspectul fizic al sistemului, dispozitivele externe conectate etc.;

•Modelul conceptual: Este modelul precis din punct de vedere tehnic creat de proiectanţi, o reprezentare completă a sistemului asupra tuturor aspectelor legate de interacţiunea cu utilizatorul. Pe baza acestui model reacţionează sistemul la acţiunile utilizatorului.

Problema fundamentală a realizării unei interfeţe între om şi calculator este apropierea cât mai mare a modelului conceptual de modelul mental al utilizatorului.

Utilizatorul unui sistem interactiv trebuie să îndeplinească anumite sarcini: trimiterea unui email, tehnoredactarea unui document etc. Toate aceste sarcini au o anumită structură, prezentă în memoria umană. Interacţiunea om-calculator trebuie să aibă aceeaşi structură, pe cât posibil. Dacă o anumită acţiune este percepută ca o singură unitate conceptuală în domeniul sarcinii, este derutantă acţionarea unui întreg şir de comenzi pentru a realiza efectul dorit. De asemenea, sarcinile care sunt apropiate în domeniul problemei (adică sunt conectate semantic) trebuie să fie apropiate şi în interacţiunea cu sistemul computerizat.

Inginerii programatori sunt înclinaţi să modeleze interfaţa cu utilizatorul după structura mecanismului de implementare şi nu după structura domeniului problemei. De exemplu, un program poate solicita introducerea secvenţei „ T10 2" pentru a obţine IO2, doar pentru că sistemul recunoaşte şi prelucrează mai uşor prima variantă, în acelaşi fel, documentaţia urmăreşte de multe ori detaliile şi modelele de implementare, iar mesajele de eroare sunt formulate în termeni care reflectă punctul de vedere al programatorului şi nu problemele utilizatorului. Pe lângă cunoştinţele specifice sarcinii, cunoştinţele generale joacă de asemenea un rol important în construirea modelului mental. Dacă o combinaţie de taste „CTRL+J" într-un editor de text mută cursorul cu o linie mai jos, utilizatorul poate deduce că apăsarea „CTRL+S" mută cursorul cu o linie mai sus. Dacă însă această combinaţie are ca efect ştergerea fişierului, acest lucru va produce confuzie şi frustrare.

Noile cunoştinţe sunt integrate cunoştinţelor existente. Limitările memoriei de scurtă durată şi atenţiei joacă şi ele un rol. Secvenţele lungi de comenzi, meniurile cu un mare număr de elemente, nesiguranţa asupra „locului unde ne aflăm" au ca efect stânjenirea interacţiunii. Aceste aspecte sunt cunoscute sub denumirea de încărcare cognitivă. Pe măsură ce încărcarea cognitivă creşte, sistemul devine mai greu de învăţat, utilizatorul oboseşte mai repede şi începe să facă mai multe greşeli.

Page 132: Ingineria Programarii

132/135

De fiecare dată când omul începe să înveţe ceva, el traversează o curbă de învăţare. Acelaşi fenomen apare şi în cazul unui program. Viteza cu care utilizatorul traversează curba de învăţare corespunzătoare programului şi devine expert este o măsură importantă a complexităţii acelui program. Această măsură este în acelaşi timp un indicator mai bun al utilizabilităţii sistemului decât noţiuni subiective precum „interfaţă prietenoasă".

O altă problemă este consistenţa interfeţei. Aceasta nu trebuie dusă la extrem, în unele cazuri, trebuie realizat un compromis între consistenţă şi funcţionalitate. Să considerăm un exemplu în care consistenţa a fost sacrificată în favoarea unei scheme care reflectă mai bine sarcinile utilizatorului. In figura l, sunt prezentate două configuraţii posibile pentru tastele săgeţi. Modelul stea este un model consistent. Totuşi, studiile efectuate au arătat că T-ul întors este cea mai utilizabilă configuraţie. Creatorii de jocuri pe calculator cunosc acest lucru de mult timp. De aceea, şi în jocurile mai vechi tastele folosite pentru direcţii erau, de exemplu, W, A, S, D şi nu W, A, Z, S.

Figura 1. Dispunerea tastelor săgeţi Pe măsură ce utilizatorul traversează curba de învăţare, mai devreme sau mai târziu el

atinge un nivel acceptabil pentru el la momentul respectiv, deşi nu este încă expert. Acest fenomen apare la un mare număr de aplicaţii. De exemplu, majoritatea utilizatorilor de Linux cunosc bine numai o submulţime a comenzilor disponibile. Despre o altă submulţime de comenzi au doar o idee vagă, însă nu le cunosc exact funcţionalităţile. Despre restul de comenzi, utilizatorii respectivi nu au auzit.

Prin instruire şi documentare, utilizatorul îşi construieşte mai întâi o imagine despre facilităţile oferite de sistem şi apoi încearcă să realizeze ceva practic cât mai devreme, înainte să aibă o înţelegere deplină asupra sistemului. Dacă nu beneficiază de ajutor în continuare, sunt mari şansele să nu înţeleagă niciodată noţiunile respective.

Pentru submulţimea de comenzi abia cunoscute, se recomandă un sistem de help pasiv, on-line sau off-line (sub formă de documentaţie). Pentru comenzile necunoscute de utilizator, se recomandă un help activ, care să-1 îndrume pe utilizator şi să-i explice noile noţiuni necunoscute întâlnite.

Pentru realizarea interfeţei grafice cu utilizatorul, se recomandă următoarele principii de proiectare: a) Dialog simplu şi natural: Dialogurile nu trebuie să conţină informaţii irelevante. Fiecare unitate suplimentară de informaţie concurează cu alte unităţi relevante pentru atragerea atenţiei. Informaţiile trebuie să apară într-o ordine logică şi naturală;

b) Limbaj potrivit utilizatorului: Dialogurile trebuie să utilizeze concepte şi fraze familiare utilizatorului. Dialogul nu trebuie exprimat în termeni legaţi de calculatoare;

c) Minimizarea încărcării mnezice: Utilizatorul nu trebuie să fie nevoit să reţină informaţii dintr-o parte a dialogului în alta. Trebuie să existe modalităţi simple de a descoperi ce trebuie făcut în continuare, unde se poate întoarce şi unde să primească instrucţiuni generale de utilizare;

d) Consistenţă: Utilizatorii nu trebuie să se întrebe dacă anumite cuvinte diferite înseamnă acelaşi lucru;

e) Feedback: Sistemul trebuie să informeze permanent utilizatorul asupra a ceea ce se întâmplă. Chiar dacă unele acţiuni necesită mai mult timp, utilizatorul trebuie să dispună de un indicator (de exemplu procentual) care să-1 informeze despre progresul acţiunii;

Page 133: Ingineria Programarii

133/135

f) Ieşiri marcate clar: Utilizatorii fac greşeli şi pot alege funcţii nepotrivite. Trebuie să existe o modalitate prin care aceştia să poată ieşi din starea nedorită fără a fi nevoie de un lung dialog cu sistemul;

g) Scurtături (engl. „shortcuts"): Utilizatorii începători nu sunt incomodaţi de dialogurile ample, deoarece se simt în siguranţă şi astfel pot învăţa mai uşor sistemul. Utilizatorii experimentaţi nu mai au nevoie de aşa ceva, se simt chiar incomodaţi în astfel de situaţii şi de aceea sistemul trebuie să le permită să sară peste paşii bine cunoscuţi;

h) Mesaje de eroare potrivite: Mesajele de eroare trebuie exprimate în limbaj natural şi nu trebuie să se refere la starea internă a sistemului. Ele trebuie să specifice clar eroare şi să propună soluţii de rezolvare;

i) Prevenirea erorilor: Este mai bine ca sistemul să prevină eventualele erori. De exemplu, dacă utilizatorul doreşte să părăsească programul, sistemul trebuie să-1 întrebe dacă vrea saşi salveze mai întâi datele.

10.2 Etica programării Programarea este rezultatul unui comportament uman intenţionat, realizat de persoane

cu anumite valori (şi deci etică). Tehnologia nu este neutră din punctul de vedere al valorilor. Chiar şi scrierea unui program simplu presupune, într-un anumit fel, valorile programatorului. Mai mult, programele sunt mijlocare de comunicare, codul sursă este o formă de exprimare. In consecinţă, prin codul scris, programatorii au ocazia să-şi comunice valorile.

10.2.1 Codul etic IEEE Un exemplu de cod etic este cel al IEEE (The Institute of Electrical and Electronics

Engineers, 1990), care conţine 10 puncte: a) acceptarea responsabilităţii de luare a deciziilor inginereşti în conformitate cu

siguranţa, sănătatea şi bunăstarea publică şi dezvăluirea promptă a factorilor care ar putea pune în pericol oamenii şi mediul;

b) evitarea conflictelor de interese reale sau percepute ori de câte ori acest lucru este posibil şi dezvăluirea lor către părţile implicate atunci când asemenea conflicte există;

c) corectitudinea şi realismul cerinţelor sau estimărilor bazate pe date disponibile; d) respingerea mitei sub toate formele ei; e) perfecţionarea înţelegerii tehnologiei, aplicaţiilor sale adecvate şi a consecinţelor

potenţiale; f) menţinerea sau perfecţionarea propriei competenţe tehnice şi asumarea răspunderii pentru sarcinile care îi privesc pe alţii numai în cazul calificării prin instruire sau experienţă sau după dezvăluirea completă a limitărilor legate de acestea;

g) căutarea, acceptarea şi acordarea de critici oneste ale lucrărilor tehnice, recunoaşterea şi corectarea erorilor şi creditarea adecvată a contribuţiilor altora;

h) tratarea nepărtinitoare a tuturor persoanelor, fără a ţine seama de factori precum rasă, religie,

sex, handicap, vârstă sau naţionalitate; i) evitarea defavorizării altora, a proprietăţii, reputaţiei sau serviciului lor prin acţiuni incorecte

sau răuvoitoare; j) asistenţa acordată colegilor în dezvoltarea lor profesională şi în respectarea prezentului cod

de etică.

10.2.2 Legea drepturilor de autor In România, legea drepturilor de autor (numărul 8 din 14 martie 1996) recunoaşte şi

garantează dreptul de autor asupra operelor de creaţie intelectuală, literară, artistică sau ştiinţifică, incluzând şi programele de calculator.

Page 134: Ingineria Programarii

134/135

Ca şi alte opere protejate, precum scrierile literare, operele de artă plastică, operele cinematografice, programele pentru calculator sunt opere de creaţie intelectuală, care nu sunt rezultate ale unei munci mecanice, executate cu mijloace tehnice obişnuite sau cu ajutorul unor cunoştinţe ce stau la îndemâna oricui. Programele de calculator sunt opere de creaţie intelectuală, a căror valoare nu este dată de suportul material pe care programul în sine este fixat şi nici de munca depusă pentru realizarea acestor suporturi (de exemplu dischete, CDROM-urile). Valoarea protejată de lege este chiar creaţia intelectuală originală concretizată în programul pentru calculator şi în materialele asociate, precum manualele de utilizare.

Legea română protejează programele de calculator independent de valoarea şi destinaţia lor concretă. Protecţia acordată de lege nu se opreşte numai la obiectul dreptului de autor. Este protejat deopotrivă şi titularul acestui drept, autorul programului de calculator respectiv. Autorul programului are dreptul de a decide dacă, în ce mod şi când va fi utilizată opera sa, inclusiv de a consimţi la utilizarea operei de către alţii. Acest drept se referă la autorizaţia de reproducere integrală sau parţială a programului de calculator şi la difuzarea acestuia.

Consimţământul pe care titularul dreptului de autor îl dă unei persoane pentru a putea reproduce, folosi, difuza sau importa copii ale unui program de calculator, se concretizează în practică în licenţe. Ele sunt documentele care probează că reproducerea, folosirea, difuzarea unui program de calculator de către o anumită persoană s-a realizat cu consimţământul autorului.

Lipsa licenţelor echivalează cu lipsa autorizării din partea autorului. Importanţa acestor licenţe este evidentă, întrucât desfăşurarea activităţilor menţionate fără aceste licenţe reprezintă infracţiuni, pedepsite cu închisoare de la 3 luni la 3 ani sau cu amendă de la 700.000 la 7 milioane de lei, dacă nu constituie infracţiune mai gravă.

10.2.3 Licenţa publică generală GNU Licenţele majorităţii programelor sunt concepute pentru a priva utilizatorul de libertatea

de a modifica şi distribui programele respective. In contrast, intenţia Licenţei Publice Generale GNU este de a garanta libertatea de a distribui şi modifica programele şi de a se asigura că programele sunt libere pentru toţi utilizatorii. Libertatea programelor nu implică neapărat absenţa costului. Licenţa GNU este concepută pentru a garanta libertatea de a distribui copii ale programelor libere (şi de a oferi acest serviciu contra cost, dacă se doreşte acest lucru doriţi), de a obţine codul sursă, de a schimba programul sau a folosi porţiuni din el în noi programe libere.

Pentru protecţia autorilor, licenţa asigură faptul că nu există nici un fel de garanţie pentru un program liber. Dacă programul este modificat de altcineva şi distribuit mai departe, beneficiarii programului trebuie să ştie că ceea ce au nu este originalul, în aşa fel încât nici o problemă introdusă de altcineva nu va avea un efect negativ asupra reputaţiei autorilor iniţiali.

Orice program liber este în mod constant ameninţat de patentele software. Licenţa GNU doreşte evitarea pericolului ca cei ce redistribuie programe libere să obţină patente, practic transformând programul într-unul aflat sub controlul total al persoanei sau instituţiei ce deţine patentul (engl. „proprietary"). Pentru a preveni această situaţie, licenţa GNU consideră că orice patent trebuie acordat fie în aşa fel încât să poată fi folosit gratuit şi fără restricţii de oricine, fie deloc.

Dintre termenii şi condiţiile licenţei GNU, menţionăm: •Puteţi copia şi distribui copii nemodificate ale codului sursă al Programului în forma în

care îl primiţi, prin orice mediu, cu condiţia să specificaţi vizibil pe fiecare copie autorul şi lipsa oricărei garanţii, să păstraţi intacte toate notele referitoare la această Licenţă şi la absenta oricărei garanţii şi să distribuiţi o copie a acestei Licenţe cu fiecare copie a Programului.

Page 135: Ingineria Programarii

135/135

Puteţi pretinde o retribuţie financiară pentru actul fizic de transfer al unei copii, şi puteţi oferi garanţie contra cost.

•Puteţi efectua modificări asupra copiilor Programului (sau asupra oricăror porţiuni ale sale), creând astfel un "proiect bazat pe Program". Copierea şi distribuirea unor asemenea modificări sau proiecte se pot face conform termenilor secţiunii precedente (1), doar dacă toate condiţiile următoarele sunt îndeplini te:

o Toate fişierele modificate trebuie să conţină note foarte vizibile menţionând faptul că dumneavoastră le-aţi modificat, precum şi data fiecărei modificări;

o Orice proiect pe care îl distribuiţi sau publicaţi, care în întregime sau în parte conţine sau este derivat din Program (sau orice parte a acestuia), trebuie să poată fi folosit de oricine, gratuit şi în întregime, în termenii acestei Licenţe;

o Daca programul modificat citeşte comenzi în mod interactiv, trebuie să îl modificaţi în aşa fel încât atunci când este pornit în mod interactiv să afişeze un mesaj referitor la drepturile de autor precum şi o notă menţionând lipsa oricărei garanţii (sau să menţioneze faptul că dumneavoastră oferiţi o garanţie).

•Puteţi copia şi distribui Programul (sau un proiect bazat pe el, conform Secţiunii 2) în format obiect sau executabil conform termenilor Secţiunilor l şi 2 de mai sus, cu condiţia să îndepliniţi una dintre condiţiile de mai jos:

o Să îl oferiţi însoţit de codul sursă corespunzător, în format citibil de către maşină, care trebuie să fie distribuit în termenii Secţiunilor l şi 2 de mai sus pe un mediu de distribuţie uzual transportului de software; sau

o Să îl oferiţi însoţit de o ofertă scrisă, (validă pentru cel puţin trei ani, pentru o taxă care să nu depăşească costul fizic al efectuării distribuţiei sursei), de a oferi o copie completă, în format citibil de către maşină, a codului sursă, distribuit în termenii Secţiunilor l şi 2 de mai sus, pe un mediu de distribuţie uzual transportului de software; sau

o Să îl oferiţi însoţit de informaţia pe care aţi primit-o referitoare la oferta de a distribui codul sursa corespunzător. (Această alternativă este permisă numai pentru distribuiri necomerciale şi doar dacă aţi primit programul în format obiect sau executabil împreună cu această ofertă, în conformitate cu Sub secţiunea b de mai sus.)

•Deoarece programul este oferit sub o licenţă ce nu implică nici un cost, nu există nici o garanţie pentru program, în măsura permisă de legile ce se aplică. Exceptând situaţiile unde este specificat altfel în scris, deţinătorii drepturilor de autor şi/sau alte părţi implicate oferă programul „în forma existentă" fără nici o garanţie de nici un fel, explicită sau implicită, incluzând, dar fără a fi limitată la, garanţii implicite de vandabilitate şi conformitate unui anumit scop. Vă asumaţi în întregime riscul în ceea ce priveşte calitatea şi performanţa acestui program, în cazul în care programul se dovedeşte a fi defect, vă asumaţi în întregime costul tuturor serviciilor, reparaţiilor şi corecţiilor necesare.

10.3 Concluzii In acest curs s-a urmărit trecerea în revistă a unor probleme de psihologie cu

aplicabilitate în domeniul programării, care pot determina eficientizarea scrierii codului, precum şi realizarea unor interfeţe cu utilizatorul uşor de învăţat şi de folosit. S-a insistat apoi asupra eticii programării, detaliindu-se un cod etic şi, în final, s-au prezentat unele chestiuni de ordin legal, privind drepturile de autor asupra programelor realizate.