Sunteți pe pagina 1din 145

Ingineria programării

Cursul 1

Fazele dezvoltării unui produs software

1. Ce este ingineria programării?


2. Fazele ingineriei programării
2.1. Faza de analiză
2.2. Faza de proiectare
2.3. Faza de implementare
2.4. Faza de testare
3. Concluzii

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-l verifica. De asemenea,
studii geologice, de fizică a pământului sau meteorologie vor fi obligatorii. Vor fi folosite cele mai
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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ă:

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

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

2
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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 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 3 I = 1.3'; instrucţiunea corectă în
limbajul FORTRAN ar fi trebuit să conţină virgulă în loc de punct;
• În 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.

3
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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.

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.

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

4
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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.
În 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;
• 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ă. În 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. În 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ă

5
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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.

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.

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.
În 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. În 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 asemea în fazele
de implementare şi respectiv testare. Însă 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

6
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

patch-urile pentru eventualele erori erau imposibile. Astfel, au fost necesare teste cu acoperire de
100%. Codul propriu-zis al BIOS-ului era destul de mic, câteva mii de linii. Însă 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! În final, o acoperire de 100% a fost atinsă, dar cu un cost foarte ridicat. O soluţie
mai ieftină a fost înscrierea BIOS-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.
În 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.

2.3. Faza de implementare

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

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.
În 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ă.

7
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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.
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. În trecut, aceste scripturi erau create manual. În 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

8
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

accesului la resurse critice. Mai apar şi probleme de memorie, când un test îşi alocă memorie şi apoi
nu o mai dezalocă. Testul pare să funcţioneze corect, însă după ce este rulat de mai multe ori,
memoria disponibilă se reduce iar sistemul cade.

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.

9
Ingineria programării
Cursurile 2-3

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

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-l 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
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

testare sau conflicte între anumite componente (de exemplu, conflicte de partajare a
resurselor). S-a 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. 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 incrementele iterative ale metodologiei ciclice.

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 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.
2
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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 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. 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. În 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. În 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. Metodologia ciclică

3
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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ă 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. În 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.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ă.

Figura 3. Metodologia hibridă ecluză

4
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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ă. În funcţie de dimensiunea echipelor, mai multe sarcini pot fi realizate în
paralel. Sarcinile rămase, de prioritate minimă, sunt păstrate pentru 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.

5
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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. În produsul final nu se acceptă erori critice.

Avantaje

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.

3. Metodologii concrete
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. Într-o variantă
detaliată, metodologia cascadă cuprinde următoarele etape:

6
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Figura 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ă.
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 l-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 i care sunt descoperite la pasul i + 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
7
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

documentaţii neadecvate deoarece schimbările intervenite în iteraţiile frecvente nu sunt actualizate


în toate documentele produse.

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

În 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);
• planificarea următorului stadiu: termenele limită, resurse umane, revizuirea stării
proiectului.

8
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

Figura 5. Metodologia spirală

Ciclul 1 – 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

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.

9
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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.

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. Însă 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;


• 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;
10
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

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.

3.6. Metode formale

În acest model de dezvoltare, sunt folosite formalismul şi rigoarea matematicii. În prima


fază este construită o specificaţie în limbaj matematic. Apoi, această specificaţie este transformată
în programe, de obicei printr-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;
• verificarea consistenţei şi testarea prin metode similare demonstrării automate de teoreme.

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.

11
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Datorită acestor caracteristici, metodele formale sunt potrivite în special pentru sisteme cu
cerinţe critice.
Există mai multe limbaje pentru specificarea formală a funcţionalităţii programelor: Z, CSP
(Communicating Sequential Processes), VDM (Vienna Development Method), Larch, FDM
(Formal Development Methodology) etc.
În continuare, vom exemplifica câteva funcţii simple de lucru cu o bază de date cu datele de
naştere ale unor persoane, descrisă în limbajul Z (Spivey, 1989):

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

12
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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ă;
• 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.

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

13
Ingineria programării
Cursul 4

Managementul unui proiect software

1. Funcţiile managementului
1.1. Planificarea
1.2. Organizarea
1.3. Selecţia de personal
1.4. Conducerea
1.5. Controlul
2. Managementul software
2.1. Planificarea proiectului
2.2. Controlul proiectului
3. Managementul configuraţiei
4. Managementul echipei
4.1. Managementul forţei de muncă
4.2. Organizarea echipei
4.2.1. Organizarea ierarhică
4.2.2. Organizarea matrice
4.2.3. Echipa programatorului şef
4.2.4. Principii generale de organizare a unei echipe
5. Concluzii

1. Funcţiile managementului
Din punct de vedere organizaţional, managementul reprezintă îndeplinirea unor obiective
prin intermediul oamenilor şi al altor tipuri de resurse. O examinare mai atentă a principiilor de
management confirmă faptul că acesta se referă de asemenea la procesul de stabilire şi atingere a
scopurilor prin cinci funcţii de bază (planificare, organizare, selecţie de personal, conducere şi
control), folosind resurse umane, financiare şi materiale. Aceste cinci funcţii vor fi detaliate în
continuare.

1.1. Planificarea

Această funcţie managerială priveşte anticiparea situaţiilor viitoare şi determinarea celei mai
potrivite modalităţi de acţiune pentru îndeplinirea obiectivelor organizaţionale. Deseori considerată
„prima” funcţie a managementului, planificarea pune bazele tuturor celorlalte funcţii. Planificarea e
un proces continuu care implică stabilirea acţiunilor necesare pentru a răspunde la întrebări precum:
ce trebuie făcut, de către cine, unde, când şi cum. De asemenea, în această fază managerul trebuie să
ia în calcul şi factorii care pot ajuta sau împiedica atingerea scopului, precum şi posibilele
alternative disponibile pentru atingerea scopului.
Prin planificare se instituie o serie de acţiuni care angajează indivizii, departamentele şi
întreaga organizaţie timp de zile, luni sau chiar ani. De aceea, planificarea trebuie făcută cu atenţie,
luându-se în calcul următoarele aspecte:

• Determinarea resurselor necesare;


• Identificarea numărului de oameni şi a tipurilor de calificare (personal tehnic, de supervizare
sau managerial);
• Dezvoltarea mediului organizaţional în care se va lucra (ierarhia organizaţională);
• Determinarea standardelor necesare pentru evaluarea evoluţiei proiectului, astfel încât să se
poată face corecţii atunci când acest lucru se impune.
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

În funcţie de amploare sau sfera de acţiune, se pot evidenţia trei categorii de planificare:

• Planificarea strategică: Determină obiectivele majore ale organizaţiei. Este iniţiată şi


ghidată de un management de nivel înalt, însă toate nivelurile de management trebuie să
participe pentru ca planificarea strategică să dea rezultate. Acest tip de planificare
îndeplineşte scopuri precum:
o Stabilirea unor direcţii şi angajamente pe termen lung pentru întreaga organizaţie;
o Implicarea mai multor niveluri de management în procesul de planificare;
o Dezvoltarea unui model organizaţional în care planurile subunităţilor să fie
armonizate şi consistente;
• Planificarea tactică: Priveşte mai ales implementarea planurilor strategice printr-un
management de nivel mediu. Tactica este mijlocul de îndeplinire a strategiei. Planurile se
referă la responsabilităţile unităţilor şi în general se concentrează pe activităţile curente şi pe
termen scurt;
• Planificarea operaţională: Se referă la îndeplinirea responsabilităţilor la nivel de post sau
compartiment. Planurile pot fi singulare (pentru activităţi care nu se repetă, de exemplu
pentru un program sau buget) sau continue (care includ politici sau anumite proceduri).

1.2. Organizarea

Organizarea este funcţia managementului care îmbină resursele umane şi materiale prin
proiectarea unei structuri formale a sarcinilor şi autorităţii. Organizarea stabileşte deci relaţiile
dintre activitate şi autoritate. În acest context, managerul trebuie să îndeplinească patru scopuri
principale:

• Să determine ce activităţi de lucru trebuie făcute pentru atingerea obiectivelor


organizaţionale;
• Să clasifice tipurile de activităţi şi grupurile de lucru în unităţi de lucru uşor gestionabile;
• Să atribuie indivizilor sarcini de lucru şi să-şi delege autoritatea într-un mod adecvat;
• Să proiecteze o ierarhie a relaţiilor pentru luarea deciziilor.

Procesul de organizare poate fi văzut ca format din cinci paşi:

• Studierea planurilor şi scopurilor: Planurile dictează ţintele şi activităţile curente sau


viitoare. În unele situaţii, ar trebui create noi unităţi, alteori, unor unităţi existente li se
atribuie noi atribuţii, unele unităţi pot fi desfiinţate. Pot apărea noi relaţii între factorii de
luare a deciziilor. Organizarea creează o nouă structură, noi relaţii şi le modifică pe cele
existente;
• Identificarea activităţilor de lucru necesare pentru atingerea obiectivelor: Este utilă crearea
unei liste de sarcini începând cu cele continue şi terminând cu cele singulare;
• Clasificarea şi gruparea activităţilor: Managerii trebuie să efectueze trei proceduri:
o Să examineze fiecare activitate identificată pentru a-i determina natura generală
(marketing, producţie etc.);
o Să grupeze activităţile în ariile corespunzătoare;
o Să stabilească departamentele adecvate în cadrul structurii organizaţiei;
• Atribuirea sarcinilor şi delegarea autorităţii: Un pas critic în care se stabilesc
departamentele, natura, scopul şi sarcinile fiecărui departament, împreună cu performanţele
aşteptate;
• Proiectarea unei ierarhii de relaţii: Acum se hotărăsc relaţiile de lucru verticale şi
orizontale. Structurarea verticală rezultă într-o ierarhie de luare a deciziilor în care se ştie

2
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

cine răspunde pentru fiecare sarcină. Structurarea orizontală defineşte relaţiile de lucru între
departamente şi stabileşte numărul de subordonaţi ai fiecărui manager.

1.3. Selecţia personalului

Această funcţie priveşte recrutarea, selecţionarea, instruirea şi desemnarea persoanei


potrivite pentru poziţia potrivită în cadrul organizaţiei. În general, se consideră ca oamenii sunt cea
mai importantă resursă de care dispune organizaţia. Prin selecţia personalului, se doreşte
identificarea, atragerea şi păstrarea personalului calificat pentru a ocupa poziţiile disponibile.
Selecţia personalului poate fi văzută ca un proces în opt paşi:

• Planificarea resurselor umane: Trebuie să asigure faptul că nevoile de personal ale


organizaţiei vor fi satisfăcute. De obicei, se analizează planurile organizaţiei pentru a vedea
ce competenţe anume vor fi necesare în viitor. După realizarea previziunilor, se stabileşte
numărul de persoane care trebuie recrutate (din afara organizaţiei) sau instruite (din
interior);
• Recrutarea: Se identifică şi se atrag candidaţi care îndeplinesc cerinţele pentru posturile
vacante prevăzute. Ca rezultat al analizei posturilor, se întocmesc descrierile şi specificaţiile
posturilor. Recrutarea efectivă se realizează prin anunţuri publice în ziare, agenţìi de ocupare
a forţei de muncă, legături cu universităţile, site-uri Internet specializate etc.;
• Selecţionarea: După recrutare, candidaţii interesaţi trebuie evaluaţi şi selecţionaţi cei ale
căror competenţe se potrivesc cu cerinţele posturilor. Etapele urmărite în evaluare pot fi:
completarea unor formulare, examinarea profesională, verificarea recomandărilor şi
interviul;
• Instalarea şi orientarea: Odată selecţionat, noii angajaţi trebuie integraţi în organizaţie;
trebuie să li se prezinte grupul de lucru, precum şi regulamentul intern sau normele
organizaţiei;
• Instruirea şi dezvoltarea: Organizaţia încearcă îmbunătăţirea capacităţii angajaţilor de a
contribui la funcţionarea eficientă a organizaţiei. Instruirea se concentrează asupra calificării
angajaţilor. Dezvoltarea implică pregătire angajaţilor pentru responsabilităţi suplimentare
sau avansare;
• Evaluarea performanţelor: Un sistem proiectat să măsoare performanţele efective ale
angajaţilor în comparaţie cu standarde de performanţă prestabilite;
• Deciziile de angajare: Pe baza evaluării performanţelor, se pot lua decizii privind
recompensele financiare, transferările, promovările sau retrogradările;
• Sciziunile: Managementul trebuie să fie preocupat şi de demisii, pensionări sau concedieri.

1.4. Conducerea

După crearea planurilor, structurii organizaţiei şi completarea personalului, următorul pas al


procesului managerial este conducerea, adică dirijarea şi motivarea angajaţilor către obiectivele
organizaţionale. Din acest motiv, conducerea este importantă mai ales la primul nivel de
supervizare, deoarece la acest nivel este concentrată majoritatea angajaţilor organizaţiei.
Caracteristicile cele mai importante ale funcţiei de conducere se referă la stilul de conducere
(autocratic, democratic) şi la procesul de luare a deciziilor. Acestea sunt în strânsă legătură cu o
serie de factori precum urgenţa situaţiei sau motivarea subordonaţilor. De asemenea, managerul ca
lider trebuie să cunoască toate aspectele situaţiei curente, să estimeze impactul pe care îl vor avea
deciziile sale şi să ia totdeauna în calcul elementul uman. El trebuie să atribuie sarcinile iniţiale
tuturor angajaţilor, să dea ordine clare şi concise şi să urmărească modul cum sunt îndeplinite
sarcinile, dând dacă este nevoie îndrumări verbale sau scrise.

3
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

1.5. Controlul

Ultima funcţie a managementului se referă la evaluarea performanţelor organizaţiei pentru a


determina dacă îşi îndeplineşte sau nu obiectivele. Planificarea, organizarea, selecţia de personal şi
conducerea trebuie monitorizate pentru a le asigura eficienţa. În această fază trebuie luate
următoarele măsuri:

• Stabilirea standardelor de performanţă utilizate pentru a măsura progresul către scop.


Standardul reprezintă un mecanism de măsură cantitativă sau calitativă, destinat
monitorizării performanţelor oamenilor sau proceselor. Deşi standardele depind de natura
problemei analizate, în general aparţin unuia din două grupuri:
o Standarde manageriale: includ rapoarte, regulamente şi evaluări de performanţă. De
exemplu, un raport lunar din partea tuturor persoanelor implicate în vânzări, centrat
pe ariile cheie de interes pentru managerul de vânzări;
o Standarde tehnice: se referă la metodele şi procesele de producţie, la materiale,
echipamente, componente şi furnizori. Standardele tehnice pot veni din surse interne
sau externe. De exemplu, standarde de calitate dictate de reglementări
guvernamentale sau specificaţiile producătorului pentru echipamente;
• Monitorizarea şi evaluarea performanţelor: performanţele sunt măsurare şi se determină
dacă respectă standardele stabilite. Dacă o asemenea comparaţie indică faptul că rezultatele
sunt acceptabile, nu este necesară nici o acţiune. În caz contrar, se trece la următorul pas:
• Corectarea deviaţiilor de la standarde: măsurile de corecţie necesare se vor lua pe baza a
trei factori – standardul, precizia măsurătorii care a indicat deviaţia şi diagnosticul
persoanelor care au investigat cauzele deviaţiei. Standardele pot fi prea slabe sau prea
stricte. Măsurătorile pot fi imprecise deoarece instrumentele de măsură pot fi utilizate
defectuos sau prezintă ele însele defecte. Oamenii pot lua decizii greşite la hotărârea
acţiunilor corective necesare.

2. Managementul software
Multe proiecte de dezvoltare software au probleme, mai devreme sau mai târziu. Programul
nu este livrat la timp, bugetul este depăşit, clienţii sunt nemulţumiţi. De multe ori, cauzele sunt de
natură tehnică. Însă î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. În 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

4
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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:

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

5
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

a) Introducerea: Sunt descrise condiţiile şi istoricul proiectului, împreună cu scopurile, numele


persoanelor responsabile şi un sumar al proiectului;
b) 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;
c) Riscurile: În 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;
d) 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;
e) 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. În acest scop, poate fi necesară o perioadă de
instruire a membrilor echipei;
f) 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;
g) 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;
h) 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;
i) Testarea: Se descriu mediile şi echipamentele de test necesare;
j) Resursele: Sunt enumerate resursele hardware şi instrumentele necesare pentru proiect;
k) Asigurarea calităţii: Se identifică procedurile care vor fi folosite pentru a verifica dacă
proiectul îndeplineşte standardele de calitate dorite;
l) 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;
m) Livrarea: În final, trebuie respectate procedurile necesare pentru livrarea produsului către
client.

6
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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.

2.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 mai mult timp trebuie 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 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ţă.

7
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

3. 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;
• 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;

8
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

• 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 (0.Y). Înainte de
versiunea 1.0, deoarece programul este în curs de formare, sunt permise modificări 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.

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

9
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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 sub-echipe astfel încât majoritatea comunicării şi coordonării să se desfăşoare la nivelul
acestora.

4.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 aceasta a productivităţii nu încurajează reutilizarea componentelor,
care ar conduce la scrierea unui cod mai mic.

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

10
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

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


scăzut ridicat
gradul de dirijare
scăzut stil de separare stil de angajament
a relaţiei
ridicat stil de relaţionare stil de integrare

Tabelul 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 manager-
ului 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

11
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

familiar, coordonarea poate fi realizată prin standardizarea producţiei. Pentru o aplicaţie complexă
şi novatoare, acest mecanism ar fi ineficient.

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

4.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 2 prezintă un exemplu de organizare ierarhică. Dreptunghiurile denotă sub-echipele
în care se lucrează efectiv. Cercurile reprezintă managerii. Aici avem două nivele de management.
La nivelul inferior, sub-echipele 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 sub-echipelor pe ansamblu.

Figura 2. Organizare ierarhică

Organizarea ierarhică reflectă deseori structura globală a sistemului care trebuie dezvoltat.
Dacă sistemul are trei sub-sisteme majore, pot exista trei sub-echipe. 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;
12
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

4.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ă 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
grafică baze de date testare
în timp real
proiectul A X X
proiectul B X X X
proiectul C X X X

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

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

13
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

4.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;
• 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:
o 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;
o 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.

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

14
Ingineria programării
Cursul 5

Estimarea costului unui proiect software

1. Introducere
2. Cum nu trebuie estimat costul
3. Modele algoritmice clasice
4. Modele algoritmice moderne
4.1. Modelul Walston-Felix
4.2. Modelul COCOMO
4.3. Analiza punctelor funcţionale
4.4. Modelul Putnam-Norden
5. Distribuirea forţei de muncă în timp
6. Concluzii

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:

E = 2.7 ⋅ KLOC 1.05

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
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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.
În 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.
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;

2
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

• 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 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:

• n1 = numărul de operatori diferiţi;


• n2 = numărul de operanzi diferiţi;
• N1 = numărul total de apariţii ale operatorilor;
• N2 = numărul total de apariţii ale operanzilor;

Pentru lungimea unui program, Halstead dă următoarea ecuaţie:

N = N1 + N 2

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 n1
şi n2. Valoarea lui n1 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ă n2 este determinat în
principal de numărul de variabile (VARS) care apar în program. Bazându-se pe aceste presupuneri, a
fost enunţată următoarea relaţie empirică:

LOC = 102 + 5.31 ⋅ 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 VARS 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.

3
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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ă 60
de luni-om poate fi realizat într-un an cu o echipă de 5 persoane sau într-o lună cu o echipă de 60 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.

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;
• Dacă ştim că concurenţa a făcut o ofertă de 100000 de euro, noi vom face o ofertă de 90000
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)


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
4
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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:

a + 4m + b
E=
6

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

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 proprietăţilor
măsurabile ale proiectului curent. În 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ă:

n
E = a0 + ∑ ai xi
i =1

Coeficienţii ai sunt constante, iar xi 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:

E = −33.63 + 9.15 x1 + 10.73 x2 + 0.51x3 + 0.46 x4 + 0.40 x5 + 7.28 x6 − 21.45 x7


+ 13.5 x8 + 12.35 x9 + 58.82 x10 + 30.61x11 +29.55 x12 + 0.54 x13 − 25.20 x14

În această ecuaţie E reprezintă numărul estimat de luni-om necesar. Semnificaţia factorilor xi


şi domeniul lor de definiţie sunt prezentate în tabelul următor:

Factor Descriere Valori posibile


x1 Instabilitatea specificaţiilor cerinţelor 0-2
x2 Instabilitatea proiectării 0-3
x3 Procentajul de instrucţiuni matematice 0-100
x4 Procentajul de instrucţiuni I/O 0-100
x5 Numărul subprogramelor număr
x6 Utilizarea unui limbaj de nivel înalt 0 (da) / 1 (nu)
x7 Aplicaţie comercială 0 (da) / 1 (nu)
x8 Program de sine stătător 0 (da) / 1 (nu)
x9 Primul program pe această maşină 1 (da) / 0 (nu)
x10 Dezvoltare concurentă de hardware 1 (da) / 0 (nu)
x11 Utilizarea dispozitivelor random-access 1 (da) / 0 (nu)
x12 Maşina gazdă diferită de maşina ţintă 1 (da) / 0 (nu)
x13 Număr de erori număr
x14 Dezvoltare pentru o organizaţie militară 0 (da) / 1 (nu)

5
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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 x12 şi x14 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ă datele proiectelor care stau la baza modelului au un impact
semnificativ asupra factorilor care influenţează costul. Există o creştere a efortului datorită folosirii
limbajului de asamblare în locul unui limbaj de nivel înalt (x6).
Î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.
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:

P((1 − α ) E ≤ R ≤ (1 + α ) 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
Tipul modulului Mică ↔ Mare
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 Sk, va rezulta
un cost al modulului M k = S k ⋅ Cij .
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.
6
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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:

E = (a + b ⋅ KLOC c ) ⋅ f ( x1 ,..., xn ) ,

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

E = a + b ⋅ KLOC c

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 1 în toate modelele.

Autor Formula
Halstead E = 0.7 ⋅ KLOC 1.50
Boehm E = 2.4 ⋅ KLOC 1.05
Walston-Felix E = 5.2 ⋅ KLOC 0.91

Când c < 1, 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. În 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.
În 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. Alegerea unui model sau a altuia depinde în principal de gradul de complexitate a
interfaţării modulelor proiectului.
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
7
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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 Walson-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 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. În 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.1. Modelul Walston-Felix

Ecuaţia ce stă la baza modelului Walston-Felix este: E = 5.2 ⋅ KLOC 0.91 . 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.

8
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Productivitatea medie pentru


Variabila PC
valoarea variabilei
< normală normală > normală
Complexitatea interfeţei utilizator 376
500 295 124
mică medie mare
Calificarea şi experienţa personalului 278
132 257 410
minimă medie vastă
Experienţă anterioară cu aplicaţii similare 264
146 221 410
Procentajul de programatori participanţi < 25% 25-50% > 50%
238
în faza de proiectare 153 242 391
Raportul dintre mărimea medie a echipei < 0.5 0.5-0.9 > 0.9
134
şi durata proiectului (persoane/lună) 305 310 171

Walston şi Felix consideră că indexul productivităţii I poare fi determinat pentru noile


proiecte după următoarea relaţie:

29
I = ∑ Wi X i ,
i =1

unde ponderile Wi sunt definite astfel:

Wi = 0.5 ⋅ log10 ( PCi ) .

În relaţia de mai sus, PCi reprezintă variaţia productivităţii factorului i. Pentru primul factor
din tabelul de mai sus, complexitatea interfeţei cu utilizatorul, rezultă următoarele: PC1 = 376 , deci
W1 = 1.29 . Variabilele X i pot lua valorile –1, 0 şi 1, 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.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:

E = b ⋅ KLOC c ,

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

9
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

î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
1000 3390 6872 14333

4.3. Analiza punctelor funcţionale

Analiza punctelor funcţionale este o metodă de estimare a costurilor care încearcă să evite
problemele determinate de estimarea dimensiunii codului. APF (Albrecht, 1983) se bazează pe
numărarea diferitelor structuri de date utilizate; se presupune că acest număr este un bun indicator
pentru dimensiunea proiectului. Metoda este potrivită mai ales pentru aplicaţiile comerciale, în care
structura datelor are o foarte mare importanţă. APF este mai puţin indicată pentru proiectele în care
algoritmii joacă rolul dominant (de exemplu compilatoarele sau aplicaţiile în timp real).
Unul din scopurile principale ale APF este evaluarea posibilităţilor sistemului din punctul de
vedere al utilizatorilor. De aceea, analiza se bazează pe modalităţile în care diverşi utilizatori
interacţionează cu aplicaţiile. Astfel, sistemul îndeplineşte cinci funcţii fundamentale:

• funcţii referitoare la date


o fişiere interne logice
o fişiere externe de interfaţă
• funcţii tranzacţionale
o intrări externe
o ieşiri externe
o cereri externe

Fişierele interne logice (engl. „Internal Logical Files”, FIL): Permit utilizatorilor să
folosească datele pe care trebuie să le întreţină. De exemplu, un pilot poate introduce datele de

10
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

navigare la un terminal din carlingă înainte de plecare. Datele sunt stocate într-un fişier şi pot fi
modificate în timpul misiunii. Pilotul este deci responsabil pentru întreţinerea acestor date.
Fişierele externe de interfaţă (engl. „External Interface Files”, FEI): În acest caz,
utilizatorul nu este responsabil pentru întreţinerea datelor; acestea sunt localizate în alt sistem care
le întreţine. Utilizatorul sistemului analizat solicită datele doar pentru informare. De exemplu, un
pilot se poate informa asupra poziţiei cu ajutorul sateliţilor GPS sau al sistemelor de la sol. El nu are
responsabilitatea actualizării acestor date, însă le poate accesa în timpul zborului.
Intrările externe (engl. „External Input”, IE): Permit utilizatorului să întreţină fişierele
interne logice prin operaţii de adăugare, modificare şi ştergere.
Ieşirile externe (engl. „External Output”, EE): Permit utilizatorului să producă date de ieşire.
De exemplu, pilotul poate să afişeze separat viteza la sol şi viteza reală în aer, informaţii derivate
din datele interne (pe care le poate întreţine) şi cele externe (pe care le poate accesa).
Cererile externe (engl. „External Inquiries”, CE): Pentru ca utilizatorul să poată selecta şi
afişa datele din fişiere, el trebuie să introducă informaţii de selecţie pentru a găsi datele în
conformitate cu anumite criterii. În această situaţie datele din fişiere nu sunt modificate, ci doar
căutate şi furnizate. De exemplu, dacă pilotul afişează date cu privire la relieful solului, date stocate
anterior, rezultatul este regăsirea directă a informaţiilor.
Prin încercări repetate, s-au stabilit ponderi pentru fiecare din aceste entităţi. Numărul de
puncte funcţionale neajustate este:

PFN = 10 ⋅ FIL + 7 ⋅ FEI + 4 ⋅ IE + 5 ⋅ EE + 4 ⋅ CE

În funcţie de complexitatea tipurilor de date, se disting o serie de valori pentru aceste puncte
funcţionale, prezentate în tabelul următor.

Nivel de complexitate
Tip
Simplu Mediu Complex
FIL 7 10 15
FEI 5 7 10
IE 3 4 6
EE 4 5 7
CE 3 4 6

Pentru ajustarea suplimentară a estimărilor, se iau în calcul şi alte 14 caracteristici care


influenţează dezvoltarea aplicaţiilor: comunicaţiile de date, funcţiile distribuite, performanţa,
folosirea masivă a configuraţiilor, rata tranzacţiilor, intrările de date online, eficienţa utilizatorilor
finali, actualizările online, prelucrările complexe, refolosirea, uşurinţa la instalare, uşurinţa la
folosire, locaţiile multimple, facilitarea modificărilor.
Influenţa fiecărei caracteristici este evaluată pe o scară de la 0 (nu influenţează) la 5
(influenţă puternică). Gradul de influenţare GI este suma acestor puncte pentru toate caracteristicile.
Se calculează apoi factorul de complexitate tehnică:

FCT = 0,65 + 0,01⋅ GI .

Punctele funcţionale ajustate (PF) se obţin astfel:

PF = PFN ⋅ FCT .

Avantajul principal al analizei punctelor funcţionale este faptul că măsura productivităţii


este un rezultat natural, deoarece punctele funcţionale sunt independente de tehnologie şi deci pot fi
utilizate pentru a compara productivitatea pe platforme diferite şi cu instrumente de dezvoltare

11
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

diferite. Ele pot fi folosite pentru a stabili o rată de productivitate (PF / h) care facilitează estimările
privind durata proiectului ca întreg.

4.4. 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
FM (t ) = 2 ⋅ K ⋅ a ⋅ t ⋅ e − at ,

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 1.
Integrarea ecuaţiei pentru FM(t) determină efortul cumulat I:
2
I (t ) = K (1 − e − at )

Figura 1. Curba Rayleigh

Dacă considerăm momentul de timp T în care curba Rayleigh ajunge în punctul de maxim,
1
atunci a = 2 . Acest moment T va fi apropiat de momentul de timp în care proiectul va fi predat
2T
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:

E = I (T ) = 0.3945K

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 sunt incluse în model. Estimările conform acestui model nu se pot aplica
decât începând cu proiectarea şi implementarea.

12
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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ă 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:

D = k ⋅ E 1/ 3 ⋅ t 4 / 3 ,

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ă:

A = E / t3 ,

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:

E = ( D / k ) 9 / 7 ⋅ A4 / 7 .

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

13
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

• 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:

L = 777 ⋅ P −0.5

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:
P( P − 1)
. Dacă fiecare membru trebuie să comunice numai cu un singur alt membru, acest număr
2
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 P 2 / 2 . Î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 1 la P − 1 .
Dacă productivitatea individuală maximă este L şi fiecare legătură de comunicaţie conduce la o
pierdere a productivităţii l, atunci productivitatea medie va fi:

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

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ă:

Ltot = P ⋅ Lγ = P ⋅ ( L − l ⋅ ( P − 1)γ ) .

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 Popt , 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 LOC / lună-om (L = 500), 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:

14
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

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

15
Ingineria programării
Cursurile 6-7

Reutilizarea resurselor software


1. Introducere
2. Reutilizarea produselor intermediare
2.1. Biblioteci şi componente software
2.2. Şabloane
2.3. Reutilizarea proiectării
2.4. Sisteme de transformare
2.5. Generatoare de aplicaţii şi limbaje de a patra generaţie
3. Reutilizarea instrumentelor şi a tehnicilor
3.1. Limbaje de interconectare a modulelor
3.2. Programarea orientată obiect
4. Perspectivele reutilizării software
5. Aspecte non-tehnice ale reutilizării software
5.1. Economia refolosirii software
5.2. Refolosirea sofware şi managementul
5.3. Aspecte psihologice ale refolosirii software
6. Concluzii

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

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.
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

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 a patra generaţie.
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.

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

2
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

şiruri de caractere
monolitice stive
cozi
structuri
liste
polilitice arbori
grafuri
utilităţi
instrumente
filtre

3
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Î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ă taxonomie 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
stive
1-1 cozi
structuri
liste
1-n arbori
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.
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ă

4
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

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
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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(1). 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.
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.

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:

6
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

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

7
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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 probleme ca: nivelul de
inteligibilitate a 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.

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.

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

...

8
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

module errormsg
provide procedure 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: În 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ă.

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 sofisticare a
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 încearcat 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:

9
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

• E ≤ E (reflexivitate);
• E1 ≤ E 2 , E2 ≤ E3 ⇒ E1 ≤ E3 (tranzitivitate);
• E1 ≤ E2 , E2 ≤ E1 ⇒ E1 = E2 (antisimetrie);
• E1 ≤ E2 sau E2 ≤ E1 (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ă 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.

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

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.

10
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

11
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

• Î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 informal, în care acelaşi lucru poate fi
exprimat în mai multe moduri, folosind concepte care nu sunt bine definite. Totuşi, limbajul
informal 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 informal existent. Trebuie apoi să se aleagă dintre diversele primitivele semantice care
există informal. 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 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 informal, 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 informal 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.

12
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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;
• 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.1. Economia reutilizării 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

13
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

ş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.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 duală, în care proiectanţii buni
au aceleaşi perspective în ceea ce priveşte slujba ca şi administratorii buni.

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

14
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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.

15
Ingineria programării
Cursurile 8-9

Ingineria cerinţelor. Analiza orientată obiect

1. Ingineria cerinţelor
1.1. Cerinţa
1.2 Extragerea cerinţelor
1.3. Metode pentru identificarea cerinţelor utilizatorilor
1.4. Metode pentru specificarea cerinţelor utilizatorilor
1.5. Documentul cerinţelor utilizatorului (DCU)
1.6. Cerinţe SMART
2. Analiza orientată obiect. Metode de analiză orientată obiect
2.1. Principiile analizei orientate obiect
2.2. Abstractizarea
2.3. Moştenirea
2.4. Comunicarea prin mesaje
2.5. Metodele de organizare
3. Metoda de analiză Coad-Yourdon
3.1. Activitatea I: Identificarea claselor şi obiectelor
3.2. Activitatea a II-a: Identificarea structurilor
3.2.1. Structura gen-spec
3.2.2. Structura întreg-parte
3.3. Activitatea a III-a: Identificarea atributelor
3.4. Activitatea a IV-a: Identificarea serviciilor
3.5. Activitatea a V-a: Identificarea subiectelor
4. Concluzii

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ă-l 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-l 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.
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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, 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ţă).

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.

2
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

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

1.4. Metode pentru specificarea cerinţelor utilizatorilor

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ă
O parte din baza de date va fi desemnată ca o adresă.
Va fi cel puţin o adresă în baza de date.

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.

3
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

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

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.

4
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

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.

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.

5
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

1.6. Cerinţe SMART

Cerinţele trebuie să fie SMART (engl. inteligente), adică:

• Specifice: Cerinţele trebuie să fie legate de condiţiile pe care urmăreşte să le schimbe


proiectul;
• Măsurabile: Sunt preferabile cerinţele cuantificabile (cantitative) deoarece sunt mai precise,
pot fi agregate şi permit ulterior o analiză statistică a rezultatelor. Totuşi, nu toate cerinţele
sunt uşor de cuantificat şi de cele mai multe ori sunt necesare şi cerinţe privind aspecte
calitative;

6
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

• Accesibile: Cerinţele trebuie să poată fi îndeplinite cu costuri rezonabile prin metode


corespunzătoare;
• Relevante: Cerinţele trebuie să fie utile echipei de dezvoltare. Amănuntele nesemnificative
pot îngreuna înţelegerea nevoilor reale ale clientului;
• disponibile în Timp util (engl. „timely”): Pentru ca deciziile manageriale să fie luate eficient,
cerinţele trebuie puse la dispoziţia echipei în timp util.

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


7
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

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 mesaje;


• Utilizarea metodelor de organizare:
o obiecte şi atribute;
o obiecte şi părţile sale;
o clase şi membrii.

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.

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.

8
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

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:

• diferenţ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).

3. Metoda de analiză Coad-Yourdon


Coad şi Yourdon descriu o metodă AOO bazată pe cinci activităţi majore:

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

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.

9
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Notaţii:

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

Figura 1. Clasă fără instanţe

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

Figura 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;
• 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?

10
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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.

3.2. Activitatea a II-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.

3.2.1. Structura gen-spec

Notaţia pentru structura gen-spec este următoarea:

Figura 3. Structura gen-spec

11
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Î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.
De exemplu, considerând 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 AvioaneParticulare.

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:

Figura 4. Structură gen-spec eronată

12
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

3.2.2. Structura întreg-parte

Notaţia pentru structura întreg-parte este următoarea:

Figura 5. Structură întreg-parte

În 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:

Figura 6. Structură întreg-parte: Avion-Motor

În acest exemplu, un Avion este un ansamblu:

• de 0 motoare (planor)
• de cel mult 4 motoare

13
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

iar un Motor este o parte din:

• nu neapărat dintr-un avion


• cel mult un avion

Figura 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

iar un funcţionar este membru al unei singure organizaţii.

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.

14
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Exemple:

Figura 8. Structură întreg-parte: ansamblu-părţi

Figura 9. Structură întreg-parte: container-conţinut

În 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ă.

15
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Exemplu:

Figura 10. Structură întreg-parte: colecţie-membri

O organizaţie poate fi considerată o colecţie de funcţionari.

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.

3.3. Activitatea a III-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 timpului, domeniul claselor rămâne relativ
stabil, în schimb, atributele sunt mult mai susceptibile de a se modifica.

16
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Se recomandă definirea atributelor pe baza următoarei strategii:

• identificarea atributelor;
• poziţionarea atributelor;
• identificarea conexiunii instanţelor;
• verificarea cazurilor speciale;
• specificarea atributelor.

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

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.

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:

Figura 11. Conexiunea instanţelor

17
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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ă
o 0, dacă este o conexiune opţională;
o ≥ 1 , dacă e o conexiune obligatorie;
• Limita superioară
o 1, dacă este o singură conexiune;
o ≥ 1 , dacă sunt conexiuni multiple.

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

Figura 12. Plasarea unui singur atribut într-o clasă pe care o descrie

Figura 13. Plasarea greşită a unui atribut

18
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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ă;
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ţaInit: 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ţeInit, 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.

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.

19
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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ţaInit, 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 este următoarea:

Figura 14. Diagrama stărilor obiectelor

De exemplu:

Figura 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ă în specificarea serviciilor receptorului.

20
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Notaţie:

Figura 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:

Figura 17. Conexiune prin mesaje spre mai multe obiecte

Interacţiunile dintre factorul uman şi sistem sunt şi ele reprezentate în modelul AOO:

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

21
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

3.5. Activitatea a V-a: Identificarea subiectelor

În 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:

1. Subiect 1

2. Subiect 2

Figura19 : Subiect ne-expandat

• Subiecte parţial expandate, conţinând lista claselor componente:

1. Subiect 1

Clasa1
Clasa2

2. Subiect 2

Clasa3
Clasa4

Figura 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. În interiorul dreptunghiurilor numerotate vor fi incluse
clasele cu notaţia completă: nume, atribute, servicii, eventual şi cu legăturile dintre ele:

22
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

1 1 2 2

1 1 2 2

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

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.

23
Ingineria programãrii
Cursul 10

Proiectarea orientatã obiect

1. Procesul proiectãrii sistemelor software


2. Caracteristicile unei proiectãri corecte
2.1. Extensibilitatea
2.2. Siguranþa
2.3. Eficienþa
3. Proiectarea orientatã obiect
4. Etapele proiectãrii orientate obiect
4.1. Contextul sistemului ºi modelele de utilizare
4.2. Proiectarea arhitecturii
4.3. Identificarea obiectelor
4.4. Modele de proiectare
4.5. Specificarea interfeþelor obiectelor
5. Metoda de proiectare Coad-Yourdon
5.1. Activitatea I: Proiectarea componentei domeniului problemei
5.2. Activitatea a II-a: Proiectarea componentei interacþiunii cu factorul uman
5.3. Activitatea a III-a: Proiectarea componentei coordonãrii task-urilor
5.4. Activitatea a IV-a: Proiectarea componentei coordonãrii datelor
6. Concluzii

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.
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Acest proces se repetã pentru fiecare subsistem pânã când componentele identificate pot fi
mapate direct în componentele limbajului de programare.

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. În practicã, trebuie fãcute compromisuri între ele. Aceste caracteristici sunt
extensibilitatea, siguranþa ºi eficienþa.

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.
Modelul problemei trebuie sã reflecte caracteristicile generale ale problemei. Un obstacol
des întâlnit atunci când se doreºte extinderea unui sistem este 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ã.
Chiar dacã programul cuprinde noþiuni generale 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ã.

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
2
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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.

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

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

3
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Aºadar, caracteristicile proiectãrii orientate obiect sunt urmãtoarele:

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.

4. Etapele proiectãrii orientate obiect

Procesul general al proiectãrii orientate obiect cuprinde mai multe etape:

 înþelegerea ºi definirea contextului în care va evolua sistemul ºi a modului în care acesta va


fi utilizat;
 proiectarea arhitecturii sistemului;
 identificarea principalelor obiecte 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. În 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.

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.

4
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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 1. Exemplu de arhitecturã: agent spell-checker

4.3. Identificarea obiectelor

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

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.

5
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

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 î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.
Între 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 clase iar clasele „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.

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 1 la 1
î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.

6
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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.

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.

Figura 2. Diamantul mic

7
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Figura 3. Diamantul mare

Chiar dacã limbajul permite moºtenire multiplã (de exemplu C++), se recomandã evitarea
acesteia datoritã complexitãþii pe care o presupune. În cazul limbajelor care suportã doar moºtenire
simplã (Java, C#) se pot aplica metode de a transforma o moºtenire multiplã într-o moºtenire
simplã:

Figura 4. Formarea unor ierarhii separate, mapate între ele prin structuri întreg-parte

8
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Ierarhia multiplã poate fi transformatã direct într-o ierarhie simplã, caz în care anumite
atribute ºi servicii vor fi repetate în clasele specializate.

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

Figura 6. Acomodarea cu limbaje care nu suportã moºtenire

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

5.2. Activitatea a II-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).

9
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

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

5.3. Activitatea a III-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.

10
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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.

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.

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.

11
Ingineria programãrii
Cursurile 11-12

Limbaje de modelare. UML


1. Limbaje de modelare
2. Ce este UML?
3. Modelarea cazurilor de utilizare
4. Modelarea conceptualã. Diagrama de clase
4.1. Asocierea
4.2. Agregarea
4.3. Compunerea
4.4. Vizibilitatea atributelor ºi operaþiilor
4.5. Moºtenirea
4.6. Polimorfismul
4.7. Interfeþe
4.8. Metode statice
5. Diagrame de interacþiune
5.1. Diagrama de secvenþe
5.2. Diagrama de colaborare
6. Diagrame de activitãþi
7. Diagrame de stãri
8. Diagrama pachetelor
9. Diagrame de implementare
9.1. Diagrame componentelor
9.2. Diagrama de lansare
10. Concluzii

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ã incompletitudinii 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.
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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:

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

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.
2
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

În 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 Rational Corp. Scopul parteneriatului era
combinarea ambelor perspective într-o metodã unificatã. În 1995 ºi Ivar Jacobson, creatorul OOSE,
a venit la Rational, 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 1.1 a fost adoptatã ca
standard de catre 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 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.

3
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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 2. Caz de utilizare

Un caz de utilizare trebuie sã aibã un iniþiator al acþiunii, numit actor. În cazul unui sistem
bancar, retragerea banilor este fãcutã de clienþi, astfel încât clientul devine unul din actori:

Figura 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:

Figura 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;
4
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

 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, confirmarea
ei, scoaterea card-ului, preluarea chitanþei. Fiecare din aceºti paºi trebuie sã fie câte un caz de
utilizare?

Figura 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 6. Caz de utilizare corect

5
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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ã 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. În partea de sus este notat numele
clasei, în partea medianã atributele iar în partea de jos operaþiile sale.

Figura 7. Clasa în notaþie UML

4.1. Asocierea

Urmãtorul pas este definirea relaþiilor dintre concepte. Sã presupunem urmãtoarele douã
concepte:

Figura 8. Concepte înrudite funcþional

Dacã fiecare manager conduce o maºinã în compania respectivã, între aceste concepte existã
o relaþie:

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

6
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Figura 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:

Figura 11. Asociere complexã

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.

7
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Figura 12. Agregarea în notaþie UML

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. În 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 13. Compunerea în notaþie UML

4.4. Vizibilitatea atributelor ºi operaþiilor

În figura urmãtoare sunt prezentate notaþiile pentru vizibilitatea atributelor ºi operaþiilor


(privat, protejat, public), în mod text ºi în mod grafic.

Figura 14. Vizibilitatea atributelor ºi operaþiilor

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:

8
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Figura 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:

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

Figura 17. Moºtenire incorectã

Se poate observa cã operaþia zboarã nu se aplicã ºi clasei Om.

9
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

4.6. Polimorfismul

Clasele derivate pot redefini implementarea unei metode. În 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 18. Polimorfismul în notaþie UML

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 19. Clasã de bazã cu o metodã abstractã (virtualã)

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 20. Notaþia UML pentru interfeþe

10
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

O notaþie alternativã este urmãtoarea:

Figura 21. Notaþie alternativã pentru interfeþe

4.8. Metode statice

În notaþia UML, metodele statice se subliniazã:

Figura 22. Clasã cu metode statice

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.

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.
În figura urmãtoare este descrisã interacþiunea dintre doi abonaþi ai unei reþele de telefonie.
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 de obiecte, iar liniile orizontale punctate reprezintã mesaje-rãspuns.

11
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Figura 23. Diagramã de secvenþe

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 24. Diagramã de colaborare

12
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

În exemplul de mai sus, asteriscul semnificã posibilitatea mai multor interogãri SQL ºi a mai
multor rezultate.

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 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, 1 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 25 este prezentatã o diagramã de activitãþi cu decizii. „Candidatul trebuie sã fie


admis” este o notã asociatã unei decizii.

13
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Figura 25. Diagramã de activitãþi cu decizii

În figura 26 este prezentatã o altã diagramã de activitãþi, cu partiþii ºi ramificaþii.

14
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Figura 26. Diagramã de activitãþi cu partiþii ºi ramificaþii

15
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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 un alt cerc, la fel ca în diagramele de activitãþi, dovadã a consistenþei
limbajului UML.

Figura 27. Diagramã de stãri

În figura 28 putem observa cum stãrile din figura 27 se grupeazã într-o aºa numitã
superstare, pentru a putea descrie un sistem complex la un nivel superior de abstractizare.

16
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Figura 28. Diagramã de stãri: superstare

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ã echipe A ºi B lucreazã în paralel, echipa A nu va trebui sã se preocupe de conþinutul pachetului
echipei B, 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.

17
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Figura 29. Diagramã de pachete

La rândul sãu, un pachet poate fi explicitat:

Figura 30. Pachet explicitat

9. Diagrame de implementare
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.

18
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Figura 31. Diagramã de componente

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.

19
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Figura 32. Diagramã de lansare

10. Concluzii

În aceste cursuri a fost mai întâi argumentatã nevoia de un 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.

20
Ingineria programării
Cursul 13

Implementarea

1. Introducere
2. Limbaje de programare
2.1. Limbaje imperative
2.2. Limbaje declarative
3. Analiza unor limbaje de programare
3.1. C/C++
3.2. Basic
3.3. Pascal
3.4. Java
3.5. C#
4. Comparaţie între unele limbaje de programare
5. Utilitare pentru implementare şi testare
6. Concluzii

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 translării proiectării în
cod sursă. În primul rând, de cele mai multe ori proiectarea nu va realiza o mapare de 1 la 1 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.
În 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;
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

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

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.

2
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

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.

3. Analiza unor limbaje de programare

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.

3
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Dezavantaje:

• Dificil de stăpânit: C/C++ este unul din cele mai dificile limbaje de programare. În 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:

On the first day of Christmas my true love gave to me and a partridge in a pear tree.
a partridge in a pear tree.
On the eighth day of Christmas my true love gave to
On the second day of Christmas my true love gave to me
me eight maids a-milking, seven swans a-swimming,
two turtle doves six geese a-laying, five golden rings;
and a partridge in a pear tree. four calling birds, three french hens, 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 On the ninth day of Christmas my true love gave to me
and a partridge in a pear tree. nine ladies dancing, eight maids a-milking, seven
swans a-swimming,
On the fourth 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 four calling birds, three french hens, two turtle doves
and a partridge in a pear tree. and a partridge in a pear tree.

On the fifth day of Christmas my true love gave to me On the tenth day of Christmas my true love gave to me
five golden rings; ten lords a-leaping,
four calling birds, three french hens, two turtle doves nine ladies dancing, eight maids a-milking, seven
and a partridge in a pear tree. swans a-swimming,
six geese a-laying, five golden rings;
On the sixth day of Christmas my true love gave to me four calling birds, three french hens, two turtle doves
six geese a-laying, five golden rings; and a partridge in a pear tree.
four calling birds, three french hens, two turtle doves
and a partridge in a pear tree. On the eleventh day of Christmas my true love gave to
me
On the seventh day of Christmas my true love gave to eleven pipers piping, ten lords a-leaping,
me nine ladies dancing, eight maids a-milking, seven
seven swans a-swimming, swans a-swimming,
six geese a-laying, five golden rings; six geese a-laying, five golden rings;
four calling birds, three french hens, two turtle doves four calling birds, three french hens, two turtle doves

4
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

and a partridge in a pear tree. nine ladies dancing, eight maids a-milking, seven
swans a-swimming,
On the twelfth day of Christmas my true love gave to six geese a-laying, five golden rings;
me four calling birds, three french hens, two turtle doves
twelve drummers drumming, eleven pipers piping, ten and a partridge in a pear tree.
lords a-leaping,

3.2. Basic

Basic (Beginner's All 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ă-l 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. În
acelaşi timp, împiedică programatorul să controleze total calculatorul, ceea ce limitează
puterea programelor;
• Portabilitate limitată: Visual Basic rulează numai pe platforme Windows.

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

5
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

pas înainte important faţă de celelalte limbaje existente la momentul respectiv a fost faptul că
suporta recursivitatea.
În 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.

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

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.

6
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

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);
}

7
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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 = 0
40 FOR i=1 TO dimvect
50 INPUT ("Vector ("; i ; "): "); vector(i)
60 IF i = 1 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

8
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

Limbajul Pascal

program simplu; { Pascal nu este un limbaj case sensitive }


uses crt;

var max: real;


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);
}
}

9
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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];
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
10
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
(retract ?f3)
(assert (max ?val))
)
)

(defrule print_max
(dim ?d)
(i ?i)
(test (> ?i ?d))
(max ?max)
=>
(printout t "Maximul: " ?max crlf)
)

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

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
11
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

12
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

memoria, de exemplu, verifică dacă apelurile pentru alocarea memoriei au corespondent în


apeluri pentru dezalocare, determinând 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.

6. Concluzii
În 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.

13
Ingineria programării
Cursul 14

Psihologia şi etica programării

1. Programarea ca activitate umană


1.1. Scrierea programelor
1.1.1. Comentariile
1.1.2 Numele variabilelor
1.1.3. Indentaţia
1.2. Concepte ale limbajelor de programare
1.3. Interfaţa cu utilizatorul
2. Etica programării
2.1. Codul etic IEEE
2.2. Legea drepturilor de autor
2.3. Licenţa publică generală GNU
3. Concluzii

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)

În 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 Y? 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 şi robusteţea construcţiilor unui limbaj;
• tipurile de erori făcute de programatori;
• uşurinţa de a utiliza aplicaţii software, cum ar fi 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.
În 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
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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”). Într-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;
• 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 poate fi pusă sub semnul întrebării. Această situaţie este cunoscută sub numele de
„eroarea examenului şcolar”;
• Contextul de lucru: În 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 să 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 sarcină pe care trebuie să o îndeplinească
programatorul. Acesta trebuie să conceapă programul, să-l testeze, să-l 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 al memoriei 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ă. Capacitatea ei se
estimează la aproximativ 7 unităţi.
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 6 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ă?

2
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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).
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 memoria 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 alte domenii utile. 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ştia sunt primii paşi spre obţinerea soluţiei.
În a doua etapă se utilizează tehnici de rafinare pas cu pas şi proiectare top-down. 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.
În 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 0 şi la care, în cadrul buclei, se adună
următoarea valorare 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
3
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

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 programelor.
Când studiem codul unui program, construim o structură semantică internă care să corespundă
programului. Elementele menţionate mai sus uşurează procesul.

1.1.1 Comentariile

Experţii nu sunt întru totul de acord cu tipul şi 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: în cazul în care comentariile nu sunt rescrise în momentul modificării
codului, 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ă orientată obiect. 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 orientată obiect 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 = 0; // x devine 0

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

4
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

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

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 constrânge 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

5
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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.

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-l 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. În 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
e-mail, 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 „ ↑ 10 2 ” pentru a obţine 10 2 , 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.

6
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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.
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. În figura 1, 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ă-l î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ă;
7
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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ă-l informeze despre progresul acţiunii;
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ă-l întrebe dacă vrea să-
şi salveze mai întâi datele.

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. În consecinţă,
prin codul scris, programatorii au ocazia să-şi comunice valorile.

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;
8
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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

2.2. Legea drepturilor de autor

În 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.
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 còpii 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ă.

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. În 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
9
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

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.
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 îndeplinite:
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 1 ş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 1 ş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 1 ş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 Subsecţ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
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm

3. Concluzii
În 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.

11
Referinþe

[1] Ambler, S. W., The Diagrams of UML 2.0, http://www.agilemodeling.com/essays/


umlDiagrams.htm, 2003
[2] Apache Org., APR's Version Numbering, http://apr.apache.org/versioning.html
[3] Brooks, F. P., No Silver Bullet: Essence and Accidents of Software Engineering, Computer 20 (4),
10-19, 1987, http://www-inst.eecs.berkeley.edu/~maratb/readings/NoSilverBullet.html
[4] Caþichi, E., Protecþia drepturilor de autor asupra programelor de calculator,
http://www.microsoft.com/romania/antipiraterie/informlegale.mspx
[5] Cederqvist, P. et al, Version Management with CVS,
http://projects.semwebcentral.org/docman/view.php/6/3/cederqvist-1.11.6.pdf
[6] Eckel, B., Thinking in C++, Prentice Hall Ptr, 1995
[7] Eckel, B., Thinking in Java, Prentice Hall Ptr, 1998
[8] Gheorghieº, O., Apetrei, A., Ingineria programãrii, http://thor.info.uaic.ro/~ogh/ip/index.php, 2002
[9] GNU, Licenses, http://www.gnu.org/licenses/licenses.html
[10] Johnson, K., Software Cost Estimation: Metrics and Models, Department of Computer Science,
University of Calgary Alberta, Canada,
http://sern.ucalgary.ca/courses/seng/621/W98/johnsonk/cost.htm
[11] Johnson, K., Software Cost Estimation: Metrics and Models, Department of Computer Science,
University of Calgary Alberta, Canada, http://sern.ucalgary.ca/courses/seng/621/
W98/johnsonk/cost.htm
[12] LeRoi Burback, R., Software Engineering Methodology: The WaterSluice, PhD Thesis, Stanford
University, http://www-db.stanford.edu/~burback/watersluice, 1999
[13] Lethbridge, T. C., Laganière, R., Object-Oriented Software Engineering: Practical Software
Development using UML and Java, McGraw Hill, 2001
[14] Naur, P., Randell, B. (eds.), Software Engineering, Report of a conference sponsored by the NATO
Science Committee, Garmisch, Germany, 7-11 Oct. 1968, Brussels, Scientific Affairs Division,
NATO (1969), http://homepages.cs.ncl.ac.uk/brian.randell/NATO/nato1968.PDF
[15] Object Management Group, Unified Modeling Language, v1.5, http://www.omg.org/cgi-
bin/apps/doc?formal/03-03-01.pdf, 2003
[16] Randell, B., Buxton, J. N. (eds.), Software Engineering Techniques, Report of a conference
sponsored by the NATO Science Committee, Rome, Italy, 27-31 Oct. 1969, Brussels, Scientific
Affairs Division, NATO (1970), http://homepages.cs.ncl.ac.uk/brian.randell/NATO/nato1969.PDF
[17] Sommerville, I., Software Engineering, Addison-Wesley Publishing Company, 2000
[18] SmartForce Ireland Ltd & Classic Systems Solutions, GUI Design Fundamentals, 1999
[19] UML - Frequently Asked Questions - http://www.rational.com/uml/gstart/faq.jsp
[20] UML Resource Center, Unified Modeling Language, Standard Software Notation,
http://www.rational.com/uml/resources/quick/uml_poster.jsp
[21] Van Vliet, H., Software Engineering, John Wiley & Sons, 1993
[22] Wampler, B. E., The Essence of Object-Oriented Programming with Java and UML, Addison-
Wesley Professional, 2001
[23] Zakon, R. H., Hobbes' Internet Timeline v8.0, http://www.zakon.org/robert/internet/timeline, 2005

S-ar putea să vă placă și