Sunteți pe pagina 1din 198

Ingineria sistemelor

de programe
Note de curs

Florin Ostafi
INTRODUCERE

Ingineria software a parcurs o cale lungă începând cu 1968, an în care acest termen a fost
utilizat pentru prima oară la o conferinţă NATO. Iar de atunci software-ul a pătruns în viaţa
fiecăruia dintre noi în diverse moduri, aşa cum puţini anticipaseră chiar cu un deceniu în urmă.
Aşadar cunoaşterea noţiunilor de bază legate de teoria şi practica ingineriei software este esenţială
pentru înţelegerea tehnicilor de construire a unui software performant şi de asemenea a metodelor
de evaluare a riscurilor şi oportunităţilor pe care software-ul le oferă vieţii noastre de zi cu zi.
Î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 că nici un program pentru calculatoare personale nu va
necesita vreodată mai mult de 64 KB de memorie RAM, Bill Gates a admis î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:
• 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
3700000 linii de cod;
• Programele scrise pentru naveta spaţială NASA au circa 40 de milioane de linii de cod
obiect;
• Pentru realizarea sistemului de operare IBM OS360 au fost necesari 5000 de ani-om.
Pentru a contracara ceea ce se prefigura ca fiind o criză a programării, a fost propus în anul
1968 termenul de “ingineria software” (software engineering), într-un mod oarecum provocator. Se
dorea ca arta programării să împrumute din rigoarea stiinţelor inginereşti pentru a putea livra
programe la timp şi în mod economic.

Definiţii:

IEEE Standard Glossary of Software Engineering Terminology (1990, 1991)


Aplicarea unei abordări sistematice, disciplinate, cuantificabile la realizarea, operarea şi
întreţinerea software-ului.
Fritz Bauer (1968)
Stabilirea şi utilizarea unor principii inginereşti în scopul realizării în mod economic
produse software fiabile care funcţionează eficient pe maşini reale.
Morven Gentlemen (1990)
Utilizarea metodologiilor , instrumentelor şi tehnicilor de rezolvare a problemelor practice
care apar în construcţia , instalarea , întreţinerea şi evoluţia produselor software.
Stephen Schach (1990)
O disciplină având drept obiectiv producţia unui software de calitate, livrat la termen, cu
respectarea bugetului şi care satisface cerinţele stabilite.
Boehm (1979)
Aplicarea practică a cunoştinţelor ştiinţifice în proiectarea şi construcţia programelor şi a
documentaţiei asociate necesare pentru dezvoltarea, operarea şi întreţinerii acestora.
Dennis (1975)
Ingineria software este aplicarea principiilor, aptitudinilor şi arta proiectării si construcţiei
programelor şi sistemelor de programe.
Fairley (1985)
Ingineria software este disciplina tehnică şi managerială având ca obiect producţia
sistematică şi întreţinerea produselor informatice care sunt realizate şi modificate în timp în condiţii
economice.
O definiţie cuprinzătoare a fost prezentată de Bruegge şi Dutoit în Object-Oriented
Software Engineering [2000] conform căreia Ingineria software este:

¾ activitate de modelare - probleme complexe sunt tratate prin modelare, atenţia fiind
concentrată asupra detaliilor semnificative si ignorând restul.
• modelul - o abstractizare a realităţii
• analiza - construcţia unui model al domeniului problemei
• proiectarea - construirea unui model pentru domeniul soluţiei
În metodele OO, modelul domeniului soluţiei este o extensie a modelului domeniului
problemei, astfel încât structura software-ului reflectă structura problemei.

¾ activitate de rezolvare a problemelor - modelele folosite pentru a căuta o soluţie acceptabilă


sunt:
• efectuarea de experimente
• reutilizarea unor soluţii model (reuses pattern solutions)
• evoluţia incrementală a sistemului spre o variantă acceptată de client
• revizuirea răspunsului la schimbări

¾ activitate de achiziţionare de informaţie-în modelare aplicaţiei şi a domeniului soluţiei, se


colectează datele, se organizează in informaţii şi se formalizează în cunoştinţe. Această
activitate este neliniară în sensul că achiziţia de noi informaţii poate invalida cunoştinţele
precedente şi se caracterizează prin:
• dezvoltare bazata pe risc - identificarea componentelor cu risc mare pentru a evita
surprizele ulterioare
• dezvoltare pe probleme (issue-based development) - execuţia in paralel a activităţilor de
dezvoltare, organizarea făcându-se ţinând cont de problemele care sunt încă nerezolvate
• dezvoltare iterativa – proiectarea si implementarea la început a părţilor cu risc ridicat
(dificile)

¾ activitate raţională-logică – realizatorii de software trebuie să înţeleagă contextul în care au


fost luate deciziile şi logica ce stă in spatele lor pentru a înţelege implicaţiile unei schimbări
propuse atunci când decizia este reanalizată; utilă în cazul unor sisteme care să schimbă în mod
frecvent precum şi utilă in etapa de întreţinere

Clasificarea aplicaţiilor software

• Sisteme de operare şi software de sistem


• Software timp real – restricţii timp de răspuns
• Sisteme informatice – baze de date
• Software ştiinţific
• Software inclus (ascensoare, telefoane, aparatură casnică)
Anthony Wasserman identifică opt noţiuni fundamentale ce formează baza operativă a disciplinei
Ingineria sistemelor software:

1. Abstractizarea – o descriere a problemei la un anumit nivel de generalizare ce ne permite să ne


concentrăm atenţia asupra aspectelor cheie ale problemei fără a ne pierde în detalii.
2. Metode şi Notaţii în proiectare şi analiză - când se lucrează în echipă, comunicarea între
membrii echipei şi documentarea în procesul de dezvoltare face necesară stabilirea unui sistem de
notaţie comun.
3. Prototipizare - construirea unei versiuni miniaturale a sistemului, de obicei cu funcţionalitate
limitată, care ajută utilizatorul să identifice cerinţele esenţiale ale sistemului şi demonstrează
fezabilitatea unui anumit mod de abordare/proiectare; utilizată în special pentru proiectarea
interfeţelor utilizator.
4. Arhitectura sistemului (software) - descrierea sistemului ca o mulţime de unităţi arhitecturale şi
stabilirea legăturilor între unităţi.
5. Procesul software – organizarea şi disciplina în activităţile ce se desfăşoară în timpul procesului
de dezvoltarea software-ului contribuie la calitatea software-ului şi rapiditatea cu care este
dezvoltat.
6. Refolosirea - evidenţierea părţilor comune cu alte aplicaţii şi refolosirea unor componente
dezvoltate anterior.
7. Măsurători - descrierea cantitativă a îmbunătăţirii proceselor, resurselor şi metodelor permite
compararea progresului intre proiecte disparate şi ajută la analiza şi luarea deciziilor.
8. Instrumente şi Mediu integrat (Tools and Integrated Environment) –instrumentele CASE
(computer-aided software engineering) sunt proiectate pentru a spori dezvoltarea soft-ului dar
rareori se adresează unui ciclu întreg de dezvoltare a software-ului

Complexitatea sistemelor software derivă din 4 elemente:

1) complexitatea domeniului problemei.

Problemele care cer o rezolvare software sunt deosebit de complexe, începând chiar cu
specificaţiile care pot fi contradictorii (ex: specificaţiile pentru construirea unui robot).
Funcţionalitatea acestor sisteme este suficient de greu de înţeles şi la aceasta se mai adaugă şi
cerinţele nefuncţionale pe care trebuie să le îndeplinească sistemul: performanţă, utilitate, fiabilitate,
cost, etc.
Această complexitate provine şi din neînţelegerile care există între proiectanţii sistemului şi
utilizatorii săi: utilizatorii, de obicei, nu-şi pot exprima clar cerinţele într-o formă pe care
proiectanţii să o înţeleagă. Uneori, ei au doar o vagă idee despre ceea ce doresc de la un sistem.
Practic, aceste probleme apar deoarece fiecărui grup îi lipsesc cunoştinţele despre domeniul celuilalt
grup.
Utilizatorii şi proiectanţii au perspective diferite de a vedea soluţia problemei. Chiar şi
atunci când utilizatorii ştiu ceea ce doresc, lipsesc instrumentele pentru extragerea precisă a
cerinţelor lor. Calea obişnuită prin care aceste specificaţii sunt exprimate constă în a scrie mult text,
incluzând ocazional şi diverse figuri. Dar aceste documente sunt greu de înţeles, sunt deschise la
diverse interpretări, şi deseori, conţin elemente care sunt mai degrabă de proiectare decât de
specificaţiile esenţiale ale problemei.
Complicaţii mai mari apar atunci când specificaţiile sistemului se modifică în timpul
dezvoltării lui. Sistemele mari tind să evolueze de-a lungul timpului, o condiţie ce impropriu este
denumită “întreţinere”. Mai precis, întreţinerea înseamnă corectarea erorilor. Evoluţia înseamnă
modificarea cerinţelor şi menţinerea înseamnă folosirea unor mijloace extraordinare de a păstra în
utilizare un sistem vechi şi depăşit.

2) dificultatea de a administra procesul de dezvoltare

Scopul principal al echipei de software e de a construi iluzia simplităţii, de a feri utilizatorul


de vasta complexitate a problemei. Volumul mare de muncă determină folosirea unei echipe de
dezvoltare a softului. Mai mulţi membri în echipă înseamnă o mai complexă comunicare între ei,
deci o coordonare mai dificilă, mai ales atunci când echipa este dispersată geografic. Într-o echipă
de dezvoltare, cheia coordonării e de a menţine mereu unitatea şi integritatea proiectului.

3) flexibilitatea sistemului

Sistemul software va trebui să fie flexibil. Acest lucru îl forţează pe proiectant de a dezvolta
blocurile primitive din care va construi apoi abstractizările de la nivelele superioare. În timp ce
domeniul construcţiilor industriale oferă standarde pentru calitatea materialelor, puţine astfel de
standarde există în software-ul industrial. Ca urmare, dezvoltarea softului rămâne o muncă intens
laborioasă.

4) problemele de caracterizare a comportării sistemelor discrete

În aplicaţiile foarte mari, pot exista sute sau mii de variabile. Colecţia de variabile, valorile
lor, adresele lor şi stiva de apeluri ale fiecărui proces din sistem, reprezintă starea curentă a
sistemului la un moment dat.
Sistemele discrete (care nu pot fi descrise prin funcţii continue), prin natura lor, au un număr
finit de stări posibile. În marile sisteme, acest număr este imens. De aceea, se încearcă proiectarea
părţilor de sistem astfel încât comportarea uneia să aibă impact minim asupra celorlalte. Totuşi,
orice eveniment extern sistemului soft îl poate plasa într-o nouă stare, şi, mai mult, maparea de la o
stare la alta nu e întotdeauna deterministă. În cele mai rele cazuri, un eveniment extern poate corupe
starea sistemului pentru că proiectanţii au uitat să considere anumite interacţiuni dintre evenimente
(ex: sistemul computerizat, comun pentru cabina de comandă şi cabina pasagerilor, într-un avion ).
Deoarece nu avem nici instrumentele matematice şi nici capacitatea intelectuală de a modela
complet comportarea marilor sisteme discrete, trebuie să ne rezumăm la acceptarea unui nivel
acceptabil de încredere în corectitudinea lor, dovedită în timpul testărilor atente.

Referitor la factorul uman în procesul de dezvoltare a softului, acesta trebuie să ia în


consideraţie foarte multe detalii în acelaşi timp. Experienţe psihologice au arătat că un individ poate
înţelege simultan 7 ± 2 unităţi de informaţii şi că există o limitare a vitezei de procesare a creierului
la 5 secunde pentru acceptarea unei noi informaţii. Şi atunci apare următoarea problemă:
complexitatea softului ce ni se cere să-l proiectăm creşte dar factorul uman prezintă incapacităţi
serioase de a face faţă acestei probleme. Sugestia lui Dijkstra este: “Tehnica de a stăpâni
complexitatea este ştiută din antichitate: divide şi cucereşte ”. În proiectarea unui sistem, este
esenţială descompunerea lui în subsisteme din ce în ce mai mici, fiecare din ele putând fi construit
independent. În felul acesta este rezolvată şi constrângerea impusă de capacitatea umană de
cunoaştere: pentru a înţelege un anumit nivel al unui sistem e nevoie de a înţelege numai câteva
părţi în acelaşi timp.
Ciclul de viată al unui produs software

Produsul software este un produs similar oricărui alt produs. El are atribute fizice ca orice alt
produs fizic, şi deci, el are o "viaţă" similară cu cea a altor produse. De aceea, produsul software
poate fi privit din punctul de vedere al unui ciclu de viaţă, acesta reprezentând toate fazele din
cursul procesului de dezvoltare a unui sistem software, începând de la conceperea sistemului şi până
la retragerea sa din folosinţă.
Fazele clasice în procesul de dezvoltare sunt:
• definirea cerinţelor utilizatorilor
• definirea specificaţiilor software
• programarea sistemului
• implementarea
• testarea
• documentarea

Problemele ingineriei software

Satisfacerea cerinţelor clienţilor. Problema fundamentală a ingineriei software este îndeplinirea


cerinţelor clientului. Aceasta trebuie realizată nu punctual, nu în acest moment, ci într-un mod
flexibil şi pe termen lung. Ingineria software 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 dezirabil ca aceasta să aibă performanţa cât mai bună
(uneori direct evaluabilă), un cost de întreţinere cât mai mic, să fie livrat la timp, şi să fie sigur.
Prezentăm în continuare o statistică privind gradul de satisfacere a cerinţelor pentru proiecte
software mari. Aceasta pare să justifice orice disciplină a muncii care să îmbunătăţească aceste
cifre.

5%
5%

livrate dar nefolosite


20% plătite dar nelivrate
45%
abandonate sau refăcute
folosite după modificări
folosite aşa cum au fost livrate

25%

Figura 1. Gradul de satisfacere a cerinţelor pentru proiecte software mari

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 sigur dacă funcţionează şi continuă să funcţioneze fără întreruperi şi fără a
efectua operaţii nedorite. Un program are o greşeală (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.
Costul produsului. Cheltuielile pentru producerea sistemelor software sunt imense. Se apreciază că
ele au depăşit suma de 140 bilioane $ la nivelul anului 1985 şi urmau să crească de atunci cu 12%
pe an. Costul este influenţat de cerinţele impuse pentru funcţionarea acestuia (exemplu: necesitatea
de a se executa în timp real - timpul tipic de răspuns este de ordinul microsecundelor). De
asemenea, timpul mare de viaţă şi frecventele modificări cerute pentru menţinerea sistemului sau
pentru adăugarea unor noi facilităţi vor mări costurile generale. Se estimează că cel mai mare volum
al cheltuielilor (60%-80%) se înregistrează în cazul întreţinerii softului. Totodată, cu cât produsul
soft este mai complex, mai mare, cu atât costurile totale vor creşte.
Figura 2 se bazează pe un număr de studii care demonstrează distribuţia tipică a timpului
consumat pentru crearea unui sistem software.

20%

40% Testare
Analiză
Proiectare
Implementare

35%
5%

Figura 2: Distribuţia efortului pentru obţinerea produsului soft

După cum se vede, cel mai mult timp e dedicat proiectării şi implementării sistemului.

36%
erori de implemetare
erori de specificare şi analiză

64%

Figura 3: Erori tipice în produsul soft

Alte studii au arătat că cerinţele, specificaţiile şi analiza produsului determină cele mai
multe erori în sistem (64% faţă de 36%) (Figura 3). Mai mult, aceste erori nu sunt descoperite de
către cei care dezvoltă sistemul ci de către cei care le testează şi/sau de către utilizatori. Cu cât
aceste erori sunt evidenţiate mai târziu, cu atât ele implică un mai mare volum de muncă şi cost.
Cel mai mare efort este concentrat în faza de dezvoltare a ciclului de viaţă al produsului deşi
cea mai mare rată de întoarcere o prezintă fazele anterioare acestui punct (cerinţele, specificaţiile
sistemului, analiza).
Varietatea din practică se referă la faptul că există multe tehnici pentru producerea sistemelor
software. Ca rezultat, ele sunt dificil de coordonat, variază mult în ceea ce priveşte costul,
flexibilitatea şi întreţinerea lor.
Şi totuşi există sute de tehnici, automate sau nu, menite să ajute la coordonarea, dezvoltarea
sau întreţinerea sistemelor software. O firească întrebare care se pune este dacă vreuna din aceste
tehnici chiar merge. Studiile realizate au arătat că aplicarea acestor tehnici au determinat scăderea
costurilor şi obţinerea unor sisteme mai flexibile. În realitate, problema este că prea puţini le
folosesc. Este uimitor cât de puţin este folosită tehnologia ingineriei software în realitate. Se pare că
ori nimeni nu citeşte aceste studii, ori nu există interes de a le folosi acolo unde ele ar putea aduce
cele mai mari beneficii.
În primul rând, nimeni nu crede că aplicarea unor tehnici de ingineria programării, şi în
special a celor automate, ar salva într-adevăr nişte bani
În al doilea rând, eforturile pentru cercetarea, dezvoltarea şi aplicarea acestor tehnici sunt
fragmentare. Există multe tehnici, dar ele sunt unice în general, nu pot fi folosite împreună cu alte
tehnici, sau nu merg pentru probleme reale.
O altă problemă este lipsa de consistenţă: nu există un vocabular general acceptat în
domeniul ingineriei programării. Nu există nici un proces sau model unic, general acceptat, de
dezvoltare a sistemelor software. Fiecare companie are modul său propriu de a produce sisteme
software.

Lipsa productivităţii. În domeniul ingineriei software, termenul de productivitate este încă


nebulos. Ea ar putea însemna:
• un volum mai mare de cod scris de către o persoană
• micşorarea costului produsului soft
• creşterea calităţii produsului
Probabil, productivitatea ar trebui văzută ca o sumă a tuturor acestor factori. Studiile
efectuate asupra acestui subiect au arătat că productivitatea depinde de 30 variabile (pregătirea
personalului, complexitatea produsului, cerinţele produsului, timpul de predare, utilitarele folosite
pentru producerea softului, calitatea managementului, limbajul de programare utilizat ). În general,
există o slabă productivitate în producerea sistemelor software datorită lipsei de personal pregătit
pentru aceasta şi datorită cererii crescânde de produse soft.
Un alt motiv pentru care problema productivităţii nu e uşor de rezolvat e “pluralitatea
scopurilor”. Nu există nici un beneficiu dacă personalul a fost suficient de pregătit ca să predea
produsul la timp dar acesta este atât de slab ca performanţă încât e inutilizabil sau dacă va fi nevoie
de mult mai mulţi bani pentru a-l putea face să meargă. Un proiect software îşi propune de multe ori
scopuri contradictorii: este dificil de a realiza un produs flexibil, eficient, utilizabil, extensibil,
modificabil, etc. simultan (in unele sisteme, o mai mare siguranţă a acestuia înseamnă mai multe
verificări care determină un timp mai mare de execuţie sau poate însemna o proiectare mai flexibilă
a sistemului. Acestea pot mări costul produsului, timpul de producere şi pot face sistemul mai greu
de întreţinut.)
O notă finală: un studiu interesant efectuat de Bell Laboratories a arătat că doar aproximativ
13% din timpul unui programator este investit în programare. S-a estimat că 33% din timpul său e
dedicat scrierii documentaţiei. Dacă se pune deci problema creşterii productivităţii, acesta ar putea
fi un punct demn de luat in consideraţie.
In capitolele următoare se va examina o abordare a problemelor cu care se confruntă azi
domeniul ingineriei programării, prin utilizarea mediilor de dezvoltare a sistemelor software. Se
speră ca prin aceasta să se reducă varietatea practicilor, să se crească productivitatea şi, în final, să
se micşoreze costul produsului.

Până în prezent s-au făcut paşi importanţi în tehnologia ingineriei software pe toate
fronturile: analiza cerinţelor produsului, strategiile de implementare, modelele de cost, etc. Fiecare
din acestea urmăreşte să reducă din: costul ridicat al softului, variaţia din practică şi lipsa
productivităţii.

Totuşi, după mai bine de 20 ani “criza softului” nu este rezolvată. Cererea pentru producerea
sistemelor software creşte mai repede decât productivitatea realizării lui. Greşeli care se făceau în
anii 70’ se repetă în continuare. Incapacitatea noastră de a stăpâni complexitatea unui sistem rezultă
în întârzierea proiectului, depăşirea cheltuielilor şi deficienţe în ceea ce priveşte îndeplinirea
specificaţiilor sistemului. E nevoie de utilitare mai bune, de tehnici şi metode mai bune, şi, ceea ce
este cel mai important, e nevoie de educaţie şi antrenare în acest scop.

Principalele soluţii abordate pentru rezolvarea “crizei software-ului”

S-au pus în evidenţă câteva probleme majore privind dezvoltarea ingineriei programării:
costul ridicat al softului, varietatea din practică şi lipsa de productivitate. De asemenea, prăpastia
dintre potenţialul echipamentelor hardware şi performanţele sistemelor software devine din ce în ce
mai mare. Acest potenţial pierdut afectează cel mai mult marile organizaţii care sunt dependente de
sisteme software foarte complexe. Mai rău decât atât, aceste sisteme sunt defectuoase şi structurate
atât de rigid încât nu permit modificări majore ulterioare fără reproiectarea lor.
Câteva din neajunsurile practicilor curente în procesul de dezvoltare a sistemelor software
sunt:
• realizarea unor specificaţii inconsistente, incoerente şi incomplete
• realizarea unor sisteme nereutilizabile
• responsabilităţi şi îndatoriri slab definite şi controlate
• slabe performanţe ale produsului final
• inexistenţa unei metrici pentru testarea calităţii sistemului
• nici un mijloc pentru a coordona complexitatea
• documentaţie care nu oferă nici un ajutor efectiv
• etc.

Rezumăm în continuare principalele eforturi care s-au făcut în direcţia obţinerii unui sistem
software robust, care îndeplineşte toate cerinţele utilizatorilor.

1. Programarea monolitică. Programele mici pot fi construite în stil monolitic, ca o singură


procedură sau un set de instrucţiuni. De obicei, ele sunt scrise de un singur programator, care poate
cuprinde mental întreaga imagine a procedurii. Sistemele mari, însă, nu pot fi realizate în acest fel.
Cu cât sistemul e mai complex, cu atât vor fi necesare echipe mai mari iar volumul de comunicări
între membrii echipei va creşte corespunzător.

2. Programarea modulară. Principiul pe care se bazează este de a împărţi programele în


componente mai mici, ce pot fi construite independent. Acesta e modul de lucru folosit în ultimii 40
ani. Suportul elementar pentru programarea modulară îl reprezintă subrutina, concept introdus în
1950.

3. Programarea structurată. În anii ’60 s-au făcut eforturi mari în direcţia dezvoltării unui stil
de programare mai disciplinat, mai consistent, concretizându-se într-o manieră “top-down” de
proiectare a programelor. Ea se bazează pe principiul descompunerii funcţionale, în virtutea căreia
sistemul e văzut ca un set de sub-sisteme, fiecare sub-sistem fiind la rândul său descompus după
acelaşi principiu. Programarea structurată a adus îmbunătăţiri semnificative în calitatea software-
ului în ultimii 20 de ani. O problemă serioasă însă este că nu întotdeauna este posibilă anticiparea
proiectului sistemului complet, înainte de implementare, şi în cazul unei erori, întregul proiect
trebuie revizuit în aceeaşi manieră top-down.

4. CASE- Computer-Aided Software Engineering

Ultima inovaţie în programarea structurată este ingineria software asistată de calculator. Cu


ajutorul mediilor CASE se coordonează procesul de descompunere funcţională, definind grafic
diagrame înlănţuite şi verificând că toate interacţiunile sistemului reflectă regulile impuse.
Sistemele avansate CASE pot realiza un sistem complet din diagramele proiectate şi din
toate informaţiile asociate lor. Totuşi, acest proces nu e chiar atât de automatizat cum pare la prima
vedere. Practic, CASE traduce proiectul unui sistem din formă grafică într-o formă textuală.
Experienţa de până azi arătat că dezvoltarea unui proiect complet, grafic, poate fi la fel de costisitor
din punct de vedere al timpului ca şi programarea lui.

5. Limbajele de generaţia a IV-a (4GL) reprezintă o altă abordare a programării automate. Ele
includ o gamă largă de utilitare pentru automatizarea unor aplicaţii de rutină (crearea de menuri,
generarea de rapoarte, etc.). Au avantajul că pot fi folosite şi de ne-programatori. Nu pot fi folosite
pentru sisteme mari şi pentru menţinerea programelor modificate

6. Abordarea orientată-obiect
Analiza orientată obiect este o metodă relativ nouă, de analiză bazată pe obiecte şi clase. Un
obiect este o entitate, o încapsulare a unor valori de atribute şi a unui set de operaţii acţionând
asupra acestora. O clasă este o descriere a unui set de obiecte cu atribuţii şi servicii comune.

Medii pentru dezvoltarea sistemelor software (Software Development


Environments)

Un mediu pentru dezvoltarea sistemelor software este o colecţie de utilitare software şi


hardware pentru producţia de sisteme software într-un domeniu specific. Există trei tipuri de astfel
de medii:
• medii de programare - pentru programare, testare, depanarea programelor;
• medii CASE - orientate spre fazele de definire a specificaţiilor software şi a proiectării
sistemelor;
• medii de inginerie software - dedicate producţiei de sisteme software complexe, cu un ciclu
îndelungat de viaţă, a căror întreţinere costă mai mult decât dezvoltarea lor şi care sunt
produse de echipe şi nu de programatori individuali;

Acestea furnizează suportul pentru toate activităţile de dezvoltare şi de management. În


practică, graniţele dintre aceste tipuri nu sunt bine delimitate.

Mediile CASE - sunt destinate diferitelor fazelor din dezvoltarea sistemelor. Aceste medii sunt
orientate spre un suport grafic care e folosit în diverse metode de proiectare. Ele pot suporta fie o
singură metodă, fie o gamă de câteva din cele mai cunoscute metode. Componentele tipice într-un
mediu CASE sunt:
• un sistem de editare a diagramelor pentru crearea diagramelor fluxurilor de date, a
hărţilor de structură, a diagramelor de tip entitate-relaţie (entity-relationship), a
dicţionarelor de date, a arborilor de decizie, etc.; informaţiile despre aceste identităţi sunt
înregistrate într-o bază de date centrală (enciclopedie);
• facilităţi de analiză şi verificare în timpul creării diagramelor;
• facilităţi ce permit utilizatorului de a regăsi informaţiile;
• dicţionarul de date, conţinând informaţii despre entităţile folosite în proiectul sistemului;
• facilităţi de generare a rapoartelor care folosesc informaţiile din baza centrală de date şi
generează documentaţia sistemului;
• utilitare de generare a formatelor ecran şi document;
• facilităţi import-export pentru schimbul de informaţii dintre baza centrală de date şi alte
utilitare de dezvoltare;
• unele medii CASE suportă generatoare de cod pe baza informaţiilor de proiectare
înregistrate în dicţionarul de date.

Unele din criticile care au fost aduse acestor medii sunt următoarele:
• nu există o standardizare care să facă informaţia interschimbabilă dintre diverse medii
CASE;
• nu există facilităţi care să permită utilizatorului să modifice regulile implicite ale
metodei pe care mediul o suportă, şi să o adapteze problemei sale specifice;
• nu sunt facilităţi suficiente pentru crearea unei documentaţii de calitate;
• facilităţile oferite pentru realizarea diagramelor determină o greoaie creare a acestora
(diagrame nu foarte complicate pot necesita câteva ore de lucru); este necesar un mod
automatizat de creare şi aranjare a diagramelor, dat fiind un text de intrare;
• suportul pentru specificaţiile formale este sărac;
• suportul pentru proiectarea orientată-obiect este sărac; de aceea, avantajele
productivităţii şi scăderii costului sistemelor soft prin folosirea unor tehnici ca analiza
orientată obiect sunt negate cel puţin datorită unor utilitare inadecvate.

Unele din aceste deficienţe au fost deja remediate. În prezent, mediile CASE sunt valabile
pentru IBM-PC sau pentru sisteme UNIX
Unele medii permit transferul de date de la şi spre alte aplicaţii şi pot genera documente de
calitate PostScript. În continuare e dificilă transferarea informaţiilor din baza centrală de date a unui
mediu CASE către altul, ceea ce obligă păstrarea mediului cu care a fost construit un sistem pentru
a putea continua întreţinerea acestuia.
O deficienţă serioasă a mediilor CASE, din punct de vedere al ingineriei sistemelor software
pe scară largă, este dificultatea integrării lor cu sistemele de coordonare al configuraţiei
(“configuration management system”). Sistemele foarte mari au un ciclu de viaţă îndelungat, ca
urmare ele se pot modifica mereu, existând în versiuni diferite. Sistemul pentru coordonarea
configuraţiei permite regăsirea unei anumite versiuni, permite construirea unui sistem din
componente şi menţine legăturile dintre aceste componente şi documentaţia lor. Mediile CASE
furnizează facilităţi pentru proiectarea sistemului software dar aceste proiecte nu sunt automat
legate de codul sursă rezultat şi documentaţia utilizatorului. Ca urmare, multiplele versiuni ale unui
sistem nu pot fi uşor coordonate.
Avantajele mediilor CASE:
• cu toate că experienţa în folosirea acestor medii nu e foarte îndelungată, unii utilizatori
au declarat o reducere a timpului necesar în dezvoltarea sistemului şi o creştere a
productivităţii;
• documentaţia este actualizată după orice modificare în proiectul şi codul sistemului;
• modificările sunt mai uşor de realizat; o modificare a cerinţelor utilizatorilor determină o
modificare simplă în codul sursă, prin folosirea acestor medii;
• activitatea de întreţinere este mult simplificată, deoarece documentaţia este realizată
odată cu dezvoltarea sistemului şi datorită unei abordări disciplinate pentru dezvoltarea
sistemului;
• oferă un limbaj comun tuturor celor implicaţi în proiect.

Pe măsură ce standardele pentru construirea acestor medii sunt definitivate şi publicate,


mediile CASE vor evolua spre mediile ingineriei software.

Mediile de inginerie software - sunt colecţii de utilitare software care acţionează într-un mod
integrat.

Caracteristicile mediului de inginerie software:

• Facilităţile mediului sunt integrate. Toate utilitarele sunt interfaţate cu un sistem de


management al obiectelor (OMS), astfel încât ieşirea unui utilitar reprezintă intrarea în altul.
• Posibilitatea coordonării mai multor versiuni ale aceluiaşi sistem software. Toate produsele
rezultate în cursul procesului de dezvoltare pot face subiectul managementului de
configuraţie (care asigură asocierea corectă a tuturor documentelor-specificaţii, proiect, cod,
documentaţia utilizatorului, etc. - corespunzătoare unei versiuni, completitudinea şi
consistenţa lor).
• Furnizează facilităţi pentru suportarea tuturor activităţilor de dezvoltare software
(specificarea, proiectarea, implementarea, testarea, documentarea, depanarea, etc.).

Concluzii
Se pare că un mediu pentru dezvoltarea sistemelor este mai util în perioada următoare
finalizării şi predării sistemului, perioadă care reprezintă 80% din cheltuielile totale ale sistemului şi
90% din ciclul de viaţă al acestuia.
Folosirea mediilor de dezvoltare a sistemelor software pot ajuta în mare măsură la
rezolvarea problemelor cu care se confruntă procesul de realizare a produselor software. După o
perioadă de timp, cheltuielile pentru producerea sistemelor vor scădea iar calitatea produselor
software va fi îmbunătăţită.
Ele nu reprezintă totuşi un panaceu pentru această problemă. Mediile de dezvoltare nu pot
înlocui gândirea, nu pot garanta o coordonare eficientă a proiectului, nu pot înlocui fireştile erori
umane, nu pot garanta că ştim ceea ce dorim să facem, etc.
Cap. 1

Modele de dezvoltare a programelor


Pentru dezvoltarea unui program avem nevoie de:
• înţelegerea clară a cerinţelor;
• un set de metode şi instrumente de lucru;
• un plan de acţiune.

Planul de acţiune se numeşte model 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.
Există o serie largă de modele de dezvoltare:
• ad-hoc (do-it-yourself)
• cascadă (waterfall)
• prototipizare
• metode formale
• spirală
• RUP (Rational Unified Process)
• XP (Extreme Programming)

1.1 Etapele dezvoltării programelor

Î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
• proiectarea arhitecturală
• proiectarea detaliată
• scrierea codului
• integrarea componentelor
• validare
• verificare
• întreţinere
¾ 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. În mod
tipic, aceasta se realizează modular, pe structura rezultată la proiectarea arhitecturală.

¾ Integrarea componentelor. Modulele programului sunt combinate în produsul final.


Rezultatul este sistemul complet.

Modelul big-bang. În acest model, componentele sunt dezvoltate şi testate


individual. Apoi ele sunt integrate în sistemul final. Având în vedere că funcţionarea
corectă a componentelor individuale a fost testată, integrarea ar trebui să fie o
formalitate. Din păcate, componentele nu pot fi testate exhaustiv, iar când acestea
lucrează împreună pot să apară situaţii pe care o anumită componentă nu le-a întâlnit
în procesul de testare sau conflicte între anumite componente (de exemplu, conflicte
de partajare a resurselor). S-a 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. Acest model 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.

¾ Validare. În procesul de validare ne asigurăm că programul îndeplineşte cerinţele


utilizatorului. Întrebarea la care răspundem este: construim produsul corect (Are we
building the right product)?
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.

¾ Verificare. Î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 (Are we building the product right)?

¾ Întreţinere. 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.
1.2 Modelul cascadă (waterfall)

Modelul cascadă defineşte următorii paşi în dezvoltarea unui program:


• Specificarea cerinţelor
• Proiectarea arhitecturală
• Proiectarea detaliată
• Scrierea codului
• Testarea componentelor
• Testarea sistemului
• Acceptarea proiectului

Nu se stipulează cum se fac aceşti paşi (metodologie, notaţii), ci doar ordinea efectuării lor.
Avantajul metodei de dezvoltare în cascadă este acela că 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.)

Ingineria
cerinţelor

Proiectarea
arhitecturală

Proiectarea
detaliată

Implementare

Testarea
unităţilor

Testarea
sistemului

Acceptarea
proiectului

Figura 1.1: Modelul de dezvoltare în cascadă


1.3 Modelul cascadă cu întoarcere

Unul din dezavantajele modelului cascadă este că într-un anume stadiu al dezvoltării nu se
poate influenţa rezultatul obţinut într-un stadiu precedent pentru a se remedia o problema găsită. De
exemplu, la testare se poate descoperi o eroare de proiectare.
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 însă 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ă este acela că un client obţine o viziune
practică asupra produsului doar în momentul terminării procesului de dezvoltare.

Ingineria
cerinţelor

Proiectarea
arhitecturală

Proiectarea
detaliată

Implementare

Testarea
unităţilor

Testarea
sistemului

Acceptarea
proiectului

Figura 1.2: Modelul de dezvoltare în cascadă cu întoarcere


1.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ţionala a sistemului.
Această versiune nu este î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 (throwaway)
• 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, în mod tipic 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.

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;
• se poate facilita instruirea utilizatorilor finali înainte de terminarea produsului.

Dezavantaje:
• 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ă.
1.5 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 într-un proces incremental.

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

Dezavantaje:
• specificarea formală este de obicei o barieră de comunicare între client şi analist;
• necesită personal foarte calificat (deci mai scump);
• folosirea impecabilă a tehnicilor specificării formale nu implică neapărat obţinerea de
programe sigure, deoarece anumite aspecte critice pot fi omise din specificaţiile iniţiale.

1.6 Modelul în spirală

Modelul în spirală încearcă să rezolve problemele modelului în cascadă, păstrând avantajele


acestuia: planificare, faze bine definite, produse intermediare. De asemenea, se poate folosi
prototipizarea dacă se consideră necesar.
Modelul în spirală 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 şi î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;
• firma concurentă lansează un program rival pe piaţă;
• un dezvoltator / arhitect părăseşte echipa de dezvoltare;
• echipa nu respectă un termen de livrare pentru o anumită componentă
• clientul schimbă cerinţele.

În modelul spirală (figura 1.3) 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 ex. analiza specificaţiilor sau scrierea
de cod)
• planificarea următorului stadiu: termenele limita, resurse umane, revizuirea stării
proiectului.

1: pregătirea 2: gestiunea riscului


[take stock] [dealing with risk]

4: planificarea următorului 3: dezvoltarea


stadiu [development]
[planning]

Figura 1.3: Modelul de dezvoltare în spirală

1.7 Rational Unified Process (RUP)

Rational Unified Process (Procesul unificat [de dezvoltare al firmei] Rational) defineşte
următoarele activităţi fundamentale:
• Ingineria funcţionalităţii. Sunt sintetizate necesităţile funcţionale.
• Cerinţe. Se translatează necesităţile funcţionale în comportament de sisteme automate.
• Analiza şi Proiectare. Se translatează cerinţele în arhitectura programului.
• Implementare. Se creează programul conform cu arhitectura astfel încât comportamentul
acestuia sa fie apropiat de cel dorit.
• Testare. Se asigură că comportamentele cerute sunt corecte şi că toate comportamentele
necesare sunt prezente în program.
• Administrarea configuraţiei şi a schimbărilor. Se gestionează versiunile tuturor
entităţilor din care este compus programul.
• Administrarea proiectului. Sunt administrate planificările şi resursele.
• Administrarea mediului. Se instalează şi se menţine mediul de lucru necesar dezvoltării
programului.
• Plasament. Se efectuează activităţile necesare punerii în funcţiune a programului.

Figura 1.4: Activităţile în cadrul RUP

Aceste activităţi nu sunt separate în timp (cum se presupune, de exemplu, în cazul modelului
în cascadă). Aici, ele sunt executate în paralel, pe parcursul întregului proiect. După cum reiese din
figura 1.4 cantitatea de cod scrisă la începutul proiectului este mică, însă nu zero. Pe măsură ce
proiectul evoluează, cele mai multe dintre cerinţe devin cunoscute, însă noi cerinţe sunt identificate:
aceasta înseamnă că activitatea cerinţe se regăseşte pe întreaga durată de viaţă a procesului, însă
apare cu pregnanţă la începutul acestuia.
Aşadar, pe măsură ce procesul de dezvoltare evoluează, importanţa anumitor activităţi scade
sau creşte, însă este permis ca aceste activităţi să se execute la orice moment al dezvoltării.
Un proiect bazat pe RUP evoluează în paşi numiţi iteraţii. Scopul unei iteraţii este să
dezvolte un program funcţional care să poată fi prezentat clientului, iar clientul să îl poată evalua.
Programul obţinut la sfârşitul unei iteraţii ar trebui să conţină elemente din toate compartimentele
programului, chiar dacă aceste compartimente nu sunt implementate complet.
Întinderea în timp a unei iteraţii depinde de tipul de proiect la care se lucrează, de experienţa
echipei etc. Este însă de preferat ca iteraţiile să fie cât mai scurte. Dacă o iteraţie este scurtă, echipa
de dezvoltare are oportunitatea de a primi mai repede reacţii din partea clientului. Iteraţiile care
durează o săptămână sau două sunt considerate acceptabile ca întindere în timp. Iteraţiile pot fi
folosite pentru a estima timpul necesar dezvoltării proiectului. Dacă, de exemplu, mai sunt de
proiectat încă 20 de iteraţii, iar până în momentul la care se face estimarea timpul de dezvoltare a
fost de 2 săptămâni pe iteraţie, putem să apreciem că proiectul va mai dura aproximativ 40 de
săptămâni. Acest tip de informaţie poate fi de mare folos administratorilor şi clienţilor. Iniţial
estimările sunt nesigure, însă pe măsura ce proiectul avansează, ele devin din ce în ce mai precise.
Se disting patru faze în RUP:
• Pornire
• Rafinare
• Construire
• Tranziţie

Pornire. În faza de pornire, scopul principal al iteraţiilor este să ajute echipa de dezvoltare
să stabilească obiectivele esenţiale ale proiectului. Se vor explora arhitecturi alternative şi soluţii
tehnice posibile. În urma acestei activităţi, se obţin următoarele:
• enumerare a cerinţelor principale, posibil în formă de cazuri de utilizare;
• imagine de ansamblu asupra arhitecturii programului;
• descriere a obiectivelor proiectului;
• un plan preliminar de dezvoltare.

Rafinare. În faza de rafinare se stabileşte o înţelegere detaliată a problemei care trebuie


rezolvată, se hotărăşte arhitectura programului, se stabileşte echipa de lucru, se elimină situaţiile cu
risc mare.
Această activitate produce următoarele:
• un prototip evoluţionar al arhitecturii programului;
• teste care verifică funcţionarea programului;
• cazuri de utilizare care descriu majoritatea funcţionalităţilor sistemului;
• un plan de proiect detaliat pentru iteraţiile următoare.

Construire. Iteraţiile din faza de construire au ca rol adăugarea se diverse caracteristici


programului dezvoltat. În această fază se aşteaptă ca utilizarile oferite de client să se stabilizeze în
mare măsură, deşi în unele aplicaţii este posibil ca ele să sufere oarecare modificări.
Cazurile de utilizare sunt adăugate unul câte unul, iteraţie cu iteraţie la program, până când
clienţii pot utiliza programul într-un mod apropiat de cel aşteptat.
Activitatea de construire produce următoarele:
• Programul propriu-zis
• Teste
• Manuale de utilizare

Tranziţie. În cadrul acestei activităţi programul este îmbogăţit mai departe cu caracteristici,
însă accentul se pune pe îmbunătăţirea şi rafinarea celor existente. Sfârşitul acestei activităţi şi a
întregului proces de dezvoltare are loc atunci când:
• Obiectivele propuse în cadrul fazei de pornire sunt îndeplinite;
• Clientul este satisfăcut.

De remarcat că a doua situaţie nu se suprapune peste prima. Adesea pot să apară cerinţe ce
nu au fost propuse iniţial.

1.8 Extreme Programming

Extreme Programming (XP, programare extremă) este o metodologie nouă de dezvoltare,


inspirată din RUP, dar care propune rezolvări originale problemelor care apar în dezvoltarea de
programe. Fiind o tehnologie nouă (şi extremistă) are adepţi şi detractori.
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ă
cunoaştere ş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 î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ă ne păcălim să credem asta. Dacă inginerului constructor respectiv i s-ar schimba cerinţele de
rezistentă, i s-ar muta malurile şi i s-ar schimba râul din Siret în Amazon, pentru frumuseţe 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, Jon Jeffries şi Kent Beck definesc următoarele două carte, ca bază filozofică
pentru această metodologie.

Carta drepturilor dezvoltatorului


• Ai dreptul să ştii ceea ce se cere, prin cerinţe clare, cu declaraţii clare de prioritate.
• Ai dreptul să spui cât îţi va lua să implementezi fiecare cerinţă, şi să îţi revizuieşti
estimările în funcţie de experienţă.
• Ai dreptul să îţi accepţi responsabilităţile, în loc ca acestea să-ţi fie asignate.
• Ai dreptul să produci 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 tu le specifici.
• 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 înţelese de la sine, 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 echipa, 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 întâi cod de test.
• Dacă apare necesitatea rescrierii sau aruncării de cod, 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.
Cap. 2

Ingineria cerinţelor

Primul pas logic în dezvoltarea unui program este stabilirea precisă a cerinţelor clientului
(ceea ce clientul vrea ca programul să facă). Partea ce 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ă,
după cum 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 ca produsul să facă, 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
Programatorii nu pot şi nu au cum să cunoască necesităţile clienţilor, mai ales dacă nu
cunosc domeniul pentru care o anumită aplicaţie este scrisă. Este responsabilitatea clientului de a
veni cu cereri exacte şi pertinente. Este obligaţia inginerului de cerinţe de a discuta cu clientul
pentru a clarifica cerinţele şi a ajuta clientul să-şi fixeze priorităţile.
Stabilirea precisa a cerinţelor este primul pas în obţinerea unui program care satisface
cerinţele clientului. O specificaţie bună este folosită şi în fazele de validare şi verificare.

De ce sunt cerinţele utilizatorilor atât de importante?

În 1994 compania Standish Group a supravegheat peste 350 de companii care au produs
peste 8000 de proiecte software şi a observat că 31 de procente dintre proiecte au fost abandonate
înainte de a fi terminate. Mai mult, în marile companii, doar 9% au fost livrate la timp şi la costul la
care au fost contractate, iar în companiile mici procentul s-a ridicat doar la 16%. În urma
cercetărilor efectuate s-a stabilit următorul clasament al factorilor care au dus la această situaţie:
1. cerinţe incomplete (13.1%)
2. lipsa implicării utilizatorilor (12.4%)
3. lipsa resurselor (10.6%)
4. aşteptări nerealiste (9.9%)
5. lipsa suportului executivului (9.3%)
6. schimbarea cerinţelor şi a specificaţiilor (8.7%)
7. lipsa planificării (8.1%)
8. sistemele nu au mai fost cerute în întregime (7.5%)

Lipsa prevederii în înţelegerea , documentarea şi managementul cerinţelor duce la o


multitudine de probleme: construirea unui sistem care rezolvă o problemă pusă greşit, care nu
funcţionează după aşteptări, sau care este greu de înţeles şi de utilizat de către clienţi. Mai mult, un
proces de extragere a cerinţelor nesatisfăcător poate fi scump. Boehm şi Papaccio (1988) au arătat
că dacă găsirea şi rezolvarea problemelor referitoare la cerinţe în faza de definire a cerinţelor costă
1$ , aceeaşi operaţie costă 5$ dacă are loc în faza proiectării, 10$ în faza scrierii codului, 20$ în faza
de testare şi mai mult de 200$ după livrarea sistemului.
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 30km/h. 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.
Controversă: este bine să facem specificarea luând în considerare felul în care sistemul va fi
implementat?
1. Nu folosim detalii de implementare. Motivaţia: 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?
2. Folosim detalii de implementare. Câteodată nu este posibil să ignoram 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. Este o diferenţă între a programa în Visual Basic şi a programa în 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, este posibil ca respectivele costuri să crească (întreţinere greoaie, probleme de
performanţă).
În legătură cu specificaţia pentru locomotivă, remarcăm că indică cerinţe, nu mod de
implementare. Cerinţele sunt testabile (cu instrumente de măsură specifice) şi sunt clare
(neambigue). Remarcăm însă că este incompletă: nu specifică nimic despre costul sau termenul
limită de realizare.
Ca să vedem dacă o specificaţie este bună ar trebui să ne întrebam dacă:
• spune ce şi nu cum
• este clară
• este suficient de detaliată
• este completă
Pentru comparaţie, considerăm următorul exemplu de specificaţie: Scrieţi un program
Pascal care oferă funcţionalitatea unei agende telefonice personale. Ar trebui să implementeze
funcţii pentru căutarea unui număr şi pentru introducerea unui nou număr de telefon. Programul ar
trebui să ofere o interfaţă utilizator prietenoasă.

2. Extragerea cerinţelor
Presupunem că analistul şi clientul lucrează împreună, fiecare încercând să înţeleagă pe
celalalt. 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. în această fază analistul înregistrează cerinţele clientului
• Gândire. în această fază 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 considera 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 care 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.
O altă problema o reprezintă literatura studiată de client, înainte de angajarea echipei de
dezvoltare. Clientul aşteaptă mult prea mult de la program şi de la echipa de dezvoltare.
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.

3. Specificarea cerinţelor
Produsul extragerii cerinţelor şi a analizei este un document de specificare a cerinţelor
(specificaţii). Specificaţiile sunt de o importanţă crucială în dezvoltarea cu succes a unui produs.
Specificaţiile sunt documente de referinţă cu ajutorul cărora se evaluează dezvoltarea programului.
Trebuie avute în vedere:
• nivelul de detaliu
• cui îi este adresat documentul
• notaţia folosită

Este de preferat ca specificaţiile să se restrângă cât mai mult în preajma a ce, nu a cum
trebuie făcut. Ar trebui să surprindă viziunea clientului asupra sistemului. Specificaţiile trebuie
înţelese de două grupuri distincte de persoane: clienţi şi dezvoltatori. Fiecare dintre aceste grupuri
tind să aibă jargoane, domenii de expertiză diferite. Membrii acestor grupuri vor ca specificaţiile să
enunţe exact cea ce trebuie făcut, însă preferinţele pentru limbajul în care se exprimă aceste lucruri
sunt diferite. Clienţii preferă de obicei limbajul natural, în timp ce analiştii tind să folosească o
notaţie precisă (limbaj de modelare).
Notaţiile folosite sunt de mai multe tipuri: informale, formale, semiformale.
În abordarea modernă se fac două seturi de documente. Un set este pentru clienţi, iar un set
pentru dezvoltatori. Problema care apare este actualizarea acestor documente şi menţinerea lor
consistentă.
Un document de specificaţii bun ar trebui să împartă pe categorii în mod explicit cerinţele în
una din următoarele clase:

Cerinţele de capabilitate - definesc o operaţie sau o secvenţă de operaţii corelate, pe care


sistemul trebuie să le îndeplinească.
În faza definirii cerinţelor software, cerinţele de capabilitate vor fi analizate pentru a
produce setul de cerinţe funcţionale.
Cerinţele de capabilitate au următoarele atribute:
• capacitatea - se referă la cât de mult din capabilitatea respectivă e necesar la orice
moment de timp.
Exemple: numărul de terminale, numărul de utilizatori, volumul de date ce va trebui
stocat
• viteza - arată cât de rapid trebuie să se execute o operaţie sau secvenţă de operaţii,
măsurat în număr de operaţii pe unitatea de timp.
Exemplu: “95% din tranzacţii se vor executa în mai puţin de o secundă”
• acurateţea unei operaţii - este măsurată prin diferenţa dintre ceea ce se intenţionează să
execute o operaţie şi ceea ce execută ea în realitate.
Exemplu: “Programul va prezice altitudinea satelitului în limitele (cu o eroare de ) a 10m
cu şapte zile înainte.”

Cerinţe privind datele - se referă la datele de intrare / ieşire din sistem (format, dimensiuni)
şi la datele stocate în interiorul sistemului.

Cerinţele restrictive - impun restricţii asupra îndeplinirii cerinţelor de capabilitate.


Utilizatorul poate impune constrângeri asupra interfeţelor privind comunicarea cu alte sisteme
(interfeţe hardware sau software) sau cu factorul uman (interfaţa utilizator). El poate impune modul
în care trebuie realizate interacţiunile cu alte sisteme dar ar trebui să lase echipei de dezvoltare
libertatea de a le defini pe cele interne (privind interacţiunile dintre componentele software).
Cerinţele pentru interfaţa utilizator pot impune aspecte privind stilul acesteia (limbaj de
comandă, sistemul de menuri, icons), formatul (conţinutul rapoartelor şi afişajelor ecran).
Utilizatorul poate impune constrângeri privind calitatea produsului software, prin
următoarele caracteristici:

• adaptabilitate (flexibilitate) - posibilitatea unui sistem de a fi uşor modificat.


Exemplu: “Trebuie să fie posibil de a adăuga noi comenzi fără a le mai testa pe cele
vechi”.
Observaţie: În ceea ce priveşte adaptabilitatea, orice schimbare presupune un risc şi
modificări ale părţilor sigure ale sistemului ar putea fi inacceptabile.
• disponibilitatea - se referă la posibilitatea sistemului de a fi utilizat în timpul perioadei
sale de operare. Cerinţele de disponibilitate ar putea specifica:
- capacitatea minimă disponibilă (exemplu: “toate terminalele”)
- timpul de început şi de sfârşit al disponibilităţii (exemplu: “de la 9.00 până la
17:30, zilnic”)
- în medie, perioada disponibilităţii.
Exemplu: “toate capabilităţile esenţiale ale sistemului vor fi disponibile în măsură de
cel puţin 98% în orice 48 ore şi în măsură de cel puţin 75% în orice perioadă
de 3 ore ”
Dacă un sistem nu este disponibil (nu poate fi folosit), acest fapt se datorează
pierderii cel puţin a uneia din capabilităţile sale, pierdere numită cădere, fiind cauzată
de erori. Timpul mediu între apariţiile erorilor interne sistemului software (“bugs”)
măsoară siguranţa sistemului.
Timpul mediu necesar pentru a fixa erorile sistemului măsoară posibilitatea
sistemului de a fi întreţinut.
• portabilitatea - reprezintă posibilitatea unui sistem de a fi mutat dintr-un mediu în altul.
Exemplu: “Software-ul va fi portabil între mediile X şi Y”
Portabilitatea poate fi măsurată în număr de linii de cod şi/sau numărul de module care
nu trebuie să se modifice în momentul portării de pe un computer pe altul
• securitatea - sistemul trebuie să fie protejat împotriva propriilor erori ale utilizatorilor,
sau împotriva unor activităţi ilegale, sau împotriva accesului unor utilizatori neautorizaţi.
• siguranţa - consecinţele “căderilor” sistemului trebuie semnalate clar echipei de
dezvoltare. Informaţiile manipulate de sistem trebuie protejate împotriva unor erori ale
sistemului (software sau hardware).
Exemplu: “Sistemul trebuie să asigure faptul că nici una din date nu se va pierde în
eventualitatea unei căderi de tensiune”
• standarde - specifică documentele care definesc standarde:
- ale procesului (exemplu: standardele pentru asigurarea produsului)
- ale produsului (exemple: formate ale fişierelor pentru export, sau formate ale
rapoartelor)
• resurse - reprezintă resursele valabile pentru producerea şi operarea sistemului, în
termeni financiari, de limitări materiale sau de resurse calculator (exemplu: memorie).
Probleme ce pot apare în legătura cu cerinţele:

• Cerinţe vagi: sistemul trebuie să fie prietenos


• Cerinţe contradictorii: toate datele trebuie scrise pe disc; timpul de răspuns 1 s

Succesul unui proces de dezvoltare a unui program se bazează pe extragerea în mod


profesionist a cerinţelor de la client.

4. Metode pentru specificarea cerinţelor utilizatorilor

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.

Formalisme matematice

Formule matematice ar trebui folosite sau referite în DCU, pentru a clarifica o cerinţă. Toate
simbolurile folosite într-o expresie trebuie să fie definite sau referite.

Engleza structurată - 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, în mod normal, folosită pentru a descrie procesele de bază ale
sistemului şi e potrivită pentru exprimarea cerinţelor de capabilitate.
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 procesarea 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.

Tabele

Tabelele reprezintă o metodă efectivă pentru descrierea 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ă.

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.

Diagrame de context

Diagramele de context prezintă intefeţele externe ale sistemului, intrările şi ieşirile sale.
5. Utilitare pentru identificarea cerinţelor utilizatorilor
Chestionarul este principalul utilitar pentru un interviu. Pentru a obţine informaţii utile,
trebuie să se acorde o atenţie deosebită conţinutului şi prezentării sale.
Mediile CASE pot fi utilizate pentru analiză şi proiectare, considerând studiul de
fezabilitate.
Similar, prototipul sistemului poate fi realizat folosind utilitare pentru o proiectare mai
detaliată.
Capturarea cerinţelor utilizatorului pe baza unui sistem software existent ar putea implica
determinarea unor modele care descriu acest sistem (analiza structurată, SSADM). Utilitarele CASE
sunt potrivite pentru construirea acestor modele.

6. Utilitare pentru specificarea cerinţelor utilizatorilor


Utilitarele pentru manipularea informaţiilor - reprezintă cerinţele utilizatorului ar trebui să suporte
una sau mai multe din funcţiile următoare:
- inserarea unor noi cerinţe
- modificarea unei cerinţe
- ştergerea unei cerinţe
- cross-referenţierea
- căutări
- afişări în diverse formate
Pentru sistemele complexe, un sistem de manipulare a bazelor de date devine esenţial în
coordonarea informaţiilor.

Utilitare pentru crearea Documentului Cerinţelor Utilizatorului (DCU) - sunt necesare utilitare
care permit crearea paragrafelor, secţiunilor, tabelelor, cuprinsurilor, etc.
Sunt utile următoarele facilităţi:
- un spell-checker
- afişarea documentului la diferite nivele de detaliu
- programe pentru compararea documentelor, care pot marca automat textele modificate
(foarte necesare în revizuirea documentelor)

7. 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ă a 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 Software
Engineering 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.

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.

Responsabilităţi

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


• definirea clară a 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.

Conţinutul DCU

Structura generală a unui DCU, recomandată de Standardul Ingineriei Software, este


prezentată în continuare:

1. Introducere
2. Descriere generală
3. Cerinţe specifice
Secţiunea 1 trebuie să prezinte 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 prezintă:
• 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 a clasifica aceşti utilizatori
şi de a estima numărul 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 reprezintă 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.
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ă.
Definirea specificaţiilor software

1. Faza definirii cerinţelor software

Introducere

Specificarea cerinţelor reprezintă definirea cerinţelor în termeni tehnici necesari


dezvoltării unui sistem software şi alcătuieşte Documentul Cerinţelor Software (DCS).
Documentul cerinţelor utilizatorilor este echivalentul tehnic al documentului cerinţelor
software. Există situaţii în care există un document unic care serveşte ambelor scopuri,
document realizat împreună de către client, inginerul de cerinţe şi programatori. În cele
mai multe cazuri însă sunt necesare ambele documente. Trebuie o atenţie specială în a nu
se pierde informaţii sau a se schimba cerinţe la interpretarea acestora ca specificaţii
software.
Evident, ar trebui să existe o corespondenţă între fiecare cerinţă din DCU şi
fiecare specificaţie din DCS. DCS reprezintă punctul de vedere al echipei de dezvoltare şi
nu al utilizatorului. Definirea cerinţelor software reprezintă răspunderea echipei de
dezvoltare. La această fază participă şi utilizatori, ingineri de sistem software sau
hardware, personal de operare. Activitatea de management va trebui să asigure că acest
document va fi revizuit de către toţi participanţii la această fază.

Managementul configuraţiei este alcătuit dintr-un set de proceduri care


urmăresc:
- cerinţele care definesc ce trebuie să facă sistemul;
- modulele de proiectare care sunt generate de aceste cerinţe;
- codul programului ce implementează modulele de proiectare;
- teste care verifică funcţionalitatea sistemului;
- documente care descriu sistemul.
În particular, în timpul fazei de definire şi de specificare a cerinţelor software,
managementul configuraţiei detaliază corespondenţa dintre elementele din DCU şi
specificaţiile software. Dacă nu se definesc aceste legături, nu se pot defini cazuri de
testare a codului pentru a verifica dacă respectă cerinţele impuse.

In continuare, se va prezenta foarte sumar un ghid pentru producerea cerinţelor


software, bazat pe recomandările standardelor Software Engineering.
Setul cerinţelor soft va fi alcătuit pe baza DCU şi pe baza modelului logic al
sistemului (care este o descriere abstractă a ceea ce ar trebui să execute sistemul şi nu
trebuie să conţină detalii de implementare).
Cerinţele software şi modelul logic al sistemului vor fi documentate în DCS,
documentul obligatoriu al acestei faze.

1
1.1 Construirea modelului logic al sistemului

Un model logic al sistemului este:


• o descriere simplificată a sistemului
• ierarhic, conform unor criterii consistente de descompunere
• compus din simboluri alese conform unor convenţii
• construit pe baza unor anumite metode şi utilitare
• folosit în analiza sistemelor

Modelul logic al sistemului determină o mai bună înţelegere a cerinţelor software,


considerate ca întreg. El trebuie construit iterativ: revizuiri ale fiecărui nivel al sistemului
trebuie realizate înainte de a trece la următorul nivel de detaliu. Utilitarele CASE se
recomandă pentru obţinerea unor modele clare şi consistente, uşor de construit şi de
modificat.
Tipul modelului logic depinde de metoda folosită. Se vor prezenta pe scurt
metodele potrivite pentru construirea unui model logic.
În cele ce urmează, prezentăm conceptele şi terminologia descompunerii
funcţionale, pentru a arăta cum se construieşte un model logic.

Descompunerea funcţională

Pentru a construi un model logic, sistemul este descompus într-un set de funcţiuni
de bază, cu doar câteva intrări şi ieşiri simple.
Descompunerea funcţională este numită şi metoda “top-down” pentru că începe
descompunerea sistemului de la o singură funcţie, rezultând apoi funcţii la nivele de
detaliu mai mare. Fiecare nivel modelează sistemul la diferite trepte de abstractizare.

Standardele SE definesc următoarele recomandări privind construirea unui model logic:


1. funcţiile trebuie să aibă un singur obiectiv, bine definit;
2. funcţiile trebuie să fie potrivite nivelului la care apar (exemplu: “Calculează …”
să nu apară cu “Verifică ….”);
3. interfeţele trebuie minimizate;
4. orice funcţie se descompune în cel mult 7 sub-funcţii;
5. nu se foloseşte terminologia specifică implementării (fişier, înregistrare, modul,
staţie de lucru, etc.);
6. atributele de performanţă ale oricărei funcţii (capacitate, viteză, acurateţe) ar
trebui specificate, dacă e posibil;
7. trebuie identificate funcţiile critice;
8. numele funcţiilor trebuie să reflecte scopul lor;
9. numele funcţiilor trebuie să aibă o structură declarativă (“Calculează suma”,
“Caută identificator ”, etc.).

Se consideră că descompunerea funcţională a atins un nivel suficient de detalii dacă:


• modelul furnizează toate capabilităţile cerute
• respectă cele nouă recomandări

2
Analiza performanţei

Cerinţele utilizatorului pot conţine atribute de performanţă (capacitate, viteză,


acurateţe). Acestea definesc performanţele pentru o funcţie sau un grup de funcţii.
Modelul logic trebuie verificat din punct de vedere al conflictelor performanţelor
prin studierea tuturor căilor de-a lungul fluxurilor de date.

Analiza funcţiilor critice

Uneori, cerinţele de capabilitate sunt definite în termeni privind categoria


severităţii consecinţelor hazardului (CSCH). Aceste categorii pot varia de la “catastrofic”
la ”critic” şi de la ”marginal” la ”neglijabil”.
Dacă asemenea termeni sunt specificaţi, modelul logic trebuie analizat pentru a
propaga CSCH către toate cerinţele corelate cu cerinţa iniţială, având ataşată această
categorie (pentru aceasta există tehnici speciale).

Prototipul

Uneori, este util de a verifica anumite părţi ale modelului. Tehnica executării unui
prototip ar putea clarifica cerinţele.
Echipa de dezvoltare poate identifica cerinţe cu un grad oarecare de risc (adică
nu e sigur că aceste cerinţe ar putea fi satisfăcute). Prototipul poate decide dacă asemenea
cerinţe trebuie sau nu incluse în DCS.

1.2 Specificarea cerinţelor software

Standardele SE definesc următoarele tipuri de cerinţe software:


• cerinţele funcţionale (provenite din cerinţele de capabilitate)
• cerinţele nefuncţionale sau de performanţă (provenite din constrângerile
asupra cerinţelor de capabilitate)

Cerinţele funcţionale

Acestea specifică funcţiile pe care sistemul trebuie să le execute.


Există o corespondenţă de la 1 la 1 între cerinţele funcţionale şi nodurile
sistemului logic.
Cerinţele funcţionale trebuie organizate în aceeaşi manieră top-down, funcţie de
structura modelului logic.
O cerinţă funcţională:
• defineşte ce execută sistemul
• defineşte transformarea ce trebuie executată pe intrările specificate pentru a
genera ieşirile
• are ataşate cerinţe de performanţă (capacitate, viteză, acurateţe)

3
• trebuie exprimate prin fraze concise, simple (de exemplu, utilizând engleza
structurată)

Cerinţele nefuncţionale (de performanţă)

Acestea provin din cerinţele restrictive. Ele ar trebui ataşate cerinţelor


funcţionale, putând apărea deci pe orice nivel, aplicându-se tuturor cerinţelor funcţionale
de pe nivelul următor.
De obicei, sunt exprimate în limbaj natural, deoarece notaţiile formale nu sunt
destul de expresive.

2. Metode pentru definirea cerinţelor software

Introducere

Faza definirii cerinţelor software poate fi numită şi faza analizei, conform


standardelor SE.
Analiza se realizează folosind o metodă sau o combinaţie de metode pentru:
• construirea modelului logic
• specificarea cerinţelor software
Se rezumă în continuare câteva bine-cunoscute metode, fără a recomanda o
metodă standard şi fără a defini un set complet de metode.
Funcţie de fiecare proiect, se alege metoda cea mai potrivită. E recomandabil a se
studia sisteme concrete, care au fost dezvoltate cu diverse metode, pentru a alege metoda
cea mai potrivită.
Metodele posibile sunt:
• descompunerea funcţională
• analiza structurată
• analiza orientată-obiect
• metode formale
• dezvoltarea sistemelor Jackson
• prototipul

2.1 Descompunerea funcţională

Aceasta este metoda tradiţională de analiză şi a fost încorporată în metoda


analizei structurate.

2.2 Analiza structurată

Este numele unei clase de metode de analiză a sistemelor prin construirea unor
modele ale fluxurilor de date.
Fac parte din această clasă următoarele metode:
• metodele Yourdon (DeMarco şi Ward-Mellor)

4
• SSADM (Structured System Analysis and Design Methodology)
• SADT (Structured Analysis and Design Techniques)
Analiza structurată include toate conceptele descompunerii funcţionale dar
produce specificaţii funcţionale de o mai mare calitate, prin definirea riguroasă a
interfeţelor între procese şi anume, fluxurile de date şi control.
Diagramele fluxurilor de date (DFD) sunt caracteristice metodelor de analiză
structurată.
Conform definiţiei propusă de DeMarco, analiza structurată înseamnă folosirea
următoarelor tehnici pentru a produce specificaţiile sistemului:
• DFD
• Dicţionarul de Date
• engleza structurată
• tabele de decizie
• arbori de decizie
Ultimele 3 sunt utilizate pentru a specifica detalii de procesare algoritmică.

Dezvoltarea analizei structurate pentru sistemele de timp real, a suplimentat


această listă cu:
• Schema Transformărilor
• Lista evenimentelor
• Diagrame stare-tranziţie
• Schema de Date
• Specificaţii pre- şi post- condiţie

SSADM, care pune accent pe modelarea datelor, mai include:


• Diagramele Entitate-Relaţie (E-R “entity-relationship”) sau Modelele Entităţii
• Istoriile vieţii entităţii (Entity Life Histories)
Analiza structurată produce specificaţii structurate, conţinând o descriere
sistematică şi riguroasă a sistemului. Analiza şi proiectarea sistemului reprezintă procesul
realizării unui model al sistemului.

Analiza structurată (“Structured Analysis”)- versiunea DeMarco (78)

SA este o metodă generală de analiză a sistemelor. Există 2 versiuni similare:


- Gane, Sarson (79)
- DeMarco (78)

Ne vom referi în continuare la versiunea DeMarco.


SA DeMarco utilizează Diagramele Fluxurilor de Date (DFD). DFD evidenţiază
transformarea datelor de intrare în date de ieşire printr-o secvenţă de transformări
funcţionale. Ele reprezintă un mod intuitiv de descriere a sistemelor şi pot fi înţelese fără
o antrenare specială.
În mod normal, aceste diagrame nu trebuie să includă informaţii de control, dar
trebuie să documenteze transformările datelor.

5
DFD sunt grafice adnotate, folosind următoarele simboluri:

flux de date

proces

fişier

surse sau destinaţii

and, or conectori logici

• Primul simbol reprezintă fluxul de date, unic etichetate textual.


• Simbolul 2 reprezintă procesul care transformă datele de intrare în date de
ieşire.
• Simbolul 3, o linie dreaptă, reprezintă un fişier sau o bază de date.
• Simbolul 4 reprezintă sursa sau destinaţia datelor. Ele sunt exterioare
sistemului care trebuie analizat.

Ultimele simboluri au înţelesul cunoscut al operatorilor expresiilor booleene. Sunt


folosite când mai multe fluxuri de date intră sau ies dintr-un proces.
Toate aceste simboluri pot fi combinate pentru a forma o DFD.

Exemplul 1:
B
S1 S2
A C D E
P1 P2 P3

DB

Figura 1: Diagramă a fluxurilor de date

Sursa S1 creează data A care urmează să fie transformată de procesul P1 în două


pachete de date B şi C. Procesul P2, folosind informaţii din baza de date DB , transformă
datele B şi C în D, procesul P3 transformă data D în data E, care ajunge la destinaţia S2.
Fiecare proces poate fi expandat într-o manieră ierarhică, pentru a prezenta un
nivel de detaliu mai mare. Un proces poate reprezenta un număr de alte procese şi fluxuri
de date, deci o DFD, ca în figura 2. Acest lucru e numit organizare pe nivele (leveling),
un proces fiind considerat părintele altor procese descendente.
Conceptul de echilibrare (balancing) se referă la faptul că intrările şi ieşirile
procesului părinte sunt, de asemenea, reprezentate în diagrama descendentă. Şi acest
lucru este pus în evidenţă în figura 2.

6
Toate datele care apar în DFD sunt documentate într-un dicţionar de date.

D
C P3
A B
P1 P2 F
E P4

C
B
P21 P22 P25
E

P23 P24

Figura 2: Organizarea pe nivele. Echilibrarea intrărilor şi ieşirilor

Celelalte elemente ale analizei structurate: Engleza structurată, Tabele de decizie


şi Arborii de decizie sunt utilizate pentru a specifica detalii ale procesării.
DFD reprezintă o parte integrală a unui număr de metode iar mediile (utilitarele)
CASE, de obicei, suportă crearea DFD.
Notaţiile folosite în diverse metode sunt asemănătoare.

Metoda E-R

Cele mai multe sisteme software complexe folosesc mari baze de date. O parte din
activitatea de modelare a sistemelor este dedicată definirii logice a datelor manipulate de
sistem.
Metode obişnuite pentru modelarea logică a datelor sunt:
• modelul entitate-relaţie (“entity-relationship”) sau E-R , Chen-1976
• SDM – Structured Data Modeling (Hammer, McLeod-81)
• modelul relaţional (“Relational Model”)
În principiu, modelul E-R stabileşte entităţi de date (care în termenii limbajelor de
programare ar corespunde tipurilor de structuri de date) şi relaţii între aceste entităţi
(corespunzătoare operaţiilor asupra tipurilor de entităţi).
Entităţile pot avea atribute (analog câmpurilor din structuri), la fel şi relaţiile
(“private data values”). În general, atributele sunt atomice (nu se descompun mai
departe).
Tipurile pot avea subtipuri astfel încât un tip special de relaţii numit relaţie de
moştenire (inheritance relation) a fost introdus pentru extinderea modelului E-R.
Subtipurile moştenesc atributele supertipurilor. Atribute adiţionale proprii pot fi adăugate
entităţii subtip.
În general, modelul E-R este suportat de mediile CASE.

7
Modelul utilizat aici reprezintă extensia modelului E-R, a lui Chen. Notaţiile
grafice utilizate sunt prezentate în figura 3.
entitate
<nume>

<nume atribut pentru entitate sau relaţii

<cardinalitate de intrare>

relaţie

<cardinalitate de ieşire>

relaţie de moştenire

Figura 3: Notaţiile folosite în modelul E-R extins

Există variante pentru aceste notaţii.


Cardinalitatea de intrare reprezintă numărul entităţilor de intrare.
Cardinalitatea de ieşire reprezintă numărul entităţilor de ieşire.
O relaţie de moştenire arată că o entitate moşteneşte atributele entităţii părinte. Subtipul este indicat printr-
o săgeată.
Relaţiile între entităţi pot fi:
• 1:1 ceea ce înseamnă că o instanţă a unei entităţi participă în relaţie cu o instanţă a altei
entităţi.
• 1:M - o instanţă a unei entităţi participă în relaţie cu mai multe instanţe ale altei entităţi.
• M:N - mai multe instanţe ale unei entităţi participă în relaţie cu mai multe instanţe ale altei
entităţi.
În acest model, relaţia M:1 nu este suportată.

Exemplul 1:
Ca o simplă ilustrare a modelului E-R, considerăm un simbol utilizat de sistemele
de editare a diagramelor:

Simbolurile reprezentând tipuri particulare sunt compuse din simbolurile


primitive ca: linii, elipse, dreptunghiuri.

8
Modelul datelor pentru simbolul compus din figura anterioară, este prezentat în
figura 4. Acest model arată că simbolul compus considerat are un atribut care indică dacă
simbolul are dimensiuni variabile sau fixe şi este realizat dintr-un număr de primitive,
toate având atributul de “culoare”. Primitivele pot fi dreptunghiuri, linii sau elipse,
fiecare din aceste subtipuri având propriile lor atribute.

Simbol compus tip dimensiune

are

primitive culoare

dreptunghi linie elipsă

lungime lăţime lungime unghi diam x diam y

Figura 4: Modelul simbolului compus


Modelele E-R au fost utilizate foarte mult în proiectarea bazelor de date. Datorită
existenţei tipurilor, subtipurilor şi a supertipurilor, este uşor de a mapa aceste modele în
baze de date orientate obiect. Deşi bazele de date orientate obiect nu au atins un stadiu
matur, se pare că vor fi folosite pe scară largă în viitor în aplicaţii ca medii CASE.

Dicţionarul de date

Pe măsură ce un model al sistemului este dezvoltat, apar foarte multe entităţi a


căror identificare prin nume trebuie să fie unică. Menţinerea unicităţii este dificilă mai
ales dacă mai multe persoane sunt implicate în procesul de dezvoltare . Este absolut
necesară o metodă automatizată şi, în acest scop, se foloseşte un utilitar pentru
coordonarea numelor: dicţionarul de date. Dicţionarul de date (DD) documentează
riguros diagramele sistemelor. Practic, dicţionarul de date este o listă de nume ale
entităţilor, însoţite de descrieri ale acestora.
Dicţionarele de date sunt utile în toate fazele procesului de dezvoltare, începând
de la modelarea iniţială şi până la întreţinerea sistemului.

9
Toate numele entităţilor, tipurilor, relaţiilor, atributelor, etc., trebuie introduse în
DD. Există posibilitatea, prin suportul software existent, de a crea , a menţine şi a
interoga DD.

Exemplul 1: Un model logic posibil al datelor pentru un DD este arătat în figura5.

DD tip dimensiune

defineşte

def nume
N
desc intrare

tip
data

folosită foloseşte include


de

intrare intrare intrare

Figura 5: Model al dicţionarului de date

Acesta arată că DD este compus dintr-un număr de intrări, fiecare intrare având ca
atribute: nume, reprezentare, descriere, data creării şi o stare (care permite unei intrări să
devină inactivă).
Fiecare intrare participă în 3 relaţii cu alte intrări. Acestea arată:
• ce entităţi vor folosi această entitate
• ce entităţi sunt folosite de această entitate
• ce entităţi sunt incluse în această entitate (de exemplu, entităţile operaţii sunt
incluse în entităţile tipurilor de date abstracte)

Exemple de intrări în DD sunt prezentate în figura 6. Informaţiile privind relaţiile


dintre entităţi nu au fost cuprinse aici. Într-un DD automat, aceste informaţii sunt
accesibile dintr-un meniu asociat fiecărei intrări.

10
Nume_entitate Tip Descriere Definiţie Data
design name data numele diagramei ce trebuie procesată …. …
Get design name proces diagrama se găseşte în baza de date a … …
sistemului ca o entitate cu un nume.
Acest proces comunică cu utilizatorul
pentru a prelua acest nume.
Get entity name proces Folosind numele diagramei, acest … …
proces găseşte în baza de date a
diagramei şi extrage numele
entităţilor.
Figura 6: O parte a înregistrărilor din DD pentru un generator de rapoarte a diagramelor

Modelul Ward-Mellor

Acesta descrie un proces iterativ de modelare a sistemului care defineşte întâi


primele nivele ale modelului esenţial, care este un model logic al sistemului. În loc de a
construi imediat nivelele următoare ale modelului, Ward şi Mellor recomandă construirea
primelor nivele ale modelului de implementare. Ciclul continuă cu definirea următorului
nivel al modelului esenţial. Acest mod evită îndepărtarea prea mare a modelului de
realitate şi previne o implementare lipsită de coerenţă şi structură. Totuşi, nu se
intenţionează în acest fel construirea modelului definitiv de implementare. Acesta ar
trebui să aibă loc în faza proiectării sistemului.
Această metodă este considerată în faza definirii specificaţiilor software a
sistemelor de timp real.

Modelul SADT (D.Ross-1977)

Acest model consideră două etape în faza definirii cerinţelor software:


- analiza contextului sistemului, a interacţiunii sale cu exteriorul
- specificarea funcţională care presupune construirea unei arhitecturi
funcţionale, exprimată prin diagrame SADT.
Arhitectura funcţională reprezintă un model logic al sistemului, derivat dintr-o
descompunere funcţională, top-down, a sistemului, începând cu diagrama de context,
care prezintă toate interfeţele sistemului, după acelaşi principiu ca şi SA.
Ca şi în SA, fluxurile de date ale sistemului sunt transformate de o funcţie sau de
o activitate. Diagramele obţinute în acest fel se numesc diagrame de activităţi.
Diagramele de activităţi SADT sunt formate din dreptunghiuri, săgeţi şi diagrame,
ca în figura 7:
Date de control

Date de ieşire
Date de intrare
Funcţie sau activitate

Mecanism

Figura 7: Diagrama SADT

11
Un dreptunghi e o funcţie sau activitate care transformă datele. Datele de intrare
pătrund prin partea stângă şi ies, transformate, prin partea dreaptă.
Pe latura superioară a dreptunghiului sunt evidenţiate constrângerile sau datele de
control pentru activitatea respectivă. Acestea constrâng modul de transformare a intrării
în ieşire sau controlează modul în care o activitate poate fi folosită pentru a produce o
ieşire.
În partea inferioară a dreptunghiului, se prezintă mecanismul folosit de funcţia
sau activitatea respectivă, dar mecanismele nu sunt esenţiale modelului SADT.
Dreptunghiul este etichetat cu un verb sau o expresie declarativă care să exprime
clar o acţiune de transformare a datelor.
Fiecare săgeată este etichetată cu un substantiv.
O diagramă constă dintr-un set de astfel de simboluri din figura anterioară şi
fiecare element al diagramei poate fi descompus mai departe, prin acelaşi tip de relaţie
părinte-descendent, întâlnit la diagramele SA. Pentru a menţine consistenţa şi
completitudinea nivelelor, intrările, ieşirile şi datele de control ale unei diagrame părinte
trebuie să apară şi în diagrama descendent. Fiecare descompunere trece astfel prin nivele
din ce în ce mai detaliate.

2.3 Metode formale

Scopul acestui capitol este de a introduce tehnicile formale de specificaţii pentru a


îndepărta unele păreri preconcepute privind acest subiect şi pentru a-i convinge pe
ingineri că aceste tehnici pot fi folosite în procesul de dezvoltare a sistemelor software.
Metodele formale trebuie folosite când e necesar a demonstra riguros că sistemul
îndeplineşte anumite acţiuni. Aceste metode se recomandă pentru sistemele critice din
punct de vedere al siguranţei, sau pentru cele cu severe constrângeri de securitate.
Metodele formale au fost criticate pentru că determină specificaţii neinteligibile
pentru utilizator. Conform standardelor ingineriei programării, DCU furnizează
specificaţiile din punctul de vedere al utilizatorului. DCS este destinat în primul rând
echipei de dezvoltare dar i se pot adăuga note explicatorii care să-l facă înţeles şi de alţi
cititori.
Metodele şi specificaţiile formale n-au fost folosite pe scară largă în dezvoltarea
sistemelor. Există, în general, tendinţa de a respinge folosirea acestora deoarece efortul
implicat în însuşirea, asimilarea lor, nu pare a fi justificat de avantajele pe care le oferă.
Totuşi, în momentul de faţă, metodele formale au atins un nivel suficient de matur, nivel
la care ele pot fi utilizate în sisteme reale. Experienţele de până acum au arătat că
utilizarea acestor metode:
- măresc calitatea sistemului
- reduc costurile totale de dezvoltare
Avantaje
• Pe măsură ce o specificaţie e rafinată, sunt detectate erorile şi omisiunile. Detectarea
erorilor este argumentul cel mai puternic pentru dezvoltarea unor specificaţii formale.
• Specificaţiile formale sunt entităţi matematice care pot fi analizate din punct de
vedere matematic. E posibil de a se demonstra consistenţa şi completitudinea
specificaţiilor sau de a demonstra că o implementare se conformează specificaţiilor
sale. Astfel, poate fi demonstrată absenţa unor clase de erori.

12
• Specificaţiile formale pot fi folosite ca un ghid pentru identificarea seturilor de test
potrivite la verificarea sistemului. În acest sens, au fost deja iniţiate acţiuni de
cercetare privind tehnicile specificaţiilor formale în scopul verificării formale a
programelor. Totuşi, e recomandabil a se folosi tehnicile specificaţiilor formale chiar
dacă încă nu există posibilitatea verificărilor formale a programelor.
Tehnicile specificaţiilor formale nu sunt folosite pe scară industrială deoarece:
• Managerii sunt conservatori şi nu adoptă noi tehnici dacă avantajele nu sunt evidente.
E greu de a demonstra că un cost relativ mare pentru dezvoltarea unei specificaţii
formale va reduce costurile totale pentru dezvoltarea sistemelor.
• Cei mai mulţi ingineri nu sunt antrenaţi în a folosi aceste tehnici. Dezvoltarea
specificaţiilor formale cere o familiarizare cu noţiuni de matematică şi logică
matematică, ceea ce face ca aceste tehnici să pară dificile.
• Există o largă ignoranţă privind cunoaşterea tehnicilor curente de specificare şi
aplicabilitatea lor. Există deja aproximativ câteva sute de sisteme relativ mari,
specificate formal.
• Cele mai multe eforturi s-au concentrat spre dezvoltarea de notaţii şi tehnici. Puţin
efort s-a îndreptat spre producerea suporturilor software pentru aceste tehnici, deşi
acesta este esenţial.
O specificaţie formală este o specificaţie exprimată într-un vocabular, sintaxă şi
semantică formal definite.
După cum am mai precizat, un model al procesului îl reprezintă cel al transformărilor
formale. Specificaţiile formale reprezintă o premisă obligatorie pentru acest proces care
implică transformarea acelei specificaţii într-un program, printr-o serie de transformări ce
păstrează corectitudinea. (figura 8).

T1 T2 T3 T4

Specificare formală R1 R2 R3 Program executabil

P1 P2 P3 P4

Figura 8: Dezvoltarea transformaţională a sistemelor

Ti = transformări formale
Pi = demonstrarea corectitudinii transformărilor

Fiecare transformare este suficient de aproape de cea anterioară astfel încât


efortul de a o verifica nu este excesiv. Avantajul abordării transformaţionale comparat cu

13
a demonstra că programul îşi îndeplineşte specificaţiile este că distanţa dintre
transformări este mai mică decât distanţa dintre specificaţii şi program. Demonstraţiile
pentru program sunt foarte lungi şi nepractice pentru sistemele pe scară largă, dar
abordarea transformaţională realizată din paşi mici poate fi mai bună. Totuşi, procesul nu
e simplu. Alegerea transformării şi demonstrarea corespondenţei dintre transformări
presupun anumite abilităţi.
Se pare însă că dezvoltări recente ale metodelor formale (limbajul de specificare
Z) vor face acest proces mai practic.

Cele mai comune metode formale sunt:

Metoda Rezumat
Z Metodă de specificaţie pentru programe secvenţiale
VDM (Vienna Development Metodă de specificaţie pentru programe secvenţiale
Method)
LOTOS Metodă formală, singura care are un standard internaţional.
(Language of Temporal Ordering Este o combinaţie între CSP, şi tipurile de date abstracte din
Specification )
limbajul de specificaţii algebrice, ACT ONE. Este dedicată
sistemelor distribuite. Sunt valabile utilitare.
CSP Limbaj de proiectare pentru paralelism asincron şi
(Communicating Sequential Processes) comunicaţii sincrone.
OBJ (OBJects) Limbaj de specificare funcţională şi utilitar pentru prototip.
Utilizat pentru programe secvenţiale. Obiectele sunt
principalele componente ale specificaţiilor
CCS Specificare şi proiectare a sistemelor concurente. Calcul
(Calculus for Communicating Systems) pentru paralelism asincron şi comunicaţii sincrone.
Reţele Petri Modelarea comportării sistemelor concurente.
Specificaţii algebrice Metodă de specificaţie pentru programe secvenţiale

2.4 Dezvoltarea sistemelor Jackson - metodă dedicată mai ales dezvoltării sistemelor de
timp real.

2.5 Prototipizarea rapidă

Un prototip este un model executabil al unor aspecte selectate ale sistemului.


Dacă cerinţele sunt neclare sau incomplete, e util a se dezvolta un prototip pentru a
determina cerinţele reale. Prototipul poate ajuta la definirea cerinţelor pentru interfaţa
utilizatorului.
Prototipizarea rapidă este procesul de a construi şi a evalua foarte rapid o serie de
prototipuri, în următoarea manieră:

repeat until utilizatorul semnează DCS


begin
analizează cerinţele
creează baza de date

14
creează interfaţa utilizator
adaugă noi funcţii
revede execuţia prototipului împreună cu utilizatorul
end

Cerinţele trebuie analizate utilizând o metodă ca SA, de exemplu.


Prototipizarea rapidă necesită un suport (utilitare) software, altfel nu e suficient
de rapidă pentru a fi efectiv utilă.
Rezultatele prototipizării rapide se folosesc la formularea cerinţelor software în
DCS.
Pentru a permite o implementare rapidă a unui prototip, constrângerile şi regulile
impuse de standardele de proiectare şi implementare pot fi relaxate. Deoarece produsul
software final trebuie să respecte anumite cerinţe de calitate, softul scris pentru
implementarea prototipului nu trebuie folosit în etapele ulterioare. Este utilă, bineînţeles,
utilizarea ideilor care a determinat construirea prototipului.

15
2. Metode pentru definirea cerinţelor software (continuare)

2.6 Analiza orientată-obiect (AOO)

Introducere

Analiza orientată obiect este numele unei clase de metode de analiză a unei probleme prin
studiul obiectelor domeniului problemei respective.
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.
Analiza orientată obiect (AAO) diferă de analiza structurată (AS) prin:
• construirea unui model al obiectelor în locul modelului funcţional
• integrarea obiectelor, atributelor şi operaţiilor în loc de separarea lor prin modelul
datelor şi cel funcţional.
AAO a fost utilizată cu succes în problemele greu abordate cu AS, ca de exemplu realizarea
interfeţelor utilizator. AAO determină o tranziţie către proiectarea orientată obiect (POO) şi limbaje
de programare ca ADA, C++ etc.
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 vor fi mai adaptabile decât cele bazate pe funcţii.
Cele mai importante şi recunoscute metode AOO sunt:
• metoda Coad-Yourdon
• Rumbaugh-Object Modelling Technique (OMT)
• Shlaer-Mellor
• Booch
În momentul de faţă, AAO evoluează iar analiştii, de obicei, combină tehnicile diferitelor
metode în faza de analiză a problemei.
AAO nu se utilizează pentru sisteme care au foarte puţine funcţiuni sau pentru sisteme cu 1-
2 clase şi obiecte.

Principiile utilizate de OOA sunt principiile utilizate pentru coordonarea complexităţii:


1. Abstractizarea (procedurală şi de date)
2. Încapsularea
3. Moştenirea
4. Asocierea
5. Comunicarea prin mesaje
6. Utilizarea metodelor de organizare

1
• obiecte şi atribute
• obiecte şi părţile sale
• clase şi membri

1. 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).
• 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 (vezi diagramele DFD unde primul nivel reprezintă
sistemul urmând celelalte nivele 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.

2. Încapsularea. Conform acestui principiu, fiecare componentă a unui program ar trebui să


încapsuleze sau să ascundă o singură decizie de proiectare. Interfaţa fiecărui modul e definită astfel
încât să releve cât mai puţin din funcţionarea sa internă. Se minimizează astfel efortul modificărilor
ulterioare.

3. Moştenirea - 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 de a specifica Atributele şi Serviciile comune doar
o dată , dar şi de a specializa şi a extinde anumite Atribute şi Servicii.

4. Asocierea - reprezintă reuniunea sau conexiunea ideilor (presupune alăturarea, legarea acelor
lucruri care au loc la un moment dat în circumstanţe similare).

5. Comunicarea prin mesaje - se referă la interacţiunea prin mesaje între obiecte, interacţiune care
corespunde modului imperativ al limbajelor (comandă, cerere).

6. Înglobarea metodelor de organizare. Strategia analizei poate fi construită pe baza a trei metode
de organizare:
• diferenţierea între obiecte şi atributele lor (ex: a distinge între un copac şi înălţimea sa)
• diferenţierea dintre obiectul ca întreg şi părţile sale componente (ex: a distinge între un
copac şi ramurile sale)
• diferenţierea diverselor clase de obiecte (ex: clasa pietrelor şi clasa arborilor)

2
Metoda de analiză orientată obiect Coad - Yourdon

Coad şi Yourdon descriu o metodă OOA bazată pe 5 activităţi majore:


• identificarea claselor şi obiectelor
• identificarea structurilor
• identificarea subiecţilor
• identificarea atributelor
• identificarea serviciilor

Aceste activităţi (care nu reprezintă neapărat paşi secvenţiali) sunt folosite pentru a construi
fiecare din cele cinci nivele ale modelului obiect.
Obiectele există în domeniul problemei. Clasele sunt abstractizări ale obiectelor. Obiectele
sunt instanţe ale claselor.
Primul obiectiv al metodei e de a identifica clasele şi obiectele.
Al doilea obiectiv e de a identifica structurile. Există 2 tipuri de structuri:
• structuri generalizare-specializare (generalization-specialization); moştenirea este
posibilă intre membrii structurii.
• structuri parte-întreg (“whole-parts”); modelează relaţiile între entităţi.
Modelele mari, complexe, sunt organizate în subiecţi, fiecare subiect reprezentând un
anumit punct de vedere al problemei. De exemplu, modelul obiect al unui motor ar putea prezenta
un punct de vedere al mecanicului şi unul al electricianului.
Atributele caracterizează fiecare clasă. De exemplu, un atribut al motorului unui vehicul este
numărul de cilindri. Fiecare obiect va avea o valoare a atributului respectiv.
Serviciile definesc funcţiunile sistemului.
Aceste activităţi conduc analistul de la nivele mari de abstractizare (clasele şi obiectele) spre
nivele din ce în ce mai detaliate ale abstractizării (Structurile, Atributele şi Serviciile):
• Unii analişti preferă ordinea: Clase-Obiecte, Atribute, Structuri, Servicii.
• Alţii preferă ordinea: Clase-Obiecte, Servicii, Structuri, Atribute.
Pentru proiectele mai complexe analiştii pot utiliza a 5-a activitate - cea a subiecţilor.
Puterea metodei constă în descrierea scurtă, concisă şi folosirea unor texte generale ca surse
de definiţii. Principalul dezavantaj al metodei constă în notaţia complicată, dificil de folosit fără
utilitare.

Modelul AOO este prezentat în 5 straturi:


• stratul subiecţilor
• stratul claselor şi obiectelor
• stratul structurilor
• stratul atributelor
• stratul serviciilor

3
Activitatea I a metodei Coad-Yourdon: Identificarea claselor-obiectelor

O clasă poate fi privită ca o colecţie de obiecte care pot fi descrise prin aceleaşi Atribute şi
Servicii. Obiectul (sinonim cu instanţa) reprezintă încapsularea Atributelor şi Serviciilor. Prin
clasă&obiect vom înţelege un termen desemnând o clasă şi obiectele din acea clasă.
Motivele 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 5 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ă trecere este dificilă de la DFD ale analizei structurate la hărţile de
structură specifice proiectării, sau de la DFD la o reprezentare obiect în faza de
proiectare). Această problemă se rezolvă prin utilizarea unei reprezentări orientată-
obiect atât în faza de analiză cât şi în fazele de proiectare şi implementare.

Notaţii

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


reprezentată astfel: astfel:

Nume clasă
Nume clasă&obiect
Atribute (proprietăţi)
Atribute (proprietăţi)
Servicii (metode)
Servicii (metode)

Numele clasei:
• este un singur substantiv sau substantiv şi adjectiv
• se alege ţinând cont de vocabularul standard al domeniului problemei. Folosirea unei
terminologii nefamiliare clientului îl frustrează şi-l face pe acesta să nu se simtă
confortabil.
• 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 uşoară citirea lor.

Numărul de clase în modelul OOA depinde de complexitatea domeniului problemei: 35 este


media; 110 clase deja sunt prea multe; pentru domenii ale problemei care conţin câteva sub-domenii
(ex: controlul traficului aerian), ar putea exista 4-5 sub-domenii cu câte 50-100 clase pentru fiecare.

4
Unde cautăm ?

Se recomandă investigarea domeniului problemei prin:


• observare (ex: controlul traficului aerian - staţi un schimb întreg în locul unde se
desfăşoară activităţile ce au legătură cu sistemul, puneţi-vă în locul clientului, încercaţi
să intuiţi problemele sale şi modul în care sunt sau ar trebui soluţionate)
• ascultare atentă - luaţi contact cu experţii în domeniu, ascultaţi cu atenţie, reveniţi
pentru întrebări şi clarificări ale problemelor, puneţi-le întrebări despre domeniul
problemei, de ce sunt mai importante unele aspecte decât altele, ce scenarii sunt
semnificative şi de ce. Analiştii trebuie să aibă acces la informaţii din domeniu
problemei. Ei trebuie să recurgă neapărat la ajutorul unui expert în domeniu. Ideal,
echipa iniţială de analişti este formată din 3 super-analişti şi 3 experţi în domeniul
problemei.
• verificarea rezultatelor anterioare ale OOA, pentru domenii similare ale problemei în
ideea refolosirii unor clase şi obiecte deja identificate.
• documentaţie permanentă . Cereţi clientului un rezumat concis al domeniului
problemei de 25, 50 sau 100 pagini. Consultaţi o documentaţie profesionistă, un excelent
mod de a asimila terminologia şi conceptele fundamentale. Colectaţi orice diagramă,
grafic, schemă bloc, etc. Construiţi diagrame, punând în evidenţă componentele ce vor
interacţiona. În felul acesta veţi căpăta rapid o perspectivă asupra domeniului problemei.
• prototipul este esenţial pentru o analiză efectivă mai ales în cazul sistemelor care
presupun o interacţiune om - computer. Clientul va trebui mereu temperat atrăgându-i
mereu atenţia că nu prototipul este produsul final.

Ce căutăm ?

Pentru identificarea claselor şi obiectelor căutăm:


• structuri - acestea sunt atât de importante încât există o activitate separată pentru
identificarea lor (Structurile Generalizare-Specializare şi structurile Whole-Parts,
prezentate în detaliu în secţiunile următoare)
• alte sisteme - evidenţierea sistemelor (“terminatorilor externi”) care interacţionează cu
sistemul în cauză.
• dispozitivele cu care sistemul va interacţiona
• evenimente/lucruri ce trebuie memorate - identificarea evenimentelor care trebuie
înregistrate de sistem
• rolul factorului uman privind sistemul. Clasele şi obiectele reprezentând factorul uman
se referă la:
- utilizatorii sistemului (funcţionarul care va interacţiona cu sistemul)
- persoanele care nu interacţionează direct cu sistemul dar informaţii despre
aceste persoane sunt păstrate de sistem
Investigarea ulterioară a Serviciilor şi Atributelor va diferenţia în detaliu cele 2 categorii.
• proceduri operaţionale pe care sistemul trebuie să le execute pentru a conduce
interacţiunile umane

5
• amplasamentul fizic - dacă e necesar ca sistemul să aibă informaţii despre acest lucru
(pot fi sisteme care trebuie configurate la o anumită altitudine, longitudine, latitudine şi
tip de relief. Ar putea deci exista o Clasă&Obiect de tip Amplasament)
• unităţi organizaţionale cărora aparţin factorii umani (de exemplu, în cazul unui
funcţionar, acesta poate lucra într-un district, şi se poate deci lua în consideraţie o
Clasă&Obiect DiviziuneRegională, dacă sistemul necesită informaţii despre acel district)
Odată identificaţi candidaţii pentru Clase&Obiect, aceştia trebuie examinaţi pentru a decide
dacă vor fi în final incluşi în modelul OO. Criterii pentru această decizie pot fi:
• Sunt toate aspectele identificate relevante pentru sistem? Poate fi orice obiect al unei
clase descris? Care sunt atributele lui potenţiale? (De exemplu, atributele potenţiale
pentru un funcţionar sunt: nume, parolă, autorizaţie. Altele cum ar fi: înălţimea, semne
particulare, greutate nu sunt relevante pentru sistem).
• Trebuie 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 ?
De exemplu, setul de Atribute pentru Clasa&Obiect Clădire va conţine: adresa,
preţul, mărimea. Dar NumărulDormitoare sau NumărulBăi se aplică doar anumitor
clădiri. Acest lucru indică faptul că ar fi necesară o structură Generalizare -
Specializare.
• se poate identifica un set de Servicii aplicabile întotdeauna, tuturor obiectelor clasei ?
Dacă nu, atunci e necesară o structură Generalizare - Specializare.
• Toate clasele obiectele 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 fără a ţine 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 (Exemplu: nu are rost de
a se reţine vârsta_persoanei dacă sistemul deja a înregistrat data_naşterii). Luaţi în
consideraţie acele Atribute şi Servicii din care apoi se pot obţine rezultate derivate.
Exemplu - Sistemul senzor

Acest sistem monitorizează senzorii şi senzorii critici. Fiecare senzor este descris de:
• modelul său (producătorul şi numărul)
• secvenţa de iniţializare (trimisă către senzor)
• conversie
• intervalul datelor
• stare (on, off, standby)
• valoare curentă

6
• valoare de prag
În plus, senzorii critici sunt descrişi de toleranţă privind intervalul datelor. Sistemul
semnalează alarma când pragul senzorului este depăşit.

Observaţii

Clasele&obiect potenţiale includ:


- două dispozitive (Senzor şi SenzorCritic)
- un eveniment memorat (EvenimentAlarmă)
- rolul factorului uman (Funcţionar) pentru controlul senzorilor

Presupunem că pentru acest exemplu clientul doreşte omiterea memorării evenimentului şi


al rolului factorului uman. Deci, modelul constă în două obiecte: Senzor şi SenzorCritic. Mai multe
detalii vor fi puse în evidenţă prin următoarele activităţi OOA.

Senzor SenzorCritic

Activitatea a II-a a metodei Coad-Yourdon: Identificarea structurilor


Această secţiune se referă la evidenţierea structurilor Generalization-Specialization (Gen-
Spec) şi Whole-Parts (Întreg - Părţi).
Structura Gen-Spec poate fi văzută ca fiind aspectul de diferenţiere între clase, aspect al
uneia din cele trei metode de organizare, utilizate de gândirea umană.
Exemplu: generalizarea vehicul şi specializarea camion.
Mai puţin formal, din punct de vedere al specializării, Gen-Spec poate fi gândită în sensul de
este un fel de sau este o (Camion “este un” Vehicul). În cadrul structurii Gen-Spec se aplică
principiul moştenirii.
Structura Whole-parts este una din cele trei metode de bază de organizare.
Exemplu: Întregul vehicul şi partea motor.
Mai puţin formal, din perspectiva întregului, structura Whole-Parts poate fi gândită ca o
structură are un sau are o. Exemplu: Vehicul are un motor.

Cum identificăm structurile ?


Pentru fiecare Clasă&Obiect se examinează fiecare clasă pentru a pune în evidenţă
structurile Gen-Spec, şi fiecare obiect pentru a identifica structurile Whole-Parts.

Structurile Gen-Spec
Notaţii

7
Generalizare

Specializare1 Specializare2

Î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.
Exemplu: Pentru generalizarea Senzor sunt preferate specializările SenzorCritic şi
SenzorStandard faţă de numele Critic şi Standard.
Simbolurile Clasă&Obiect apar pentru toate specializările aflate pe nivelele cele mai de jos.
Pe celelalte nivele, apar fie simbolurile Clasă&Obiect fie Clasă.

Senzor

Model
SecvenţaIniţializar
e
Conversie
Interval
Adresă
Prag
Stare
Initializează
Monitorizează

SenzorCritic SenzorStandard
Toleranţă
Monitorizează
Folosirea unei Clase ca generalizare
Aici, un senzor este fie un senzor critic, fie un senzor standard. Senzor este o clasă fără
obiecte corespunzătoare directe, dar cu obiecte corespunzătoare în clasele specializate. Deoarece
nici un nou Atribut şi mici un nou Serviciu nu au fost adăugate senzorului standard, structura
anterioară poate fi rescrisă astfel încât un senzor este fie un senzor critic, fie doar un senzor:

8
Senzor

Model
SecvenţaIniţială
Conversie
Interval
Adresă
Prag
Stare
Valoare
Initializează
Monitorizează

SenzorCritic
Toleranţă
Monitorizează

Folosirea unei Clase&Obiect ca generalizare

Se recomandă ca structurile identificate:


• să facă parte din domeniul problemei
• să facă parte din responsabilităţile sistemului
• să existe efectiv moşteniri între clase
De asemenea, se recomandă să se studieze rezultate anterioare (dacă există) ale analizei OO
pentru probleme similare şi să se reutilizeze structuri Gen-Spec evidenţiate.
Dacă sunt posibile multe specializări este util de a se considera întâi cea mai simplă
specializare şi cea mai complicată, apoi de a considera diverse variante între cele două.

Unul din criteriile construirii unei structuri Gen-Spec este de a reflecta o generalizare-
specializare în domeniul problemei. Aceste structuri nu trebuie construite doar pentru a extrage
Atribute comune. Un exemplu de structură Gen-Spec eronată este următorul:

Localizare

Avion Aeroport

În acest exemplu chiar numele claselor nu reflectă o generalizare şi apoi o specializare.

9
Fiecare Structură Gen-Spec formează fie o ierarhie fie o latice. În practică, cea mai
obişnuită formă este de ierarhie.

Exemplu: Persoană
NumeLegal
Adresă

PersoanăPosesor PersoanăFuncţionar PersoanăPosesorFuncţionar


CetăţeanAl DataNaşterii CetăţeanAl
Identificator DataNaşterii
Parolă Identificator
Fotografie Parolă
Fotografie

Structură Gen-Spec ierarhie

În acest exemplu, o persoană este fie posesor, fie funcţionar, fie şi posesor şi funcţionar, în
acelaşi timp. În ierarhia, se observă anumite redundanţe de-a lungul specializărilor. Această
problemă se rezolvă prin structura Gen-Spec latice după cum se poate observa în exemplul următor:
Persoană
NumeLegal
Adresă

PersoanăPosesor PersoanăFuncţionar
CetăţeanAl DataNaşterii
Identificator
Parolă
Fotografie

PersoanăPosesorFuncţionar

Structură Gen-Spec pentru clasa Persoană, formă latice


Acest exemplu evidenţiază că o persoană poate fi posesor, funcţionar sau şi posesor şi
funcţionar. Specializarea PersoanăPosesorFuncţionar evidenţiază o clasă cu multiple generalizări.

10
Arată că un obiect din această clasă moşteneşte o combinaţie de Atribute şi Servicii de la
predecesorii săi.
Se observă ca forma latice poate mări complexitatea structurii. Dacă acest lucru se întâmplă,
se recomandă reorganizarea unor părţi din latice ca ierarhii.
Folosirea aceloraşi nume de Atribute şi Servicii în mai multe generalizări imediate, poate
crea probleme în timpul specificării Claselor&Obiecte. Trebuie evitate asemenea conflicte de nume.

Exemplu: - Structură Gen-Spec latice, pentru Client şi Persoană

Client Persoană

OrganizaţieClient IndividClientPersoan AngajatPersoană

IndividClientAngajatPersoană

Această formă de Gen-Spec capturează o suprapunere între două structuri Gen-Spec diferite,
Client şi Persoană. Un client este fie o organizaţie (corporaţie, trust, întreprindere), fie o persoană
individuală. O persoană este fie un client individual, fie un angajat, fie ambele. Deci, un obiect
poate fi unul din următoarele: client organizaţie, client individual, angajat, angajat şi client
individual, simultan.

11
Structuri Întreg-Părţi

Notaţii
Întreg

a,b e,f

c,d g,h

Parte1 Parte2

Notaţia structurii Întreg-Parte

În partea superioară este reprezentat obiectul întreg al Clasei&Obiect, iar în partea


inferioară părţi ale acestui obiect. Triunghiul face distincţia dintre întreg şi părţi. Se observă că
liniile unesc între ele obiecte şi nu clase, reflectând o mapare între clase. 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:
Avion Organizaţie

0,4 0,m

0,1 1

Motor Funcţionar

Un Avion este un ansamblu: O organizaţie este o colecţie:


• de 0 motoare (este un planor) • posibil, fără funcţionari
• de cel mult 4 motoare • de cel mult m funcţionari
Un Motor este o parte din: Un funcţionar este membru al unei
• cel mult un avion singure organizaţii.
• nu neapărat dintr-un avion
Dacă mapările către un alt Obiect de-a lungul timpului trebuie memorate, atunci acel Obiect
necesită un Atribut Data, sau Stare, pentru a distinge cele mai recente (respectiv active), Obiecte de
celelalte.
Pentru identificarea potenţialelor structuri întreg-parte, se consideră aspectele:

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

Avion Avion

0,4 0,m

0,1 0,m

Motor Pilot

Structură Întreg-Parte: ansamblu-părţi Structură Întreg-Parte: container-conţinut


Avionul este considerat un container, pilotul găsindu-se
înăuntru. Dacă domeniul problemei şi responsabilităţile
sistemului trebuie să asigneze un pilot unui avion, atunci o
Clasă&Obiect Pilot este necesară.

Organizaţie Misiune

0,m 0,m

1 0,m

Funcţionar SegmentDeZbor

Structură Întreg-Parte: colecţie - membri Structură Întreg-Parte: colecţie - membri


O organizaţie este o colecţie de funcţionari. Această structură este doar un “model mental” (părţile
şi întregii nu sunt tangibili şi nici observabili) dar foarte util
pentru a coordona complexitatea problemei.

13
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 (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ă&obiect Motor. Este suficient de a se
prevedea un Atribut Stare în clasa Avion. Dacă însă, sistemul trebuie să ştie mai mult
despre motor - Model, NumărSerie, DataFabricaţiei etc., atunci este necesară o
Clasă&Obiect Motor).
Considerând apoi fiecare obiect ca o parte potenţială dintr-un întreg, se verifică utilitatea
prezenţei lui în acelaşi mod.

Activitatea a III-a a metodei Coad-Yourdon: Identificarea Atributelor

Atributul = proprietate, calitate sau caracteristică pentru care fiecare Obiect al unei Clase
are o anumită valoare.
Atributele adaugă detalii abstractizărilor Clasa&Obiect şi Structuri. Alegerea atributelor
implică analiză şi decizii (exemplu: pentru Senzor - greutatea, culoarea, textura, deşi importante în
alt context, nu sunt importante pentru exemplul nostru; în schimb, adresa, prioritatea, pragul
acestuia sunt esenţiale.)
Atributele 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.

Se recomandă definirea Atributelor prin aplicarea următoarelor acţiuni:


• Identificarea Atributelor
• Poziţia Atributelor
• Identificarea Conexiunii Instanţelor
• Verificarea cazurilor speciale
• Specificarea Atributelor

Notaţii. Atributele se plasează în centrul secţiunii simbolurilor Clasa&Obiect şi Clasa.

Identificarea Atributelor
Pentru fiecare Clasă&Obiect, urmărim:
• cum sunt descrise obiectele în general
• cum sunt descrise obiectele în domeniul problemei
• cum sunt descrise obiectele în contextul responsabilităţilor sistemului

14
• ce anume trebuie memorat în timp
• care sunt stările în care se poate afla obiectul
• studiul rezultatelor anterioare a AAO în probleme similare pentru reutilizarea
Atributelor
Identificarea atributelor trebuie să aibă în vedere următoarele aspecte;
• 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
adresa: compusă din ţara, oraşul, strada, numărul)
• identificarea Atributelor furnizează un mijloc de a referi un Obiect şi conexiunile sale cu
alte Obiecte. Fiecare Obiect necesită asemenea identificatori. Fiecare Obiect are
- un identificator implicit (id pentru “identificator”)
- identificatori pentru conectare (cid, pentru “identificatori de conectare”)
Aceşti identificatori sunt impliciţi în sensul că nu sunt arătaţi pe stratul Atributelor.

Poziţia Atributelor
Fiecare Atribut va fi pus în Clasa&Obiect pe care o descrie mai bine.
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&Obiect EvenimentÎnregistrare sau al Clasei&Obiect
Vehicul ? Răspunsul este evident: Vehicul.
Pentru clasele care prezintă o structură Gen-Spec, se pune Atributul î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.

Exemplu: Persoană
NumeLegal
Adresă

PersoanăPosesor PersoanăFuncţionar
CetăţeanAl DataNaşterii
Identificator
Parolă
Fotografie

PersoanăPosesorFuncţionar

Poziţionarea Atributelor

15
Identificarea Conexiunilor Instanţelor
Conexiunile instanţelor modelează principiul asocierii, un principiu pentru coordonarea
complexităţii.
O conexiune a instanţelor e un model al mapărilor între domeniile problemelor Obiectelor.
Notaţie
a,b c,d

Notaţia Conexiunea Instanţelor

Conexiunea Instanţelor e reprezentată printr-o linie unind Obiectele (părţile laterale ale
acestora). Fiecare Obiect are o cantitate (a) sau interval (a,b), marcate pe conexiune, reflectând
restricţiile conexiunilor cu alte Obiecte.
Limitele intervalului:
• Limita inferioară = 0, dacă este o conexiune opţională
= 1 sau mai mare, dacă e o conexiune obligatorie
• Limita superioară = 1, dacă este o singură conexiune
>1, dacă sunt conexiuni multiple

Exemplu:

Avion PlanDeZbor
0,m 1

Conexiunea Instanţelor- exemplu

Un anumit plan de zbor trebuie pregătit pentru exact un avion. Fiecare avion poate fi
pregătit pentru 0 sau mai multe planuri de zbor (un plan de zbor are o conexiune cu un anumit
avion; un avion are mai multe conexiuni cu planurile de zbor).

Strategie
• Se revăd rezultatele AOO anterioare pentru probleme similare, pentru refolosirea
conexiunilor evidenţiate.
• Pentru fiecare Obiect, se adaugă conexiuni spre alte Obiecte, reflectând mapări.
Conectarea cu o Structură Gen-Spec se face prin nivelul cel mai din vârf al structurii.
• Pentru fiecare Obiect, se defineşte intervalul conexiunilor, din perspectiva fiecărui
Obiect.

Verificarea cazurilor speciale privind Atributele

• Se verifică pentru fiecare Atribut dacă sunt cazuri când nu sunt aplicabile

16
Exemplu:
Vehicul
...............
Tracţiune

Atributul Tracţiune poate avea valorile: petrol, diesel, electric. Dar pentru anumite vehicule,
care nu au motor, acest atribut poate avea valoarea “Inaplicabil”. Dacă este 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.

• verifică fiecare Clasă&Obiect care are doar un Atribut, caz în care:


- fie Clasa&Obiect are un singur Atribut pentru că aşa impun responsabilităţile sistemului
Exemplu:
Funcţionar
Nume

Plasarea unui singur Atribut într-o Clasă&Obiect, pe care o descrie

- fie Atributul nu este bine poziţionat, el aparţinând unei alte Clase&Obiect.


Exemplu:

Garanţie Adresă Garanţie

Nume Adresă Nume


Cantitate Cantitate
Adresă

Plasarea unui Atribut în Clasa&Obiect pe care o descrie.

Verificând domeniul problemei, se constată că în loc de cele două simboluri din stânga
(Garanţie şi Adresă) se poate folosi doar cel din dreapta.

Specificarea Atributelor
• Numele Atributelor trebuie să fie sugestive, din domeniul problemei
• Specificarea unui Atribut este însoţită de o sumară descriere a sa
• Specificarea unui Atribut este însoţită de eventuale restricţii:
- unitate de măsură, intervale
- precizie
- valoare implicită
- trebuie acest Atribut setat ?
- în ce condiţii sunt permise Serviciile de creare şi acces
- în ce măsură este afectat de valorile altor atribute

17
Exemplu de specificare a Atributelor:

Specificaţie 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

Exemplu pentru Identificarea Atributelor - Sistemul Senzor

Unele Atribute îşi schimbă foarte rar valoarea (Model, SecvenţăIniţială, Conversie), altele
îşi schimbă valoarea mai des (Interval, Adresa, Prag, Stare), iar altele sunt dinamice (Valoare).
Atributul Valoare este rezultatul citirii unei valori în unităţi fizice (ex: volţi ) şi conversia ei
î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 impune existenţa acestui Atribut.

Senzor

Model
SecvenţaIniţializar
e
Conversie
Interval
Adresă
Prag
Stare
Valoare

SenzorCritic
Toleranţă

Sistemul Senzor: Stratul Clase&Obiecte, stratul Structurilor şi stratul Atributelor

18
Activitatea a IV-a a metodei Coad-Yourdon: Identificarea Serviciilor

În analiza orientată obiect se recomandă ca identificarea serviciilor să se realizeze după


identificarea Claselor&Obiectelor, Structurilor şi Atributelor.
Serviciul este definit ca fiind o operaţie specifică a unui Obiect. Pe lângă evidenţierea
acestor Servicii se pune problema şi de a defini comunicaţiile necesare între Obiecte (Message
Connections).

Notaţii Clasă
Clasă&Obiect
Serviciul1
Serviciul1 Serviciul2
Serviciul2

Strategia definirii Serviciilor constă în următoarele activităţi:


• 1 Identificarea stărilor Obiectelor
• 2 Identificarea Serviciilor
• 3 Identificarea Conexiunilor-Mesaje între Obiecte
• 4 Specificarea Serviciilor
• 5 Realizarea documentaţiei finale OOA

1 Identificarea Stărilor Obiectelor

Starea unui Obiect este reprezentată de valorile atributelor sale. Pentru a identifica Starea
Obiectului:
• se examinează valorile potenţiale ale Atributelor
• se determină dacă sistemul are comportări diferite pentru aceste valori
• se examinează rezultatele analizei OO anterioare

Exemplu: care din Atributele sistemului Senzor reflectă o schimbare în starea Obiectului ?
Atributele sistemului sunt: Model, SecvenţaIniţializare, Conversie, Interval, Adresă, Prag,
Stare, Valoare. Pentru acest sistem valorile Atributelor: Model, SecvenţaIniţializare, Conversie,
Interval, Adresă, Prag nu implică o modificare a comportării sistemului. Dar 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:

Stare
Stare
Tranziţie

19
Exemplu: sistemul Senzor - diagrama stărilor obiectului

Stare=off

Stare=standby

Stare=on

În acest exemplu, săgeata din vârf indică starea iniţială. Sunt prezentate doar stările şi
tranziţiile legale.

2 Identificarea Serviciilor
• servicii algoritmic-simple
• servicii algoritmic-complexe

Serviciile algoritmic-simple constau în specificarea unor servicii care se aplică fiecărei


Clase&Obiect. Acestea sunt implicite, ele nu sunt reprezentate pe stratul Serviciilor.
Cele 4 tipuri de servicii algoritmic-simple sunt:
• create - creează şi iniţializează un nou Obiect într-o clasă
• connect - conectează un Obiect cu altul. Acest Serviciu stabileşte sau anulează o
mapare dintre Obiecte.
• acces - setează sau extrage valoarea unui Atribut al unui Obiect
• release (distrugere)- eliberează (distruge) un Obiect
Majoritatea operaţiunilor unui sistem (80%-95%) se concentrează în serviciile algoritmic-
simple.

Serviciile algoritmic-complexe constau în 2 tipuri de Servicii:


• Calculate (Calculează) - calculează un rezultat folosind valorile unor Atribute
• Monitor (Monitorizează) - supraveghează un sistem extern sau dispozitiv. Tratează
intrările şi ieşirile sistemului extern, achiziţionează şi controlează date.

3 Identificarea Conexiunilor - Mesaje între Obiecte

O Conexiune prin Mesaje este o mapare de la un Obiect spre altul (sau spre o clasă, pentru a
crea un nou obiect), în care un transmiţător trimite un mesaj către un receptor, pentru a executa o
anumită procesare. Procesarea este denumită în specificarea Serviciilor transmiţătorului şi este
definită în specificarea Serviciilor receptorului.
Conexiunile prin Mesaje există doar în beneficiul Serviciilor.

20
Notaţie
Transmiţător Receptor

Săgeata uneşte Obiecte.


Receptorul primeşte mesajul, execută operaţiunea corespunzătoare şi returnează un rezultat
transmiţătorului.
Firele de execuţie pot fi denumite, definite şi reprezentate cu diferite pattern-uri de linii
pentru fiecare fir. Cu ajutorul mediilor CASE:
• firele de execuţie pot fi denumite, definite şi reprezentate diferit
• un Serviciu poate fi selectat, pentru a vedea ce mesaje îl invocă şi ce mesaje trimite.
Pentru Conexiuni prin Mesaje de la un Obiect spre mai multe Obiecte, există următoarea
notaţie:

Transmiţător

Interacţiunile dintre factorul uman şi sistem sunt şi ele reprezentate în modelul OOA.

Transmiţător

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 ? Trasează câte o săgeată dinspre fiecare din
aceste Obiecte.
• urmăreşte conexiunea prin mesaj spre Obiectul destinatar şi repetă aceleaşi întrebări.
• examinează rezultatele analizei OO anterioare.

În aplicarea acestei strategii, analistul este interesat în firele execuţiei, formate din mesajele
de la un obiect la altul. Firele execuţiei pot fi analizate mai departe (pentru determinarea
completitudinii modelului) şi se pot impune constrângeri asupra lor, mai ales pentru sistemele de
timp real. Firele de execuţie apar în specificaţiile Claselor&Obiecte, secţiunea Serviciilor sau în
hărţile Serviciilor.

4 Specificarea Serviciilor

Serviciile se specifică în Clasele&Obiect, folosind o harta Serviciului pentru fiecare


Serviciu.
21
specification
attribute
attribute
….
attribute
ExternalInput
ExternalOutput
objectStateDiagram
additionalConstraints
note
service <nume & harta serviciului>
……
service <nume & harta serviciului>

Harta Serviciului prezintă grafic cerinţele Serviciului:

Condiţie (if; precondiţie; trigger, terminare)

Bloc text

Buclă (while; do; repeat; terminare)

Conector (conectat la simbolul următor, cel mai din vârf )

Notaţiile Hărţii Serviciului

Notaţia Hărţii Serviciului furnizează un mijloc de a aplica principiul abstractizării


procedurale sistematic, în contextul limitat al unui singur Serviciu.

Exemplul 1 – Create

Verifică valorile atributului funcţie


de restricţiile acestuia şi ale conexiunii

doar restricţii ale atributului NU


Sunt restricţii?

DA

creează; creează; returnează eroare;


iniţializează; iniţializează;
returnează warning; returnează succes;

Harta Serviciului pentru Serviciul implicit Create

22
Exemplul 2. Specificarea Clasei&Obiect Senzor

Se observă cum harta serviciului exprimă comportarea dependentă de stare folosind


precondiţia, trigger (declanşator) şi terminare.
A se observa utilizarea mecanismului de moştenire în specificarea Atributelor şi Serviciilor
în cazul Senzorului Critic.

Specification: Senzor
attribute Model: numărul modelului
attribute SecvenţaIniţializare: secvenţa de iniţializare
attribute Conversie: constă din factor de scalare, unităţi de măsură
attribute Interval: intervalul de date (de eşantioane) pentru acest Senzor
attribute Adresă: adresa acestui Senzor
attribute Prag: valoarea de prag, indicând alarma
attribute Stare: starea senzorului (monitor, off, standby)
attribute Valoare: cea mai recentă valoare a senzorului şi convertită în unităţi de
măsură

ExternalInput
CitireSenzor: datele citite de la Senzor

ExternalOutput
ControlSenzor: comanda de control trimisă spre Senzor

objectStateDiagram

Stare=off

Stare=standby

Stare=on

service Iniţializează(out: rezultat)


NU
Precondiţie: State<>monitor

DA

Trimite SecvenţaIniţializare către interfaţa


SenzorControl a actualului Senzor, la Adresa sa

Stare=standby

returnează rezultatul transmiţătorului

23
service EşantioneazăSenzor (selectează)(out: rezultat)

citeşte CitireSenzor din actualul Senzor,


la Intervalul corespunzător

returnează CitireSenzor

service MonitorizeazăCondiţiaDeAlarmă

Precondiţie: Stare=standby

Declanşare când Stare=monitor;


terminare când Stare<>monitor

La Intervalul specificat, trimite un mesaj


către CitireSenzor (out: CitireSenzor)

converteşte data în unităţi actuale, folosind


Valoare=(Conversion.bias*CitireSenzor)+Conversion.offset

NU
Valoare>=Prag?

DA

raportează alarmă

specification SenzorCritic
attribute Toleranţă: intervalul de date de toleranţă pentru acest senzor
service Eşantionează (out: rezultat)

citeşte CitireSenzor din actualul Senzor,


la Intervalul şi Toleranţa
corespunzătoare

returnează CitireSenzor

24
5 Realizarea documentaţiei finale OOA

Documentaţia finala a modelului AOO conţine:


• cele 5 straturi ale modelului OOA
• specificaţiile Claselor si Obiectelor.
Exemplu: Sistemul Senzor
Se observa Serviciile şi moştenirea Serviciilor în Structura Gen-Spec.
Serviciile Senzorului sunt:
Iniţializează, Eşantionează, MonitorizeazăCondiţiaDeAlaramă
Serviciile SenzoruluiCritic sunt:
Iniţializează, Monitorizează, Eşantionează (care este extins în SenzorCritic)

Senzor

Iniţializează
Eşantionează
Monitorizează

SenzorCritic

Eşantionează
Sistemul Senzor: straturile Claselor&Obiecte, Structurilor şi Serviciilor

Activitatea a V-a a metodei Coad-Yourdon: Identificarea subiecţilor

În AOO termenul de Subiect este un mecanism de pentru ghidarea cititorului (analist,


manager, expert, utilizator, client) în înţelegerea unui model de analiză foarte mare şi complex.
Subiecţii oferă o imagine de perspectivă asupra unui model complex.
Numărul de clase în modelul AOO este dependent de complexitatea problemei. În medie
poate fi de 30-35 clase. 100 de clase sunt deja prea multe. Dar pot exista domenii ale unor probleme
care la rândul lor se impart în sub-domenii, fiecăruia corespunzându-I 50-100 de clase. Astfel de
modele sunt foarte dificil de înţeles dacă nu există un mecanism de coordonare a acestei
complexităţi.

Cum se selectează Subiecţii ?

Fiecare Clasă din vârful fiecărei Structuri se asignează unui Subiect. Fiecare Clasă&Obiect
care nu face parte din nici o Structură se asignează, de asemenea, unui Subiect.
Se recomandă studiul rezultatelor anterioare ale analizei OO pentru probleme similare
pentru a se utiliza Subiecţi identificaţi deja.

25
Cum se rafinează Subiecţii ?
Subiecţii se rafinează utilizând sub-domeniile problemei. De fapt, se aplică principiul
întreg-parte pe domeniul problemei.
Rafinarea ia în consideraţie interdependenţele şi interacţiunile minimale între Subiecţi.
“Interdependenţele” sunt exprimate de Structuri (Identificarea Structurilor) şi de Conexiunea
Instanţelor (Identificarea Atributelor). “Interacţiunile” sunt exprimate de Conexiunile prin Mesaje
(Message Connections - Identificarea Serviciilor) . Se folosesc deci, straturile Structurilor,
Atributelor şi Serviciilor în stabilirea şi rafinarea Subiecţilor.

Cum se construiesc ?

În stratul Subiecţilor, 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, Subiecţii se pot reprezenta în 3 moduri:
• Subiecţi neexpandaţi (doar dreptunghiul cu numele şi numărul lor)

1. Subiect 1

2. Subiect 2

Figura: Notaţia Subiectului neexpandat

• Subiecţi parţial expandaţi (conţinând lista claselor-obiecte componente)

2. Subiect 1

Clasa&Obiect1
Clasa&Obiect2

1. Subiect 2

Clasa&Obiect3
Clasa&Obiect4

Figura: Notaţia Subiectului parţial expandat (opţiune CASE)

• Subiecţi total expandaţi (când se reprezintă împreună cu alte straturi ale modelului
OOA, prin zone partiţionate şi numerotate)

1 1 2 2

1 1 2 2
Figura: Notaţia Subiectului total expandat

26
Clasă&Obiect poate face parte din mai mulţi Subiecţi (când e necesar pentru ghidarea
cititorului). Subiecţii pot conţine la rândul lor Subiecţi, furnizând astfel o hartă multi-nivel.

Când se introduc Subiecţii în model ?

În general, Subiecţii sunt necesari pentru modele relativ mari, având aproximativ 30-40
clase. Identificarea Subiecţilor se va face după ce Clasele şi Obiectele au fost identificate şi bine
înţelese.

Exemplu - Sistemul Senzor

Sistemul Senzor este atât de mic încât nu necesită un mecanism pentru ghidarea cititorului.
Dacă totuşi dorim introducerea unui strat Subiect, este suficient unul singur.

1. Senzor
Stratul Subiecţilor

Exemplu- Sistemul de Transport Aerian


cu identificarea stratului Subiecţilor, Claselor&Obiectelor,
Structurilor şi Serviciilor

Clasele din vârfurile Structurilor sunt:


• Misiune, SegmentDeZbor, Încărcătură
• Avion, DefecţiuneAvion
• Traiectorie
• Radar

Rafinarea Subiecţilor, prin combinarea: Misiune, SegmentDeZbor, Încărcătură într-un singur


Subiect:
• Misiune
• Avion, DefecţiuneAvion
• Traiectorie
• Radar

Rafinare suplimentară, combinând prin:


• interacţiune: Radar, Localizare, Traiectorie (vezi Identificarea Serviciilor)
• interdependenţă: Avion, DefecţiuneAvion (vezi Identificarea Atributelor)

Rezultă Subiecţii:
• Misiune
• Poziţionare
• Avion

Rafinare prin combinarea sub-domeniilor problemei:


• Misiune
• Transport aerian
1. Misiune 2. Transport aerian
Stratul subiecţilor
27
1 1 2 2
Misiune DefecţiuneAvion

0,m

0,1 Avion Traiectorie

SegmentZbor ConstruieşteTraiectori
e
0,m PrezicePoziţie

0,m 2,m (2)

0,1 0,1 0,m

Încărcătură Radar Localizare


(1)

CautăÎnSpaţiu
2 2

ÎncărcăturăCargo Pasageri

1 1
Sistemul de transport aerian – stratul Subiecţilor, stratul Clase&Obiecte,
stratul Structurilor şi stratul Serviciilor

28
UML – Unified Modeling Language

Introducere
Problema principală care apare în dezvoltarea programelor este complexitatea. Folosirea
de modele poate înlesni abordarea de probleme complexe.
Un model este o simplificare unui anumit sistem, ce 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 sau.
Folosirea tehnicii ” divide et impera” permite înţelegerea părţilor componente ale unui
sistem, iar ansamblul sistemului ca o interacţiune între părţile acestuia. Un model reuşit reţine
caracteristicile importante ale obiectului modelat ş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 între
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ă lipsei unor elemente importante ale acestuia; adesea o serie de amănunte
subtile ce sunt evidente pentru designer nu sunt transmise. 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 al 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 asupra
problemei şi de pregătirea acestuia. Folosirea unui limbaj ”intern” nu trebuie privită ca negativă,
ea având se pare un rol esenţial în gândire în general şi în modelare în particular. Modelarea este
posibilă, chiar şi fără folosirea explicită a unui limbaj.
Formalismul limbajului de modelare constă în stabilirea de elemente cu o semantică
asupra căreia se convine şi cu ajutorul cărora să se poată transmite idei în mod cât mai eficient şi
mai puţin ambiguu.
Limbajul de modelare UML îşi propune să definească o modalitate cât mai precisă,
generală şi extensibilă de comunicare a modelelor. El a fost creat în primul rând pentru a facilita
proiectarea programelor, însă datorită expresivităţii sale poate fi folosit şi în alte domenii
(proiectare hardware, modelarea proceselor de afaceri etc.). Folosirea UML facilitează
comunicarea modelelor, însă nu asigură crearea unui model bun. Limbajul de modelare nu
specifică şi nu poate specifica ce modele trebuie create, în ce ordine şi cum trebuie ele utilizate
pentru un sistem particular.

Începând cu mijlocul anilor 1970 şi continuând în anii 1980 au apărut diverse limbaje de
modelare, odată cu creşterea experienţei de lucru în cadrul paradigmei orientate obiect. Astfel,
numărul de limbaje de modelare ajunge de la 10 până la mai mult de 50 în perioada 1989-1994.
Limbajele de modelare de succes din această perioadă sunt:
• Booch - potrivită mai ales pentru proiectare şi implementare, cu dezavantajul
unor notaţii complicate;
• OOSE (Object-Oriented Software Engineering) - această metodă a propus aşa-
numitele cazuri de utilizare, care ajutau la înţelegerea comportamentului
întregului sistem;
• OMT - Object Modeling Technique - potrivită pentru analiză şi sisteme
informaţionale cu multe date.

La mijlocul anilor 1990, când este semnalată o nouă generaţie de limbaje de modelare, a
început un proces de omogenizare, prin încorporarea în fiecare limbaj a caracteristicilor găsite în
celelalte limbaje.
Cei trei autori ai limbajelor de modelare principale, Booch, Jacobson şi Rumbaugh au
ajuns la concluzia ca ar fi mai bine să conducă evoluţia limbajelor lor pe un acelaşi drum, pentru
a elimina diferenţele gratuite ce nu făceau decât sa încurce utilizatorii. Un al doilea motiv a fost
unul pragmatic, reieşit din necesitatea industriei software de a avea o oarecare stabilitate pe piaţa
limbajelor de modelare. Al treilea motiv a fost convingerea că prin unirea forţelor se pot aduce
îmbunătăţiri tehnicilor existente.
Dezvoltarea UML a început în mod oficial în octombrie 1994, când Rumbaugh s-a
alăturat lui Booch în cadrul companiei Rational Software, ambii lucrând la unificarea limbajelor
Booch şi OMT.
Versiunea preliminară 0.8 a Unified (Metoda Unificată - aşa cum era numită atunci) a
fost publicată în octombrie 1995. Tot atunci, Jacobson s-a alăturat echipei de la Rational şi
scopul UML a fost extins pentru a include şi OOSE. În iunie 1996 a fost publicată versiunea 0.9
a UML. Pe parcursul anului 1996, ca o consecinţă a reacţiilor comunităţii proiectanţilor de
sistem, a devenit clar ca UML este văzut de către multe companii ca o opţiune strategică pentru
dezvoltarea produselor lor.
A fost creat un consorţiu UML format din organizaţii doritoare să aloce resurse pentru a
obţine o definiţie finală a UML. Dintre aceste companii care au contribuit la crearea UML 1.0 au
făcut parte DEC, Hewlet-Packard, I-Logix, Intellicorp, IBM, MCI Systemhouse, Microsoft,
Oracle, Rational, Texas Instruments etc.
UML 1.0 a fost propus spre standardizare în cadrul OMG î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 către OMG în noiembrie 1997.
Versiunea 1.2 a UML a fost o revizie editorială; versiunile 1.3 au fost publicate începând
cu sfârşitul anului 1998.
În septembrie 2001 a fost publicată versiunea 1.4 şi în martie 2003 a fost versiunea 1.5.
În octombrie 2004 a fost introdusă versiunea 2.0.

UML este un limbaj de modelare bazat pe notaţii grafice folosit pentru a specifica,
vizualiza, construi şi documenta componentele unui program.
UML este un limbaj cu ajutorul căruia se pot construi (descrie) modele. Un model
surprinde un anumit aspect al unui program şi acelaşi model poate fi descris la diferite nivele de
abstractizare. Fiecărui model îi corespunde o diagramă. Tipurile de diagrame existente în UML
sunt:

¾ Diagrama cazurilor de utilizare (Use Case Diagram)


¾ Diagrama de clase (Class Diagram)
¾ Diagrame care descriu comportamentul:
• Diagrame de interacţiuni (Interactions Diagrams)
- Diagrama de secvenţă (Sequence Diagram)
- Diagrama de colaborare (Collaboration Diagram)
• Diagrama de stări (State chart Diagram)
• Diagrama de activităţi (Activity Diagram)
¾ Diagrame de implementare:
• Diagrama de componente (Component Diagram)
• Diagrama de plasare (Deployment Diagram)

Fiecăreia din cele trei mari faze din dezvoltarea un proiect software îi corespund una sau
mai multe diagrame UML şi anume:
- pentru faza de analiza se utilizează diagrama cazurilor de utilizare şi diagrama de
activităţi;
- în faza de proiectare se folosesc: diagrama de clase pentru precizarea structurii
sistemului şi diagramele de stări şi interacţiune pentru descrierea comportamentului
acestuia;
- în faza de implementare se utilizează diagramele de implementare.

1. Diagrama cazurilor de utilizare (Use Case Diagram)


O diagramă use case este una din diagramele folosite în UML pentru a modela aspectele
dinamice ale unui program alături de diagrama de activităţi, diagrama de stări, diagrama de
secvenţă şi diagrama de colaborare. Altfel spus, diagramele use case se utilizează pentru:
• pentru a modela contextul unui sistem: determinarea graniţelor sistemului şi a
actorilor cu care acesta interacţionează.
• pentru a modela cerinţele unui sistem: ce trebuie să facă sistemul (dintr-un punct de
vedere exterior sistemului) independent de cum trebuie să facă. Va rezulta specificarea
comportamentului dorit. Sistemul apare ca o cutie neagră. Ceea ce se vede este cum
reacţionează el la acţiunile din exterior.

Elementele componente ale unei diagrame use case sunt:


- use case-uri;
- actori;
- relaţiile care se stabilesc între use case-uri, între actori şi între use case-uri şi actori.

Use case-uri

Un use case (caz de utilizare) reprezintă cerinţe ale utilizatorilor. Este o descriere a unei
mulţimi de secvenţe de acţiuni (incluzând variante) pe care un program le execută atunci când
interacţionează cu entităţile din afara lui (actori) şi care conduc la obţinerea unui rezultat
observabil şi de folos actorului. Un use case descrie ce face un program sau subprogram, dar nu
precizează nimic despre cum este realizată (implementată) o anumită funcţionalitate.
Fiecare use case are un nume prin care se deosebeşte de celelalte use case-uri. Acesta
poate fi un şir arbitrar de caractere, însă de regulă numele sunt scurte fraze verbale care
denumesc un comportament ce există în vocabularul sistemului ce trebuie modelat.
Figura 1.1 prezintă notaţia grafică pentru use case.
Figura 1.1: Notaţia grafică pentru use case

Comportamentul unui use case poate fi specificat descriind un flux de evenimente într-un
text suficient de clar ca să poată fi înţeles de cineva din exterior (de exemplu utilizatorul). Acest
flux de evenimente trebuie să includă cum începe şi se termină use case-ul atunci când acesta
interacţionează cu actori, ce obiecte sunt interschimbate, precum şi fluxuri alternative ale acestui
comportament. Aceste fluxuri de evenimente reprezintă scenarii posibile de utilizare a
sistemului.
Identificarea use case-urilor se face pornind de la cerinţele utilizatorului şi analizând
descrierea problemei.

Actori

Un actor reprezintă idealizarea unei peroane, proces sau obiect exterior care
interacţionează cu un sistem, subsistem sau o clasă. Actorii sunt entităţi exterioare sistemului. Ei
pot fi utilizatori (persoane), echipamente hardware sau alte programe. Fiecare actor are un nume
care indică rolul pe care acesta îl joacă în interacţiunea cu programul.
Notaţie grafică pentru un actor este ilustrată în figura 1.2.

nume

Figura 1.2: Notaţia grafică pentru actor

Există două moduri în care actorii pot interacţiona cu un sistem:


• folosind sistemul, adică iniţiază execuţia unor use case-uri;
• sunt folosiţi de către sistem, adică oferă funcţionalitate pentru realizarea unor use
case-uri.
Fiecare actor trebuie să comunice cu cel puţin un use case.
Pentru identificarea actorilor ar trebui să răspundem la următoarele întrebări:
• Cine foloseşte programul?
• De cine are nevoie programul pentru a-şi îndeplini sarcinile?
• Cine este responsabil cu administrarea sistemului?
• Cu ce echipamente externe trebuie să comunice programul?
• Cu ce sisteme software externe trebuie să comunice programul?
• Cine are nevoie de rezultatele (răspunsurile) oferite de program?

Relaţii

După cum am mai precizat, relaţiile exprimă interacţiuni între use case-uri, între actori şi
între use case-uri şi actori. Relaţiile pot fi de mai multe tipuri: asociere, dependenţă şi
generalizare.
Relaţia de asociere se defineşte între actori şi use case-uri, sau între use case-uri. Este folosită
pentru a exprima interacţiunea (comunicarea) între elementele pe care le uneşte. Relaţia de
asociere se reprezintă grafic printr-o linie şi este evidenţiată în exemplele din figurile 1.3 şi 1.4.

Fig. 1.3. Exemplu de asociere între use case-uri

Fig. 1.4. Exemplu de asociere între actor şi use case

Relaţia de dependenţă se poate stabili numai între use case-uri. Acest tip de relaţie modelează
două situaţii:
• cazul în care un use case foloseşte funcţionalitatea oferită de un alt use case -
dependenţa de tip include;
• există variante ale aceluiaşi use case – dependenţa de tip extend.
Dependenţa de tip include. Notaţia grafică este dată în figura 1.5.

Fig. 1.5. Dependenţă de tip include

În acest caz comportamentul use case-ului B este inclus în use case-ul A. B este de sine
stătător, însă este necesar pentru a asigura funcţionalitatea use case-ului de bază A. În exemplul
din figura 1.6, use case-ul Stabileste grupul care lucreaza la campanie are o
relaţie de dependenţă de tip include cu use case-ul Gaseste campanie. Aceasta înseamnă că
atunci când actorul Manager campanie utilizează Stabileste grupul care
lucreaza la campanie, comportamentul use case-ului Gaseste campanie va fi
inclus pentru a putea selecta o campanie relevantă.

Fig. 1.6. Diagramă use case cu dependenţă de tip include

Dependenţa de tip include se foloseşte şi pentru a scoate în evidenţă un comportament


comun (B poate fi inclus în mai multe use case-uri de bază – vezi figura 1.7).

Fig. 1.7. Dependenţă de tip include în care un use case este inclus
în mai multe use case-uri de bază
Dependenţa de tip extend. Notaţie grafică se poate vedea în figura 1.8.

Fig. 1.8. Dependenţă de tip extend

În acest caz comportamentul use case-ului B poate fi înglobat în use case-ul A. A şi B


sunt de sine stătătoare. A controlează dacă B va fi executat sau nu (vezi exemplul din figura 1.9).

Fig. 1.9. Exemplu de dependenţă de tip extend

Într-o dependenţă de tip extend pot apărea aşa numitele puncte de extensie care specifică
locul în care use case-ul specializat (B) extinde use case-ul de bază (A). Pentru fiecare use case
pot fi specificate mai multe puncte de extensie. Fiecare dintre aceste puncte trebuie să aibă un
nume. Aceste nume trebuie să fie unice, însă nu este obligatoriu ca ele să coincidă cu numele use
case-urilor specializate. De asemenea, trebuie precizată condiţia de care depinde apelul use case-
ului specializat. Acest tip de relaţie se foloseşte pentru a modela alternative. În figura 1.10 este
prezentată o diagramă use case cu dependenţă extend care are puncte de extensie.

Fig. 1.10. Diagramă use case cu dependenţă de tip extend cu punct de extensie

Relaţia de generalizare se stabileşte între elemente de acelaşi tip (doi actori, sau doua use case-
uri).
Este similară relaţiei de generalizare (moştenire) care se stabileşte între clase. Figura 1.11
ilustrează notaţia grafică pentru relaţia de generalizare între use case-uri. Elementul derivat B
moşteneşte comportamentul şi relaţiile elementului de bază A. Use case-ul B este o specializare a
use case-ului A.
În cazul unei relaţii de generalizare între use case-uri comportamentul poate fi modificat
sau extins; use case-ul derivat B poate înlocui în anumite situaţii use case-ul de bază A. Case-ul
derivat B controlează ce anume se execută şi ce se modifică din use case-ul de bază A.
Fig. 1.11. Notaţia grafică pentru relaţia de generalizare între use case-uri

În figura 1.12 este prezentat un exemplu de relaţie de generalizare între use case-uri.

Fig. 1.12. Exemplu de relaţie de generalizare între use case-uri

După cum am precizat mai sus, relaţia de generalizare se poate aplica şi între actori. În
exemplul din figura 1.13 este prezentată o relaţie de generalizare între actori.

Fig. 1.13. Relaţie de generalizare între actori

În exemplul din figura 1.14a actorii A şi B joacă acelaşi rol R atunci când interacţionează
cu use case-ul UC şi joaca roluri diferite în interacţiunea cu use case-ul UC în figura 1.14b.

(a)
(b)
Fig. 1.14. Tipuri de roluri jucate de actori în interacţiunea cu use case-ul

În figura 1.15 este prezentată o diagramă use case care utilizează toate tipurile de relaţii
definite anterior.
Fig. 1.15. Exemplu de diagramă use case

2. Diagrama de clase (Class Diagram)


Diagrama de clase este folosită pentru a modela structura (viziunea statică asupra) unui
sistem. O astfel de diagramă conţine clase / interfeţe, obiecte şi relaţii care se stabilesc între
acestea. Relaţiile pot fi de tipul:
• asociere;
• agregare;
• generalizare;
• dependenţă;
• realizare.
Clasele sunt folosite pentru a surprinde vocabularul sistemului ce trebuie dezvoltat. Ele
pot include:
• abstracţii care fac parte din domeniul problemei;
• clase necesare la momentul implementării.

O clasă poate reprezenta entităţi software, entităţi hardware sau concepte. Modelarea unui
sistem presupune identificarea elementelor importante din punctul de vedere al celui care
modelează. Aceste elemente formează vocabularul sistemului. Fiecare dintre aceste elemente are
o mulţime de proprietăţi.
Clase

Elementele unei clase sunt:


Nume - prin care se distinge de alte clase - o clasă poate fi desenată arătându-i numai
numele;
Atribute - reprezintă numele unor proprietăţi ale clasei;
Operaţii (metode) - reprezintă implementarea unor servicii care pot fi cerute oricărui
obiect al clasei.
Notaţia grafică pentru clasă poate fi observată în figura 2.1.

JOB

Figura 2.1. Notaţia grafică pentru clasă

Specificatorii de vizibilitate au următoarea semnificaţie:


• + public (dreptul de utilizare se acordă şi celorlalte clase);
• - private (numai clasa dată poate folosi atributul sau operaţia);
• # protected (posibilitatea de utilizare este accesibilă numai claselor succesoare)

O clasă poate avea oricâte atribute şi operaţii sau poate să nu aibă nici un atribut sau nici
o operaţie. Modelarea vocabularului unui sistem presupune identificarea elementelor pe care
utilizatorul sau programatorul le foloseşte pentru a descrie soluţia problemei. Pentru fiecare
element se identifică o mulţime de responsabilităţi (ce trebuie să facă acesta), după care se
definesc atributele şi operaţiile necesare îndeplinirii acestor responsabilităţi.

Există trei tipuri particulare de clase (stereotipuri):


• clasa limită – realizează interacţiunea cu utilizatorii sau cu alte sisteme (notaţie -
figura 2.2a); permit interfaţarea cu alte sisteme software şi cu dispozitive fizice ca
imprimante, motoare, senzori etc.
• clasa entitate – modelează obiecte din interiorul domeniului aplicaţiei, dar externe
sistemului software, despre care sistemul trebuie să stocheze câteva informaţii (notaţie -
figura 2.2b);
• clasa de control – modelează coordonarea, secvenţierea, controlul altor obiecte
(notaţie - figura 2.2c).; recomandarea este să existe câte o clasă de control pentru fiecare
use-case.

a. b. c.
Figura 2.2. Notaţii grafice pentru stereotipuri
Obiecte

Obiectele sunt instanţe ale claselor. Obiectele au identitate şi valori ale atributelor.
Notaţia grafică pentru obiect se poate observa în figura 2.3.

Figura 2.3 Notaţii grafice pentru obiecte

Interfeţe

Interfaţa specifică un grup de operaţii caracteristice unei comportări determinate a unei


clase. Se modelează cu acelaşi simbol ca şi clasele. Interfaţa are numai operaţii. Pentru a le putea
deosebi de clase se plasează stereotipul <<interface>> sau caracterul “I” la începutul numelui
interfeţei respective.

Figura 2.4. Notaţii grafice pentru interfeţe

Clasă parametrizată (template)

Clasa parametrizată are unul sau mai mulţi parametri formali. Prin intermediul acestei
clase se poate defini o familie de clase dând valori parametrilor formali. De obicei parametrii
reprezintă tipuri ale atributelor. Notaţia grafică se poate vedea în figura 2.5.

Figura 2.5. Exemplu de clasă parametrizată

Relaţii

O relaţie modelează o conexiune semantică sau o interacţiune între elementele pe care le


conectează. În modelarea orientată obiect cele mai importante relaţii sunt relaţiile de asociere,
dependenţa, generalizare şi realizare. Un caz particular al relaţiei de asociere îl constituie relaţia
de agregare. Între clase se pot stabili relaţii de generalizare, dependenţă şi realizare. Relaţiile de
asociere şi agregare se stabilesc între instanţe ale claselor.
a) b)

c) d)

e) e)
Figura 2.6. Notaţii grafice pentru diferite tipuri de relaţii: a)asociere bidirecţională;
b)asociere unidirecţională; c) agregare; d) dependenţă; e) generalizare, f) realizare

Relaţia de asociere

Relaţia de asociere exprimă o conexiune semantică sau o interacţiune între obiecte


aparţinând unor anumite clase. Asocierea poartă informaţii despre legăturile dintre obiectele unui
sistem. Pe măsură ce sistemul evoluează, pot fi create legături noi între obiecte sau pot fi distruse
legăturile existente. Relaţia de asociere oferă baza arhitecturală pentru modelarea conlucrării şi
interacţiunii dintre clase.
O asociere interacţionează cu obiectele sale prin intermediul capetelor de asociere.
Capetele de asociere pot avea nume, cunoscute sub denumirea de roluri, şi vizibilitate, ce
specifică modul în care se poate naviga înspre respectivul capăt de asociere. Cea mai importantă
caracteristică a lor este multiplicitatea, ce specifică câte instanţe ale unei clase corespund unei
singure instanţe a altei clase. De obicei multiplicitatea este folosită pentru asociaţiile binare.
Reprezentarea grafică a asocierii este o linie (sau drum) ce conectează clasele ce participă
în asociere. Numele asocierii este plasat pe linie, iar multiplicitatea şi rolurile sunt plasate la
extremităţile sale (figura 2.7).

Figura 2.7: Notaţia grafică detaliată a relaţiei de asociere

Observaţie. Numele rolurilor pot fi omise (eventual şi numele asocierii)


Este posibilă specificarea direcţiei unei asocieri, pentru a elimina legături redundante sau
irelevante pentru un anumit model. În această situaţie notaţia grafică pentru relaţia de asociere
este o linie cu o săgeată la unul din capete indicând direcţia asocierii (figura 2.6b).

Relaţia de agregare

Relaţia de agregare este o asociere ce modelează o relaţie parte-întreg. Este reprezentată


ca un romb gol ataşat la capătul asocierii de lângă clasa agregat (figura 2.6c). În figură o instanţă
a clasei A conţine o instanţă a clasei B (altfel spus un obiect B este o parte unui obiect A).
Relaţia de agregare este deci un caz particular al relaţiei de asociere. Ea poate avea toate
elementele unei relaţii de asociere, însă în general se specifică numai multiplicitatea. Se foloseşte
pentru a modela situaţiile în care un obiect este format din mai multe componente.
Compoziţia este o formă mai puternică a agregării. Partea are „timpul de viaţă” al
întregului. Întregul poate avea responsabilitatea directă pentru crearea sau distrugerea părţii sau
poate accepta o altă parte creată şi mai apoi să paseze „responsabilitatea” altui întreg.

Obs. Instanţele nu pot avea relaţii de agregare ciclice (o parte nu poate conţine întregul)
În figura 2.8 este prezentat un exemplu în care se foloseşte agregarea şi compoziţia.

Figura 2.8. Exemplu de relaţii de agregare şi compoziţie

Relaţia de dependenţă

O dependenţă (figura 2.6d) indică o relaţie semantică între două elemente ale modelului.
Se foloseşte în situaţia în care o schimbare în elementul destinaţie (B) poate atrage după sine o
schimbare în elementul sursă (A). Această relaţie modelează interdependenţele ce apar la
implementare.

Figura 2.9. Exemplu de relaţii de dependenţă

Dependenţele pot să apară şi la structurarea unui sistem în pachete, definind arhitectura


sistemului.

Relaţia de generalizare

Relaţia de generalizare (figura 2.6e) se foloseşte pentru a modela conceptul de moştenire


între clase. În figură clasa A este derivată din clasa B. Spunem că A este clasa derivată (sau
subclasa, sau clasa copil), iar B este clasa de baza (sau superclasă, sau clasă părinte).
Relaţia de generalizare mai poartă denumirea de relaţie de tip is a (este un fel de), în
sensul că o instanţă a clasei derivate A este în acelaşi timp o instanţă a clasei de bază B (clasa A
este un caz particular al clasei B sau, altfel spus, clasa B este o generalizare a clasei A). Clasa A
moşteneşte toate atributele şi metodele clasei B. Ea poate adăuga noi atribute sau metode sau le
poate redefini pe cele existente.
Figura 2.10. Exemplu de relaţie de generalizare

Relaţia de realizare
Relaţia de realizare (figura 2.6f) se foloseşte în cazul în care o clasă (A) implementează o
interfaţă (B). Se spune că A realizează interfaţa specificată de B. O interfaţă este o specificare
comportamentală fără o implementare asociată. O clasă include o implementare. Una sau mai
multe clase pot realiza o interfaţă prin implementarea metodelor definite de acea interfaţă.

Figura 2.11. Exemplu de relaţie de realizare.

În figura 2.11 sunt prezentate două diagrame de clase în care se folosesc toate tipurile de
relaţii prezentate mai sus.
Figura 2.12. Exemple de diagrame de clase
3. Diagrama de stări (State chart Diagram)

Comportamentul unui program poate fi descris prin următoarele tipuri de diagrame:


• diagrama de stări
• diagrama de activităţi
• diagrama de interacţiuni:
- diagrama de secvenţe
- diagrama de colaborare

Diagrama de stări este folosită pentru a modela comportamentul unui singur obiect.
Diagrama de stări specifică o secvenţă de stări prin care trece un obiect de-a lungul vieţii sale ca
răspuns la evenimente împreună cu răspunsul la aceste evenimente.

Noţiuni preliminare

Un eveniment reprezintă ceva (atomic) ce se întâmplă la un moment dat şi care are


ataşată o locaţie în timp şi spaţiu. Evenimentele modelează apariţia unui stimul care poate
conduce la efectuarea unei tranziţii între stări. Evenimentele nu au durată în timp.
Evenimentele pot fi clasificate în felul următor:
• sincrone sau asincrone
• externe sau interne
Evenimentele externe se produc între sistem şi actori (de exemplu apăsarea unui buton
pentru întreruperea execuţiei programului).
Evenimentele interne se produc între obiectele ce alcătuiesc un sistem (de exemplu
overflow exception).
Evenimentele pot include:
• semnale; semnalul este un stimul asincron care are un nume şi care este trimis de
un obiect şi recepţionat de altul (ex: excepţii).
• apeluri de operaţii (de obicei sincrone): un obiect invoca o operaţie pe un alt
obiect; controlul este preluat de obiectul apelat, se efectuează operaţia,
obiectul apelat poate trece într-o nouă stare, după care se redă controlul
obiectului apelant.
• trecerea timpului
• o schimbare a rezultatului evaluării unei condiţii
O acţiune reprezintă execuţia atomică a unui calcul care are ca efect schimbarea stării
sau returnarea unei valori. Acţiunile au o durată mică în timp, fiind tranzitorie (ex.: i++).
Prin activitate se înţelege execuţia neatomică a unor acţiuni. Activităţile au durată în
timp, pot persista pe toată durata stării şi poate fi întreruptă (ex.: execuţia unei funcţii).
O diagramă de stări poate conţine stări şi tranziţii.

Starea

Prin stare se înţelege o condiţie sau situaţie din viaţa unui obiect în timpul căreia acesta:
• satisface anumite condiţii;
• efectuează o activitate;
• aşteaptă apariţia unui eveniment.
Notaţia grafică pentru stare este prezentată în figura 3.1.

Figura 3.1: Notaţia grafică pentru stare

Există două stări particulare şi anume starea iniţială şi starea finală.


Starea iniţială (figura 3.2a) este starea din care pleacă entitatea modelată.
Starea finală (figura 3.2b) este starea în care entitatea modelată îşi încheie existenţa.

a) b)

Figura 3.2: Notaţii grafice pentru starea iniţială (a) şi starea finală (b)

Elementele care caracterizează o stare sunt:

• Nume - identifică în mod unic o stare; numele este reprezentat de o succesiune de şiruri de
caractere.
• Acţiuni de intrare/ieşire - sunt acţiuni ce se produc la intrarea, respectiv ieşirea din starea
respectivă.
• Substări care pot fi
- concurente (simultan active) – figura 3.3a
- disjuncte (secvenţial active) –figura 3.3b

b)

a)

Figura 3.3. Notaţii grafice pentru substări concurente (a) şi disjuncte (b)

• Tranziţii interne - sunt acţiuni şi activităţi pe care obiectul le execută cât timp se află în
acea stare; se produc între substări şi nu produc schimbarea stării
obiectului.
Figura 3.4. Notaţia completă pentru stare

nume_eveniment – identifică circumstanţele în care acţiunea specificată se execută;


condiţie_gardă – condiţie booleană care se evaluează la fiecare apariţie a evenimentului
specificat; acţiunea se execută doar când rezultatul evaluării este
true;
acţiunea – poate folosi atribute şi legături care sunt vizibile entităţii modelate.

După cum se poate observa din figura 3.4, două evenimente au notaţii speciale: entry
şi exit. Aceste evenimente nu pot avea condiţii gardă deoarece se invocă implicit la intrarea,
respectiv ieşirea din starea respectivă.
Activităţile sunt precedate de cuvântul cheie do.
Pentru a arăta că o stare conţine substări, se foloseşte cuvântul cheie include, urmat de
numele diagramei substărilor.

Figura 3.5. Exemplu de stare

Tranziţia

O tranziţie reprezintă o relaţie între două stări indicând faptul că un obiect aflat în prima
stare va efectua nişte acţiuni şi apoi va intra în starea a doua atunci când un anumit eveniment se
petrece. Notaţia grafică pentru tranziţie se poate observa în figura 3.6.
Starea sursă reprezintă starea din care se pleacă.
Eveniment este evenimentul care declanşează tranziţia.
Condiţie gardă (guard condition) este o expresie booleană. Aceasta se evaluează la
producerea evenimentului care declanşează tranziţia. Tranziţia poate avea loc numai dacă
condiţia este satisfăcută.
Figura 3.6. Notaţia grafică pentru tranziţie

Acţiune - opţional se poate specifica o acţiune care să se execute odată cu efectuarea


tranziţiei.
Starea destinaţie reprezintă starea în care ajunge obiectul după efectuarea tranziţiei.
În figurile 3.7 şi 3.8 se pot observa exemple de stări cu substări disjuncte, respectiv
concurente.

Figura 3.7. Exemplu de stare cu substări disjuncte

Figura 3.8. Exemplu de stare cu substări concurente


În figura 3.9 este prezentat un exemplu de diagramă de stare pentru un proiect propus de
un student

Figura 3.9. Exemplu de diagramă de stare pentru un proiect propus de un student

4. Diagrama de activităţi (Activity Diagram)

Diagrama de activităţi este o variantă a diagramei de stare şi este folosită pentru a modela
dinamica unui proces sau a unei operaţii.
Diagramele de activităţi scot în evidenţă controlul execuţiei de la o activitate la alta.
Diagramele de activităţi pot conţine:
• stări activităţi şi stări acţiuni, care sunt stări ale sistemului;
• tranziţii;
• obiecte;
• bare de sincronizare;
• ramificaţii.

Stările activitate (activity states) - pot fi descompuse, activitatea lor putând fi


reprezentată cu ajutorul altor diagrame de activităţi.
Stările activitate nu sunt atomice (pot fi întrerupte de apariţia unui eveniment) şi au
durată (îndeplinirea lor se face într-un interval de timp).

Stările acţiuni (action states) - modelează ceva care se întâmplă (de exemplu evaluarea
unei expresii, apelul unei operaţii a unui obiect, crearea/distrugerea unui obiect).
O stare acţiune reprezintă execuţia unei acţiuni.
Ea nu poate fi descompusă.
Stările acţiuni sunt atomice (nu pot fi întrerupte chiar dacă se produc evenimente) şi au o
durată nesemnificativă (foarte mică).
Notaţia grafică a stărilor activitate/acţiune se poate observa în figura 4.1.

Figura 4.1. Notaţia grafică a stărilor activitate/acţiune

Tranziţiile – reprezintă relaţii între două activităţi.


Tranziţia este iniţiată de terminarea primei activităţi şi are ca efect preluarea controlului
execuţiei de către a doua activitate.

Figura 4.2. Notaţia grafică a tranziţiei

În exemplul din figura 4.3, prima activitate este aceea în care se adaugă un client nou.
Tranziţia la a doua activitate (şi anume aceea de a atribui un staff de contact pentru o eventuală
campanie de promovare) implică faptul ca odată ce prima activitate s-a terminat, a doua
activitate este pornită.

Figura 4.3. Exemplu de două activităţi unite de o tranziţie

Ramificaţiile - se folosesc pentru a modela alternative (decizii) a căror alegere depinde


de o expresie booleană Au o tranziţie de intrare şi două sau mai multe tranziţii de ieşire
Fiecare tranziţie de ieşire trebuie să aibă o condiţie gardă
Condiţiile gardă trebuie să fie disjuncte (să nu se suprapună) şi să acopere toate
posibilităţile de continuare a execuţiei (vezi exemplele din figura 4.4), altfel fluxul de control al
execuţiei va fi ambiguu (nu se ştie pe care cale se va continua execuţia).
Condiţiile trebuie însă să acopere toate posibilităţile, altfel sistemul se poate bloca.
a)

b)

Figura 4.4. Exemple de activităţi cu puncte de ramificaţie

Uneori nu este necesară precizarea explicită a unui punct de decizie, pentru a simplifica
diagrama (vezi exemplul din figura 4.5).
În figurile 4.4 şi 4.5 apare un alt element al diagramelor de activităţi şi anume starea
finală.
În general, odată încheiată ultima activitate dintr-o diagramă, trebuie marcată tranziţia
spre starea finală. De asemenea, după cum se poate observa în figura 4.6, fiecare diagramă de
activităţi trebuie să înceapă cu starea iniţială.
Figura 4.5. Exemplu de alegere reprezentată fără un punct de ramificaţie explicit

Figura 4.6. Diagramă de activităţi cu stare iniţială şi finală

Bare de sincronizare

Există posibilitatea ca mai multe activităţi să se execute în paralel. Pentru sincronizarea


acestora se folosesc aşa numitele bare de sincronizare. Acestea pot fi de două feluri:
• fork - poate avea o tranziţie de intrare şi două sau mai multe tranziţii de ieşire, fiecare
tranziţie de ieşire prezentând un flux de control independent. Activităţile de sub fork sunt
concurente.
• join - reprezintă sincronizarea a două sau mai multor fluxuri de control. La join fiecare
flux de control aşteaptă până când toate celelalte fluxuri de intrare ajung în acel punct.
Poate avea două sau mai multe tranziţii de intrare şi o singură tranziţie de ieşire.

Figura 4.7. Notaţia grafică pentru barele de sincronizare

Conceptul de „swimlanes” modelează (arată) activităţile care au loc în interiorul unui


sistem. Diagrama se împarte în coloane care se intitulează semnificativ pentru entitatea pe care o
modelează (vezi figura 4.8a,b).

a)
b)

Figura 4.8. Diagrame de activităţi cu „swimlanes”

Obiecte

Acţiunile sunt realizate de către obiecte sau operează asupra unor obiecte. Un obiect
poate interveni într-o diagramă de activităţi în două moduri:
• o operaţie a unui obiect poate fi folosită drept nume al unei activităţi (figura 4.9);
• un obiect poate fi privit ca intrare sau ieşire a unei activităţi (figura 4.10).
Obiectele pot fi conectate de acţiuni prin linii punctate cu o săgeată la unul din capete
(orientarea săgeţii indică tipul parametrului - intrare sau ieşire)
Un obiect poate apărea de mai multe ori în cadrul aceleiaşi diagrame de activităţi.
Fiecare apariţie indică un alt punct (stare) în viaţa obiectului. Pentru a distinge apariţiile
numele stării obiectului poate fi adăugat la sfârşitul numelui obiectului .
Figura 4.9. Diagrama de activităţi cu operaţia obiectului ca activitate

Figura 4.10. Diagrama de activităţi cu fluxuri de obiecte


Figura 4.11. Diagramă de activităţi cu obiecte şi „swimlanes”
Figura 4.12. Exemplu de diagramă de activităţi pentru un automat de cafea
5. Diagrame de Interacţiuni
Diagramele de interacţiuni sunt folosite pentru a modela comportamentul unei mulţimi
de obiecte dintr-un anumit context care interacţionează în vederea îndeplinirii unui anumit scop.
Scopul specifică modul în care se realizează o operaţie sau un caz de utilizare.
Contextul unei interacţiuni (unde pot găsi interacţiuni) poate fi:
• sistem / un subsistem (uzual) - mulţimea obiectelor din sistem care colaborează între
ele;
• operaţie - interacţiuni între parametri, variabile locale şi globale;
• clasă - interacţiuni între atributele unei clase (cum colaborează ele), interacţiuni cu
obiecte globale, sau cu parametrii unei operaţii.
Obiectele care participă la o interacţiune pot fi lucruri concrete sau prototipuri. De obicei,
într-o colaborare obiectele reprezintă prototipuri ce joacă diferite roluri, şi nu obiecte specifice
din lumea reală.
Între obiectele care participă la o colaborare se pot stabili legături.
O legătură (link) reprezintă o conexiune semantică între obiecte. Ea este o instanţă a unei
asocieri şi poate avea toate atributele specifice asocierii (nume, roluri, navigare, agregare), dar nu
şi multiplicitate.
Obiectele care interacţionează comunică între ele, comunicarea făcându-se prin schimb
de mesaje.
Un mesaj specifică o comunicare între obiecte. El poartă o informaţie şi este urmat de o
activitate. Primirea unei instanţe a unui mesaj poate fi considerată o instanţă a unui eveniment.
Unui mesaj îi este asociată o acţiune care poate avea ca efect schimbarea stării actuale a
obiectului.
Forma generală a unui mesaj este
[cond_gardă] acţiune (lista_parametrilor)
unde
condiţie_gardă – condiţie booleană care se evaluează la fiecare apariţie a mesajului
specificat; acţiunea se execută doar când rezultatul evaluării este
true;

Tipuri de acţiuni existente în UML:


• call: invocă o operaţie a unui obiect. Un obiect îşi poate trimite lui însuşi un mesaj
(invocare locală a unei operaţii). Este cel mai comun tip de mesaj. Operaţia apelată
trebuie să fie definită de obiectul apelat şi vizibilă apelantului.
• return: returnează o valoare apelantului.
• send: trimite un semnal unui obiect.
• create: creează un obiect.
• destroy: distruge un obiect. Un obiect se poate autodistruge.

O diagramă de interacţiuni constă dintr-o mulţime de obiecte şi relaţiile dintre ele


(inclusiv mesajele care se transmit). Există două tipuri de diagrame de interacţiuni:
• diagrama de secvenţă;
• diagrama de colaborare.
Cele două diagrame specifică aceeaşi informaţie, însă pun accentul pe aspecte diferite.

5.1 Diagrama de secvenţă


Diagrama de secvenţă pune accentul pe aspectul temporal (ordonarea în timp a
mesajelor).
Notaţia grafică este un tabel (figurile 5.1, 5.2) care are pe axa X obiecte, iar pe axa Y
mesaje ordonate crescător în timp.
Axa Y arată pentru fiecare obiect:
• linia vieţii - linie punctată verticală;
• perioada în care obiectul preia controlul execuţiei - reprezentată printr-un dreptunghi
subţire pe linia vieţii; în această perioadă obiectul efectuează o acţiune, direct sau prin
intermediul procedurilor subordonate.

Figura 5.1. Exemplu de diagramă de secvenţă


Notaţia grafică pentru mesaje

A B Comunicare sincronă. Controlul execuţiei trece de la A la B şi


revine la A după ce B îşi termină execuţia. Exemplu: apel de funcţie.
A B Comunicare asincronă. A trimite un semnal după care îşi continuă
execuţia mai departe. Exemplu: aruncarea unei excepţii.
Întoarcere dintr-o funcţie (procedură). În cazul în care este omisă
se consideră implicit că revenirea se face la sfârşitul activării.
În figura 5.2 este prezentat un exemplu de diagramă de secvenţă. Mesajul
extrageNume() este primul mesaj recepţionat de Client şi corespunde cererii
Managerului de Campanie de a furniza numele clientului selectat. Obiectul Client
recepţionează apoi mesajul listeazăcampanii() şi începe a doua perioadă de activare.
Obiectul Client trimite apoi mesajul extrageDetaliiCampanie() fiecărui obiect
Campanie pe rând pentru a construi o listă a campaniilor. Această operaţiune repetată se
numeşte iteraţie şi se marchează prin caracterul „*” înaintea numelui mesajului. Condiţia de
continuare sau de încetare poate fi precizată în interiorul numelui mesajului. Condiţia de
continuare poate fi scrisă sub forma:
[pentru toţi clienţii campaniilor] *extrageDetaliiCampanie()
Manager Campanie trimite apoi un mesaj unui obiect particular Campanie
pentru a obţine o listă a reclamelor. Obiectul Campanie deleagă responsabilitatea pentru a
extrage titlul reclamei fiecărui obiect Reclamă deşi obiectul Campanie păstrează
responsabilitatea pentru lista reclamelor (fapt indicat de păstrarea activării şi după ce este trimis
mesajul).
Când o reclamă este adăugată la campanie este creat un obiect Reclama. Această creare
este indicată de mesajul Reclama() (care invocă un constructor).

Figura 5.2. Exemplu de diagramă de secvenţă care


modelează adăugarea unei noi reclame unei campanii

Obiectele pot fi create sau distruse la diferite momente ale interacţiunii. Distrugerea unui
obiect este marcată printr-un “X” pe linia vietii. Un obiect poate fi distrus când primeşte un
mesaj (ca în figura 5.3) sau se poate distruge singur la sfârşitul unei activări.
Figura 5.3. Distrugerea unui obiect

Un obiect îşi poate trimite un mesaj lui însuşi. Acest mesaj este cunoscut sub numele de
mesaj reflexiv şi este reprezentat de o săgeată care pleacă şi se termină pe o activare a aceluiaşi
obiect. Diagram de secvenţă din figura 5.4 include mesajul reflexiv
calculeazăCheltuieliregie() trimis de obiectul Campanie lui însuşi.

Figura 5.4. Diagramă de secvenţă care modelează Verifică bugetul campaniei

După cum am mai precizat, revenirea controlului la obiectul care a trimis un mesaj se
poate marca explicit în diagrama de secvenţă printr-o săgeată trasată cu linie întreruptă (figura
5.5). Valoarea de revenire de obicei nu se prezintă într-o diagramă de secvenţă.
Figura 5.5. Diagramă de secvenţă care modelează Verifică bugetul campaniei cu
marcarea explicită a revenirilor

Dacă mesajele sincrone (care invocă o operaţie) determină suspendarea execuţiei


obiectului sursă până când destinatarul îşi termină execuţia, mesajele asincrone nu necesită
suspendarea execuţiei obiectului ce trimite mesajul. Mesajele asincrone sunt des folosite în
aplicaţiile de timp real în care operaţiile diferitelor obiecte se execută în paralel, fie din motive
de eficienţă, fie deoarece sistemul simulează procese concurente. Este necesar ca o operaţie
invocată să notifice obiectul care a invocat-o în momentul când îşi termină execuţia. Această
notificare se face printr-un mesaj explicit (numit callback).

Constrângeri de timp

O diagramă de secvenţă poate fi etichetată cu constrângeri de timp în moduri diferite. În


figura 6 se asociază expresiile de timp cu numele mesajului astfel încât constrângerile de timp
pot fi specificate pentru execuţia unei operaţii sau transmisia unui mesaj. Spre exemplu funcţia
a.sendTime furnizează timpul la care mesajul a este trimis şi d.receiveTime furnizează
timpul la care o instanţă a clasei A primeşte mesajul d. Există construcţii care pot fi utilizate
pentru a marca un interval de timp – în figura 5.6 este marcat intervalul de timp scurs între
recepţia mesajului b şi trimiterea mesajului c. Constrângerile de timp sunt utilizate frecvent în
modelarea sistemelor de timp real. Pentru alte tipuri de sisteme constrângerile de timp nu sunt
semnificative.
Figura 5.6. Diagramă de secvenţă cu tipuri diferite de mesaje şi constrângeri de timp

Ramificaţii

Diagramele de secvenţă permit reprezentările ramificaţiilor prin mai multe săgeţi care
pleacă din acelaşi punct şi eventual sunt etichetate cu condiţii.
În figura 5.7 este prezentat un exemplu de diagramă de secvenţă cu ramificaţii.

Figura 5.7. Exemplu de diagramă de secvenţe cu ramificaţii.


5.2 Diagrama de colaborare
Diagrama de colaborare este o diagramă de interacţiuni care pune accentul pe organizarea
structurală a obiectelor care participă la interacţiune.
Diagrama de colaborare poate conţine:
• obiecte;
• legături între obiecte;
• mesajele prin care obiectele comunică.
Diagramele de colaborare au multe asemănări cu diagramele de secvenţă, exprimând
aceleaşi informaţii dar într-un alt format. Pot fi create la nivele diverse de detaliu şi în diferite
stadii de dezvoltare a procesului software. Deoarece au un conţinut similar, pot fi folosite pentru
generarea diagramelor de secvenţă şi viceversa.
Diferenţa semnificativă faţă de diagrama de secvenţă este aceea că diagrama de
colaborare arată explicit legăturile dintre obiecte. De asemenea, la diagrama de colaborare timpul
nu are o dimensiune explicită. Din acest motiv, ordinea în care sunt trimise mesajele este
reprezentată prin numere de secvenţă.
Mesajele dintr-o diagramă de colaborare sunt reprezentate de un set de simboluri care
sunt asemănătoare celor utilizate în diagrama de secvenţă, dar cu câteva elemente adiţionale
pentru a marca secvenţierea şi recurenţa.
Notă. Termenii care vor apărea între paranteze pătrate sunt opţionali iar termenii care vor apărea
între acolade pot să nu apară sau să apară de mai multe ori.

Sintaxa unui mesaj este următoarea:


[predecesor] [condiţie_gardă] expresie_secvenţă
[valoare_întoarsă ‘:=’] nume_mesaj ‘(‘[listă_argumente]’)’
unde
• predecesor – listă a numerelor de secvenţă a mesajelor care trebuie trimise înainte de
trimiterea mesajului curent (permite sincronizarea trimiterii mesajelor);
permite specificarea detaliată a căilor ramificaţiilor.
Sintaxa predecesorului este următoarea:
număr_secvenţă { ’,’ număr_secvenţă } ’/’
’/’ – marchează sfârşitul listei şi se include doar dacă este precizat explicit
predecesorul.
• condiţie_gardă – expresie booleană care permite condiţionarea transmiterii mesajului
(se scrie în OCL – Object Constraint Language) şi se poate utiliza
pentru reprezentarea sincronizării diferitelor fluxuri de control.
• expresie_secvenţă – listă de întregi separaţi prin caracterul ’.’, urmată opţional de
un nume (o singură literă), un termen recurenţă şi terminată de
caracterul ’:’.
Sintaxa expresiei_ secvenţă este următoarea:
întreg { ‘.’ întreg } [nume] [recurenţă] ‘:’
Se observă că numerotarea este asemănătoare celei utilizate la numerotarea
capitolelor şi paragrafelor într-un document.
întreg – precizează ordinea mesajului; poate fi folosit într-o construcţie de tip buclă
sau ramificaţie.
Exemplu: mesajul 5.1.3 se transmite după 5.1.2 şi ambele se transmit după activarea
mesajului 5.1.
nume – se foloseşte pentru a diferenţia două mesaje concurente când acestea au
acelaşi număr de secvenţă.
Exemplu: mesajele 3.2.1a şi 3.2.1b se transmit simultan în cadrul activării mesajului
3.2.
recurenţa – permite specificarea modului de transmitere a mesajelor:
ƒ secvenţial - ‘*’‘[‘clauză_iteraţie’]’
ƒ paralel - ‘||’‘[‘clauză_iteraţie’]’
ƒ ramificaţie - ‘[‘clauză_condiţie’]’

În tabelul următor se prezintă câteva exemple de tipuri de mesaje:

Tipuri de mesaje: Exemple

mesaj simplu 4: adaugă ReclamaNoua ()

subapeluri cu valoarea întoarsă 3.1.2: nume:= extrageNume ()


Valoarea întoarsă este plasată în variabila nume

mesaj condiţional
mesajul este trimis doar dacă este adevărată [balanţă > 0] 5: debit (sumă)
condiţia [balanţă > 0]

sincronizare cu alte mesaje


mesajul 4: playVideo este trimis doar după
3.1a, 3.1b / 4: playVideo()
ce mesajele concurente 3.1a şi 3.1b sunt
complete

iteraţii [i = 1..n] update ()

În figura 5.8 este prezentată diagrama de colaborare corespunzătoare diagramei de


secvenţă din figura 5.1.
Figura 5.8. Exemplu de diagramă de colaborare

Există mai multe posibilităţi de interacţiune pentru un use case particular. Acestea se
datorează alocărilor posibile diferite ale responsabilităţilor. Spre exemplu, interacţiunile din
figura 5.9 pot avea trăsături nedorite. Mesajul extrageDetaliiCampanie trimis de
Client obiectului Campanie necesită ca obiectul Client să returneze aceste detalii
obiectului AdaugaReclama. Dacă detaliile despre campanie includ doar numele campaniei
atunci un volum relativ mic de date este pasat de la Campanie la Client şi apoi la
AdaugaReclama. Acest fapt poate fi acceptabil.

Figura 5.9. Diagrama de colaborare pentru use case-ul


Adaugă o reclamă nouă unei campanii
Pe de altă parte, dacă detaliile despre campanie includ data de start, de terminare şi
bugetul campaniei, atunci prin Client se pasează mai mult de o dată. În acest caz obiectul
Client este acum responsabil pentru furnizarea unor date semnificative pentru campanii în loc
de obiectul Campanie. S-ar putea deci transfera date direct de la Campanie la
AdaugaReclama. Această posibilitate este prezentată în figura 5.10, în care
AdaugaReclama preia responsabilitatea de a extrage detalii despre campanie direct de la
obiectul Campanie. În această interacţiune, obiectul Client este responsabil doar de
furnizarea unei liste de campanii obiectului AdaugăReclamă.

Figura 5.10. Diagramă de colaborare alternativă pentru use case-ul


Adaugă o reclamă nouă unei campanii

În figura 5.11 este prezentată o diagramă de colaborare care prezintă interacţiunile pentru
o singură operaţie – verificăBugetCampanie() – care este una din operaţiile din
diagramele din figurile 5.9 şi 5.10.
Diagramele de colaborare sunt preferate de unii dezvoltatori diagramelor de secvenţă
deoarece interacţiunile între obiecte pot fi translate uşor în diagramele de clase datorită
vizibilităţii legăturilor între obiecte.

Figura 5.11. Diagrama de colaborare pentru operaţia verificaBugetCampanie()


6. Organizarea modelelor în pachete

Pachetul (package) este o grupare de elemente ale unui model (use case-uri, clase etc.) şi
reprezintă baza necesară controlului configuraţiei, depozitării şi accesului. Este un container
logic pentru elemente între care se stabilesc legături.
Pachetul defineşte un spaţiu de nume.
Toate elementele UML pot fi grupate în pachete (cel mai des pachetele sunt folosite
pentru a grupa clase). Un element poate fi conţinut într-un singur pachet.
Un pachet poate conţine subpachete, deci se creează o structură arborescentă (similară cu
organizarea fişierelor/directoarelor).

Notaţia grafică pentru pachet este prezentată în figura 6.1.

Figura 6.1. Notaţia grafică pentru pachet

Pachetele pot face referire la alte pachete, iar modelarea se face folosind unul din
stereotipurile <<import>> (import public) şi <<acces>> (import privat) asociate unei relaţii de
dependenţă (vezi figura 6.2).

Figura 6.2. Exemple de relaţii care se pot stabili între pachete

Ambele tipuri de relaţii permit folosirea elementelor aflate în pachetul destinaţie de către
elementele aflate în pachetul sursă fără a fi necesară calificarea numelor elementelor din pachetul
destinaţie.
Figura 6. 3. Exemplu de diagramă de pachete

În figura 6.3 elementele din Types sunt importate în ShoppingCart şi apoi sunt
importate mai departe de către WebShop. Elementele din Auxiliary pot fi accesate însă doar
din ShoppingCart şi nu pot fi referite folosind nume necalificate din WebShop.
Utilitatea pachetelor. Pachetele împart sistemele mari în subsisteme mai mici şi mai uşor de
gestionat. De asemenea permit dezvoltare paralelă iterativă şi definirea unor interfeţe clare între
pachete promovează refolosirea codului (ex. pachet care oferă funcţii grafice).
7. Diagrame de implementare

Diagrama de componente
Diagrama de componente este o diagramă de implementare care modelează dependenţele
dintre componentele software ale sistemului şi entităţile care le implementează (fişiere cod sursă,
cod binar, executabile, scripturi etc.).
Într-un proiect de dimensiune mare, vor exista multe fişiere care realizează sistemul.
Aceste fişiere depind unele de altele. Natura acestor dependenţe e dată de limbajul (limbajelor)
folosite pentru dezvoltarea proiectului. Dependenţele pot exista în momentul compilării, link-
editării sau rulării. Există de asemenea dependenţe între fişiere sursă şi cele executabile sau
obiect, rezultate din primele prin compilare.
În figura 7.1 este prezentată o diagramă de componente care descrie dependenţele dintre
o sursă scrisă în C++ şi fişierul header asociat, dependenţa fişierului obiect de cele două fişiere
anterioare şi dependenţa fişierului executabil de fişierul obiect. Se pot adăuga şi stereotipuri care
se folosesc pentru a arăta tipurile diferitelor componente.

Figura 7.1. Diagramă de componente care arată dependenţele în C++

O alternativă de reprezentare a unei părţi a diagramei de componente este de a utiliza


notaţia pentru interfaţă în UML pentru a arăta specificaţia unei clase (fişierul header în C++) ca o
interfaţă şi corpul ca o componentă (vezi figura 7.2).

Figura 7.2. Dependenţa unei componente printr-o interfaţă cu o altă componentă


Observaţii.
• Componentele unei diagrame de componente pot fi componente fizice ale sistemului.
• Diagramele de componente sunt utilizate în general pentru a marca dependenţele la scară
mare între componentele unui sistem (vezi figura 7.3).

Figura 7.3. Exemplu de dependenţă la scară mare între componente unui sistem
• Obiectele active care rulează pe fire de execuţie separate pot fi prezentate în diagrama de
componente (vezi figura 7.4)

Figura 7.4. Exemplu de obiect activ în interiorul unei componente

• În timpul analizei şi la începutul proiectării, se folosesc diagramele de pachete pentru a


arăta gruparea logică a diagramelor de clase (sau a modelelor care utilizează alte tipuri de
diagrame) în pachete referitoare la subsisteme.
• În timpul implementării, diagramele de pachete pot fi folosite a arăta gruparea
componentelor fizice în subsisteme.
• Diagrama de componente poate fi combinată cu diagrama de plasare pentru a arăta
localizarea fizică a componentelor sistemului. Clasele dintr-un pachet logic pot fi
distribuite peste locaţiile fizice dintr-un sistem fizic, iar diagramele de componente şi
plasare arată tocmai acest lucru.
Figura 7.5. Exemplu de diagramă de componente
Diagrama de plasare
Diagrama de plasare arată configuraţia procesării elementelor care execută direct
activităţi specifice şi componentele de program, procesele, obiectele care determină funcţionarea
acestor componente.
Componentele care nu au rol direct în execuţie nu sunt arătate în această diagramă.
Diagrama de plasare este alcătuită din:
• noduri;
• Asocieri.
Nodurile arată computerele iar asocierile marchează reţeaua şi protocoalele care sunt
folosite pentru a comunica între noduri (se modelează sistemele client / server din punct de
vedere al topologiei).
Nodurile mai pot fi utilizate pentru a modela şi alte resurse cum ar fi personalul uman şi
resursele mecanice.
Diagramele de plasare modelează arhitectura fizică a sistemului.
Notaţia grafică pentru noduri este prezentată în figura 7.6.

Figura 7.6. Notaţia grafică pentru nodurile unei diagrame de plasare.

Diagramele de plasare pot arăta fie tipuri de maşini fie instanţe particulare ca în figura
7.7.
În figura 7.8 se prezintă locul bazei de date Vanzari (pe server) şi câteva
componente ale calculatoarelor clienţilor.

Figura 7.7. Exemplu de diagramă de plasare

Figura 7.8. Diagramă de plasare cu componente ale PC Client


şi cu un obiect activ pe server
Figura 7.9. Exemplu de diagramă de plasare
OCL - Object Constraint Language

În realizarea unei diagrame de clase cea mai mare parte a timpului şi a efortului este
dedicată aplicării constrângerilor. Spre exemplu, multiplicitatea într-o relaţie de asociere reprezintă
o restricţie referitoare la modul în care mai multe obiecte ale unei clase pot fi legate de fiecare
obiect al altei clase. Acest exemplu particular poate fi exprimat în mod adecvat în limbajul grafic al
diagramei de clase, dar nu toate constrângerile pot fi exprimate astfel. Spre exemplu, multe pre şi
post condiţii dintr-un contract reprezintă constrângeri asupra comportării obiectelor care sunt părţi
ale contractului. Definirea unor astfel de constrângeri poate fi făcută într-o manieră informală, dar
în cazul în care este cerută o precizie ridicată, se foloseşte exprimarea formală folosind OCL.
OCL este un limbaj de exprimare a constrângerilor (restricţiilor) pentru obiecte. A fost
dezvoltat de IBM (divizia de asigurări) şi se utilizează pentru precizarea caracteristicilor modelelor
UML.
OCL este un limbaj bazat pe teoria matematică a mulţimilor şi logica predicatelor. Este un
limbaj tipizat, fiecare expresie având un tip.
OCL este un limbaj declarativ în sensul că expresiile descriu ce anume este de realizat şi nu
cum. Expresiile OCL nu au efecte secundare, ele conservând starea sistemului.
Fiind un limbaj de modelare, OCL nu este executabil. Expresiile OCL se evaluează
instantaneu. Nu există restricţii de implementare, un model UML/OCL putând fi realizat
independent de platformă.
OCL poate fi utilizat pentru:
• Specificarea invarianţilor;
• Specificarea pre-condiţiilor şi post-condiţiilor;
• Specificarea condiţiilor gardă;
• Limbaj de navigare.
Expresiile OCL sunt construite dintr-o colecţie de elemente predefinite şi tipuri, limbajul
având o gramatică precisă care permite construcţia unor declaraţii clare despre proprietăţile
componentelor modelului şi despre relaţiile dintre ele.
Majoritatea construcţiilor OCL constau în următoarele elemente structurale:
• Contextul defineşte un domeniu în interiorul căruia expresia este validă. Acesta este de
obicei o instanţă a unui tip specific, spre exemplu un obiect într-o diagramă de clase. O
legătură (o instanţă a unei asocieri) poate fi de asemenea contextul pentru o expresie
OCL. În cazul în care constrângerea este specificată într-o diagramă UML (de clase sau
de colaborare) utilizând un stereotip şi linia punctată pentru a-l conecta de elementul
contextual, nu mai este necesară declararea explicită a contextului în constrângere.
Declararea contextului este opţională. Într-o diagramă de clase expresiile OCL sunt
precizate sub forma din figura 1. Cuvântul rezervat self este folosit pentru a referi o
instanţă contextuală. Spre exemplu, în cazul în care contextul este Persoana, atunci
self se referă la o instanţă a clasei Persoana.
• Proprietatea acelei instanţe care este context pentru o expresie. Proprietăţile pot include
atribute, capete de asociere, operaţii de interogare.

• Operaţia OCL se aplică proprietăţii. Operaţiile includ (dar nu sunt limitate la) operatorii
aritmetici (*, +, -, /), operatori colecţie cum ar fi size, isEmpty şi select şi operatori
de tip ca oclIsTypeOf.
Figura 1. Modelarea constrângerilor în UML

Declaraţiile OCL pot să includă de asemenea cuvinte cheie OCL cum ar fi operatorii logici
(and, or, implies, if, then, else, not) şi operatorul mulţime in, scrise cu litere îngroşate pentru a-i
distinge de alţi termeni şi operaţii OCL. Cuvintele cheie împreună cu cuvintele menţionate anterior
(care nu sunt cuvinte cheie) pot fi folosite pentru a defini exact pre-condiţiile şi post-condiţiile
pentru o operaţie.
În tabelul următor sunt prezentate câteva expresii OCL. Toate exemplele au drept context un
obiect al unei clase. Se prezintă expresia OCL şi semnificaţia acesteia.

Expresie OCL Interpretare


În contextul unei persoane specifice, valoarea
context persoana
self.gen proprietăţii „gen” a acelei persoane este genul acelei
persoane
context persoana Proprietatea „economii” a persoanei luate în
self.economii >= 500 considerare trebuie să fie mai mare sau egală cu 500.
context persoana În cazul în care colecţia „sot” asociat unei persoane
self.sot -> notEmpty() implies nu este vidă, atunci proprietatea „gen” a soţului
self.sot.gen = masculin trebuie să fie „masculin”
Mărimea colecţiei proprietăţii DT (director tehnic) a
context companie unei companii trebuie să fie mai mică sau egală cu 1.
self.DT -> size() <= 1 Într-adevăr, o companie nu poate avea mai mult de
un director tehnic.
context companie
self.angajat -> select
Mulţimea angajaţilor unei companii cu vârsta mai
(varsta < 60) mică de 60.
Figura 2. Exemplu de diagramă de clase

Conceptul de invariant – expresie booleană care exprimă o condiţie ce trebuie să fie îndeplinită
de toate instanţele tipului pentru care este definită. Invariantul trebuie să aibă valoarea true la
finalul execuţiei constructorului de instanţe precum şi după execuţia operaţiilor.
Spre exemplu, în contextul Companie referitor la figura 2, expresia următoare specifică
faptul că numărul de angajaţi trebuie să fie mai mare decât 50.

self.numarAngajati > 50

unde self e o instanţă a tipului Companie. Acest invariant se păstrează pentru orice instanţă a
tipului Companie.
Tipul instanţei contextuale a unei expresii OCL care este parte a unui invariant se scrie
folosind cuvântul context urmat de numele tipului ca în exemplul de mai jos. Eticheta inv:
declară constrângerea ca fiind de tipul invariant.

context Companie inv:


self.numarAngajati > 50

În unele cazuri se poate renunţa la cuvântul cheie self dacă este clar contextul. Ca
alternativă pentru self se poate defini un nume care joacă rolul cuvântului cheie self, ca în
exemplul următor:

context c:Companie inv:


c.numarAngajati > 50

Acest invariant este echivalent cu cel precedent.


Opţional, numele constrângerii poate fi scris după cuvântul cheie inv:, permiţând
constrângerii să fie referite prin nume. În următorul exemplu, numele constrângerii este
angajatiSuficienti.

context c:Companie inv: angajatiSuficienti


c.numarAngajati > 50

Specificaţiile operaţiei includ frecvent invarianţi. Când un invariant e asociat cu specificarea


unei operaţii, el descrie o condiţie care rămâne adevărată pentru un obiect şi care nu trebuie alterată
de operaţie. Definiţia formală a invarianţilor este importantă deoarece furnizează teste riguroase
pentru execuţia softului.
Exemplu: valoarea Campanie.costEstimat trebuie să fie întotdeauna egală cu suma tuturor
valorilor Reclama.costEstimat asociate, multiplicate cu valoarea cheltuielilor de regie (CR).
În OCL se poate scrie:
Campanie
inv: self.costEstimat=CR*self.Reclama.costEstimat -> sum
În exemplul precedent contextul este clasa Campanie.

Pre-condiţii şi post-condiţii

O expresie OCL poate fi parte a unei pre-condiţii sau post-condiţii, corespunzătoare


stereotipurilor <<pre-condiţie>> respectiv <<post-condiţie>> asociate cu o operaţie sau metodă
(vezi figura 1).
OCL poate specifica multe constrângeri care nu pot fi exprimate direct în diagramă şi de
aceea este necesar un limbaj precis pentru pre-condiţii şi post-condiţii.
Instanţa contextuală self este o instanţă a tipului care este proprietar al operaţiei sau
metodei. Declaraţia contextului în OCL foloseşte cuvântul cheie context, urmat de tipul şi
declaraţia operaţiei. Etichetele pre: şi post: declară constrângerea ca fiind o constrângere pre-
condiţie respectiv post-condiţie.
Sintaxa generală pentru specificarea unei operaţii este:

context type::operation(param1:type, param2:type,...):returnType


pre: param1 operation
param2 operation
post:result = ...

Expresiile pre: sunt funcţii de parametrii operaţiei, în timp ce expresiile post: sunt funcţii de
self, de parametrii operaţiei sau de ambele.
Pre-condiţia este o expresia booleană care trebuie să fie adevărată în momentul începerii
execuţiei unei operaţii.
Post-condiţia este o expresie booleană care trebuie să fie adevărată în momentul terminării
execuţiei unei operaţii.
Result reprezintă valoarea returnată.
Declaraţia explicită context poate lipsi.
Pentru exemplul din figura 2 putem scrie

context Persoana::venit(d:Date):Integer
post: result = 5000

Opţional se poate da un nume pre-condiţiei, respectiv post-condiţiei, care permite referirea


constrângerii prin nume. Numele este precedat de cuvintele cheie pre: respectiv post:. În exemplul
următor, numele pre-condiţiei respectiv al post-condiţiei sunt paramOk respectiv resultOK:

context type::operation(param1:type, param2:type,...):returnType


pre paramOK: param1 operation
...
post resultOK: result = ...

Pentru utilizarea unui invariant în interiorul specificaţiei unei operaţii se poate scrie o clauză
adiţională care începe cu inv:.

ClassName::operation(param1:type, param2:type,...):return type


pre: param1
param2
...
post:result1...
result2...
...
inv: invariant1...
invariant2...
...

Valori precedente în post-condiţii

O caracteristică particulară utilă a OCL-ului este că într-o post-condiţie expresia se poate


referi la două seturi de valori pentru fiecare proprietate a unui obiect:
• valoarea proprietăţii la începutul operaţiei sau metodei;
• valoarea proprietăţii după terminarea operaţiei sau metodei.
Valoarea unei proprietăţi într-o post-condiţie este valoarea după terminarea operaţiei. Pentru
a referi o valoare a proprietăţii la începutul operaţiei se adaugă sufixul @pre numelui proprietăţii.

O utilizare tipică este de a constrânge relaţia între valorile unui atribut înainte şi după ce o
operaţie are loc.

Exemplu:

context Persoana :: aniversare()


post: varsta = varsta@pre+1
Proprietatea varsta se referă la proprietatea unei instanţe o clasei Persoană asupra căreia
se execută operaţia. Proprietatea varsta@pre se referă la valoarea proprietăţii varsta la
începutul execuţiei operaţiei.

Exemplu:
Să considerăm o decizie care defineşte diferitele acţiuni ce depind de schimbările costului
unei campanii de publicitate în comparaţie cu bugetul său. Dacă noul cost estimat este mai mare
decât costul vechi estimat, dar nu depăşeşte bugetul cu mai mult de 2%, valoarea pentru acest
atribut este setată pe true şi se generează o scrisoare pentru client (aceasta se face prin adăugarea
unui atribut Campanie.scrisoareCerutaClient). Aceste restricţii se scriu în OCL sub
forma:
context Campanie inv
post:if costEstimat > costEstimat@pre and
costEstimat > buget and
costEstimat <= buget * 1.02 then
self.scrisoareCerutaClient:Boolean = ’true’
endif

Dacă proprietatea are parametri, sufixul @pre se adaugă înainte de parametri:

context Companie::angajeazăAngajat(p:Persoana)
post: angajat = angajat@pre -> including(p) and
pretActiune() = pretActiune@pre()+10

Operaţia anterioară poate fi specificată folosind atât o post-condiţie cât şi o pre-condiţie:

context Companie::angajeazăAngajat(p:Persoana)
pre: not angajat -> includes(p)
post: angajat -> includes(p) and
pretActiune() = pretActiune@pre()+10

Tipuri şi valori de bază

În OCL există un număr de tipuri de bază predefinite, independente de model. Câteva dintre
acestea împreună cu exemple de valori ce le corespund sunt prezentate în tabelul următor.

Tip Valori
Boolean true, false
Integer 1, -5, 2, 34, ...
Real 1.5, 3.14,...
String ‘A fi sau a nu fi...’

OCL defineşte un număr de operaţii asupra tipurilor predefinite, câteva dintre acestea fiind
prezentate în tabelul următor.

Tip Operaţii
Integer *, + - /, abs()
Real *, + - /, floor()
Boolean And, or, xor, not, implies, if-then-else
String toUpper(), concat()

Tipul enumerare

Enumerarea este un tip de dată în UML şi are un nume (vezi figura 2). O enumerare
defineşte un număr de literali care sunt valori posibile ale enumerării. În cadrul OCL ne putem
referi la o valoare a unei enumerări. Dacă spre exemplu avem enumerarea Sex cu valorile
‘masculin’, ‘feminin’, putem face construcţia

context Persoana inv:


sex = Sex::masculin
Expresii let

Există situaţii în care o sub-expresie este utilizată de mai multe ori într-o constrângere.
Expresia let permite definirea unui atribut sau a unei operaţii care poate fi utilizată în
constrângere.

context Persoana inv:


let venit:Integer = self.job.salariu -> suma()
let areTitlu(t:String):Boolean = self.job -> exists(titlu=t)
if esteSomer then
self.venit < 100
else
self.venit >= 100 and self.areTitlu(‘manager’)
endif

O expresie let poate fi inclusă într-un invariant sau în pre-condiţii şi post-condiţii şi este
cunoscută doar în interiorul respectivei constrângeri. Pentru a putea reutiliza aceste expresii se poate
utiliza o constrângere cu stereotipul <<definition>> în care se definesc expresiile let. Toate
variabilele şi operaţiile definite în constrângerea <<definition>> sunt cunoscute în acelaşi context în
care orice proprietate poate fi utilizată. În esenţă, astfel de variabile şi operaţii sunt pseudo-atribute
şi pseudo-operaţii şi se utilizează în OCL în aceeaşi manieră ca şi atributele sau operaţiile. Notaţia
pentru constrângerea <<definition>> utilizează cuvântul cheie def ca în exemplul următor:

context Persoana def:


let venit:Integer = self.job.salariu -> suma()
let areTitlu(t:String):Boolean = self.job -> exists(titlu=t)

Cuvinte predefinite în OCL

Lista cuvintelor predefinite este prezentată în tabelul următor

Obiecte şi proprietăţi

Proprietăţi

Valoarea unei proprietăţi a unui obiect care este definit într-o diagramă de clase este
specificată printr-un „punct” urmat de numele proprietăţii:
context AType inv:
self.proprietate

Dacă self este o referinţă la un obiect, atunci self.proprietate este valoarea


proprietăţii proprietate a lui self.

Proprietăţi: Atribute

Spre exemplu, vârsta unei persoane se scrie self.varsta:

context Persoana inv:


self.varsta > 0

Valoarea sub-expresiei self.varsta este valoarea atributului varsta pentru o instanţă


particulară a clasei Persoana identificată prin self. Tipul acestei sub-expresii este tipul
atributului varsta, care este de tip Integer.

Proprietăţi: Operaţii

Operaţiile pot avea parametri. Spre exemplu, un obiect Persoană are un venit exprimat ca
funcţie de o dată (vezi figura 2). Această operaţie poate fi accesată în felul următor, pentru o
persoană aPersoana şi o dată aDate:

aPersoana.venit(aDate)

Operaţia însăşi poate fi definită printr-o constrângere de tip post-condiţie sub forma:

context Persoana::venit (d:Date) : Integer inv:


post: result = varsta * 1000

Tipul lui result este tipul returnat de operaţie, care este Integer în exemplul anterior.

Pentru a ne referi la o operaţie sau metodă care nu are parametri, se utilizezaă parantezele
rotunde fără nici un argument:

context Companie inv:


self.pretActiune() > 0

Proprietăţi: Capete de asociere şi Navigare

Pornind de la un anumit obiect putem naviga pe o asociere într-o diagramă de clase pentru a
ne referi la alte obiecte şi la proprietăţile acestora. În acest scop se utilizează extremitatea opusă a
asocierii:

obiect.numeRol

Valoarea acestei expresii este un set de obiecte situat la extremitatea cealaltă a asocierii.
Dacă multiplicitatea unui capăt de asociere este maxim 1 („0..1” sau 1) atunci valoarea acestei
expresii este un obiect. Referitor la diagrama de clase din figura 2, dacă suntem în contextul
Companie (deci self este instanţă a clasei Companie), putem scrie:
context Companie
inv: self.manager.esteSomer = false
inv: self.angajat -> notEmpty()

În primul invariant self.manager este o persoană, deoarece multiplicitatea asocierii este


1. În al doilea invariant self.angajat va fi evaluat ca mulţime de persoane.

Observaţie. Colecţiile, cum ar fi mulţimi, secvenţe, sunt tipuri predefinite în OCL. Există şi un set
de operaţii predefinite ce se pot efectua asupra lor. O proprietate a colecţiei este accesată folosind
simbolul „->” urmat de numele proprietăţii.

Exemple

context Persoana inv:


self.angajator -> size() < 0

Proprietatea size se aplică asupra mulţimii self.angajator formată din numărul


angajatorilor Persoanei self.

context Persoana inv:


self.angajator -> isEmpty()

Proprietatea isEmpty se aplică asupra mulţimii self.angajator. Aceasta se evaluează


ca fiind true dacă mulţimea angajaţilor este vidă şi false în caz contrar.

Deoarece multiplicitatea unui rol manager este 1, self.manager este un obiect de tip
Persoana. Un astfel de obiect singur poate fi utilizat ca Mulţime, presupunând de fapt că mulţimea
are un singur element. Utilizarea ca mulţime este precizată prin utilizarea simbolului „->” urmat de
numele proprietăţii mulţimii.
Exemple

context Companie inv:


self.manager -> size() = 1

Sub-expresia self.manager este utilizată ca Mulţime deoarece se foloseşte săgeata


pentru a accesa proprietatea size. Expresia precedentă este evaluată ca true.

context Companie inv:


self.manager.varsta > 40

Sub-expresia self.manager este utilizată ca o Persoana deoarece se foloseşte simbolul


„.” Pentru accesarea proprietatea varsta a Persoanei.

În cazul unei asocieri opţionale (multiplicitate 0..1) este util de a verifica dacă există un
obiect sau nu când se navighează spre asociere.
Exemplu
context Persoana inv:
self.sotie -> notEmpty() implies self.sotie.sex = Sex::feminin

Proprietăţile pot fi combinate pentru a scrie expresii mai complicate. O regulă importantă
este că o expresie OCL se evaluează întotdeauna pe un obiect specific cu un tip specific. După
obţinerea rezultatului se poate aplica întotdeauna o altă proprietate rezultatului pentru a obţine o altă
valoare a rezultatului. Din acest motiv, fiecare expresie OCL poate fi citită şi evaluată de la stânga
la dreapta.

Exemplu:

[1] Persoanele căsătorite au vârsta mai mare sau egală cu 18

context Persoana inv:


self.sotie -> notEmpty() implies self.sotie.varsta >= 18 and
self.sot -> notEmpty() implies self.sot.varsta >= 18

[2] o companie are cel mult 50 de angajaţi

context Companie inv:


self.manager -> size() <= 50

În momentul în care proprietăţile se redefinesc în interiorul unui tip, proprietatea supra-


tipului poate fi accesată utilizând operaţia oclAsType().Spre exemplu, dacă o clasă B este
specializare a clasei A şi proprietatea p1 este a ambelor clase A şi B, se poate scrie:

context B inv:
self.oclAsType(A).p1 -- se accesează proprietatea p1 definită în A
self.p1 -- se accesează proprietatea p1 definită în B

Operaţii asupra tipurilor predefinite colecţie

Tipurile colecţie definite în OCL sunt Set, Sequence, şi Bag. Tipul Set reprezintă o
mulţime de valori în sens matematic (nu conţine duplicate). Tipul Bag este asemănător tipului Set,
dar poate conţine duplicate. Tipul Sequence este asemănător tipului bag, elementele fiind însă
ordonate.
OCL are construcţii speciale pentru specificarea selecţiei dintr-o colecţie: select şi
reject. Sintaxa este:

colecţie -> select(...)

Parametrii selecţiei au o sintaxă specială care permite specificarea elementelor din colecţie
ce trebuie selectate. Un exemplu de astfel de parametri este următorul:

colecţie -> select(expresie_booleană)

Următoarea expresie OCL specifică faptul că mulţimea angajaţilor de peste 50 de ani nu este
vidă:

context Companie inv:


self.angajat -> select (varsta > 50) -> notEmpty()

Contextul expresiei din argumentul construcţiei select este elementul colecţiei pentru
care select este invocată. Astfel, proprietatea varsta e în contextul Persoana.
În exemplul precedent, este imposibilă referirea explicită la persoanele însele., referirea
făcându-se doar la proprietăţile acestora.
Pentru a evita acest inconvenient se foloseşte sintaxa:

colecţie -> select(v | expresie_booleană_în_care_apare_v)

Variabila v se numeşte iterator. Următoarele exemple sunt echivalente:

context Companie inv:


self.angajat -> select (varsta > 50) -> notEmpty()

context Companie inv:


self.angajat -> select (p | p.varsta > 50) -> notEmpty()

O altă extensie a sintaxei anterioare este aceea în care se dă şi tipul variabilei v.

colecţie -> select(v:Type | expresie_booleană_în_care_apare_v)

Exemplul următor este echivalent cu anteriorul:

context Companie inv:


self.angajat -> select (p:Persoana | p.varsta > 50) -> notEmpty()

Operaţiunea reject este identică operaţiunii select, dar în acest caz se obţin elementele
colecţiei pentru care expresia evaluată este false. Sintaxa este asemănătoare operaţiunii
select:

colecţie -> select(expresie_booleană)


colecţie -> select(v | expresie_booleană_în_care_apare_v)
colecţie -> select(v:Type | expresie_booleană_în_care_apare_v)

Spre exemplu, pentru a specifica faptul că mulţimea angajaţilor care nu sunt căsătoriţi este
vidă putem scrie:
context Companie inv:
self.angajat -> reject (esteCasatorit) -> isEmpty()
PROIECTAREA SISTEMELOR SOFTWARE

1. Introducere

Proiectarea sistemelor software poate fi privită ca un proces creativ de transformare a


unei probleme într-o soluţie; descrierea soluţiei face parte de asemenea din procesul de
proiectare. Proiectarea presupune o experienţă practică, acumulată în timp şi 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.

Pentru a transforma cerinţele într-un sistem funcţional, proiectanţii trebuie să ţină cont
atât de clienţi cât şi de programatori. Clienţii înţeleg ce face sistemul, în timp ce programatorii
trebuie să înţeleagă cum funcţionează sistemul.
Din acest motiv, proiectarea are două părţi. Prima, proiectarea conceptuală, arată
clientului exact ce va face sistemul. Odată ce clientul este de acord, proiectarea conceptuală se
translează într-un document mai detaliat, proiectarea tehnică, care permite programatorilor să
înţeleagă nevoile hard şi soft care pot rezolva problema clientului.

Activităţile esenţiale în cursul proiectării sunt următoarele:

• Proiectarea arhitecturală. Sub-sistemele întregului sistem sunt identificate şi


documentate.
• Specificarea abstractă: Pentru fiecare sub-sistem, se prezintă o specificare abstractă
a serviciilor şi a constrângerilor sub care acestea operează.
• Proiectarea interfeţelor. Pentru fiecare sub-sistem, interfaţa cu celelalte sub-sisteme
este proiectată şi documentată.
• Proiectarea componentelor. Serviciile furnizate de un sub-sistem sunt partiţionate
între componentele acelui sub-sistem.
• Proiectarea structurilor de date. Structurile de date utilizate în implementarea
sistemului sunt proiectate în detaliu şi specificate.
• Proiectarea algoritmilor. Algoritmii utilizaţi pentru a furniza servicii sunt proiectaţi
în detaliu şi specificaţi.

Acest proces se repetă pentru fiecare sub-sistem până când componentele identificate pot
fi mapate direct în componentele limbajului de programare

Caracteristicile unei proiectări corecte

Ca rezultat al fazei de proiectare se obţine:


• Un model al codului care arată cum este implementat sistemul
• diagramă a dependenţelor dintre module, care arată cum va fi divizat în module
sistemul şi cum interacţionează acestea
• Pentru module mai dificile, se includ specificaţii speciale
Se pot defini nişte proprietăţi cheie pentru măsurarea calităţii proiectării şi anume:
¾ Extensibilitatea
• Proiectarea trebuie să poată suporta noi funcţii
• Modelul problemei trebuie să reflecte caracteristicile generale ale problemei
• Proiectarea trebuie să fie localizată; modulele trebuie decuplate cât mai mult
¾ 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
• Pentru sistemele distribuite, este importantă disponibilitatea
• Pentru sisteme de timp real, este importantă sincronizarea
• Siguranţa nu poate fi introdusă uşor într-un sistem existent; cheia realizării de
produse sigure este modelarea şi dezvoltarea atentă pe parcursul întregului ciclu
de viaţă al produsului
¾ Eficienţa
• Sistemul trebuie să consume resurse în limite rezonabile
• O proiectare mai economică poate fi preferabilă uneia care îndeplineşte toate
metricile de calitate dar este mai scumpă
• Puncte cheie
- Modelarea obiectelor (greu de modificat)
- Evitarea tendenţiozităţii (prin detalii de implementare)
- Optimizarea (nu trebuie făcută scăzând claritatea structurii)

Metode de proiectare

1. Proiectarea structurată

• metodele Yourdon, caracterizate de utilizarea:


- Hărţilor Structurilor, pentru a evidenţia ierarhia, controlul şi fluxurile de date;
- pseudo-codului pentru descrierea procesului.
Există şi o versiune a lui DeMarco pentru proiectarea structurată.
Metode mai recente de proiectare structurată sunt cele ale lui Ward şi Mellor, în care
‘modelul esenţial’, obţinut în faza analizei, este transformat într-un ‘model de implementare’.
Acesta este proiectat într-o manieră top-down prin alocarea funcţiilor către procesoare, taskuri şi
module.
• SADT (Structured Analysis and Design Technique) - utilizează aceleaşi tehnici ale
diagramelor atât pentru analiză cât şi pentru proiectare. Se poate utiliza pentru modelarea
sistemelor de timp real, deoarece permite reprezentarea fluxurilor de control a datelor.

• SSADM (System Structured Analysis and Design Methodology) - SSADM rezultă într-un
model al proceselor şi al datelor. Modelele sunt construite în paralel şi fiecare este utilizat
pentru verificarea consistenţei celorlalte, ceea ce constituie avantajul major al acestei tehnici.
Nu suportă proiectarea sistemelor de timp real.

2. Proiectarea orientată obiect (OOD)

OOD este o strategie de proiectare bazată pe obiecte şi clase. Metodele orientate obiect
oferă un suport mai bun de reutilizare decât celelalte metode. Mecanismul moştenirii permite
refolosirea top-down a atributelor şi operaţiilor super-claselor.
Alte caracteristici ale strategiei orientată obiect sunt:
• Comunicarea prin mesaje. Obiectele trimit mesaje spre alte obiecte pentru
executarea unor operaţii sau pentru transmiterea unor informaţii.
• Polimorfismul, reprezentând capabilitatea , în momentul execuţiei, de a referi
instanţe ale diferitelor clase. El este implementat prin acceptarea “dynamic binding”,
bazându-se pe alocarea dinamicã a memoriei.
Utilizarea proiectării orientate-obiect este avantajoasă doar atunci când implementarea se
face într-un limbaj de programare care suportă definirea obiectelor, moştenirea, comunicarea
prin mesaje şi polimorfismul (Smalltalk, C++, ObjectPascal).
Tehnicile orientate obiect sunt mult mai potrivite pentru construirea sistemelor orientate-
eveniment, cum sunt interfeţele GUI (Graphical User Interface), decât tehnicile structurate.
Dacă se ia decizia de a se utiliza tehnica OO pentru dezvoltarea unui sistem, aceasta
trebuie utilizată de-a lungul întregului ciclu de viaţă al sistemului. Booch a descris o tehnică de
transformare a unui model logic construit folosind analiza structurată într-un model fizic
utilizând proiectarea OO. In practică, rezultatele nu au fost satisfăcătoare. Analiza structurată se
bazează pe funcţii şi date iar perspectiva OO este bazată pe clase, obiecte, atribute şi servicii.
Perspectivele sunt diferite şi e dificil de a le avea pe amândouă simultan în minte.
Ca şi analiza structurată, OOD este numele unei clase de metode OO:
• Booch - continuă să reprezinte o metodă de referinţă în procesele de dezvoltare a sistemelor.
Booch modelează proiectul în termenii unei perspective logice (clase, obiecte şi relaţiile între
ele) şi o perspectivă fizică (arhitectura modulelor şi proceselor).
Există 2 tipuri de diagrame:
• diagrama modulelor pentru evidenţierea alocării claselor şi obiectelor în module
(programe sau taskuri).
• diagrama proceselor care prezintă alocarea modulelor procesoarelor hardware.

• HOOD (Hierarchical Object-Oriented Design) - este o metodă care încearcă reunirea


metodei OO cu metodele structurate. Ierarhia decurge natural din descompunerea obiectului
rădăcină. Ca şi în proiectarea structurată, fluxurile de date se reprezintă între componentele
software. Principala diferenţă între HOOD şi metodele structurate este că identitatea
componentelor software îşi au corespondent mai mult în lumea reală decât în cea a funcţiilor
pe care sistemul trebuie să le îndeplinească. HOOD nu are o metodă complementară de
analiză. Modelul logic al sistemului este construit prin metodele analizei structurate.

• Coad şi Yourdon. Un proiect OO este construit din 4 componente:


1. componenta domeniului problemei - se bazează pe modelul logic construit în timpul
OOA. Dacă sistemul va fi implementat într-un limbaj de programare OO, atunci va
exista o corespondenţă de 1 la 1 între clasele şi obiectele domeniului problemei.
2. componenta interacţiunii cu factorul uman - coordonează mesajele dinspre si spre
utilizator.
3. componenta coordonării taskurilor - se refera la multiplele fire de execuţie, existente
intr-un sistem.
4. componenta coordonării datelor - furnizează infrastructura pentru înregistrarea si
regăsirea datelor.
Fiecare componentă este construită din clase şi obiecte.
Clasele si obiectele sunt organizate în structuri generalizare-specializare şi întreg-parte.
Structurile generalizare-specializare sunt “familii de arbori”, descendenţii moştenind
atributele şi serviciile părinţilor.
Structurile întreg-parte sunt formate prin descompunerea unui obiect.

• OMT (Object Modelling Technique) - introdusă de Rumbaugh şi conţine 2 activităţi de


proiectare:
- proiectarea sistemului
- proiectarea obiectelor

• Shlaer-Mellor. Shlaer-Mellor descriu un limbaj pentru proiectarea OO (OODLE), derivat


din notaţiile Booch şi Buhr, punând în evidenţă 4 tipuri de diagrame:
- diagrama claselor (definind atributele şi operaţiile clasei)
- clasa hărţilor structurii (definind structura modulelor claselor)
- diagrama dependinţelor între clase (tip client-server sau friend)
- diagrama moştenirilor (prezentând relaţiile de moştenire între clase)

3. Metode Formale - se aplică în faza proiectării doar dacă s-au aplicat în faza analizei.
Ele se recomandă pentru sistemele cu cerinţe critice din punct de vedere al siguranţei şi
securităţii, furnizând specificaţii riguroase ale sistemului software.

Utilitare pentru proiectare

In cazul tuturor proiectelor, cu excepţia celor mici, se recomandă folosirea utilitarelor


CASE.

Documentul fazei de proiectare

Documentul fazei de proiectare este documentul cu care se finalizează această fază. El


trebuie realizat de către dezvoltatorii de sisteme soft, cu aptitudini pentru proiectare şi
implementare. Documentul trebuie să fie suficient de detaliat astfel încât coordonatorul
proiectului să poată detalia un plan de implementare şi să poată urmări proiectul, în ansamblu,
de-a lungul procesului de dezvoltare.
2. Proiectarea orientată – obiect (OOD). Metoda Coad-Yourdon

Modelul de proiectare OO Coad-Yourdon constă în patru componente:


1. componenta domeniului problemei (CDP)
2. componenta interacţiunii cu factorul uman (CIFU)
3. componenta coordonării taskurilor (CCT)
4. componenta coordonării datelor (CCD)
Aceste componente corespund următoarelor patru activităţi:
1. proiectarea componentei domeniului problemei
2. proiectarea componentei interacţiunii cu factorul uman
3. proiectarea componentei coordonării taskurilor
4. proiectarea componentei coordonării datelor
Există mai multe strategii care se pot aplica pentru realizarea proiectării orientate-obiect:

1. Se aplică OOA (analiza orientată obiect)

OOA este organizată în 5 straturi, OOD este organizată în 5 componente. Cum se vor
aplica OOA şi OOD? Utilizând Waterfall, modelul spirală sau incremental ?
Prin Modelul Waterfall, un proiect include analiza, proiectarea, implementarea, cu o
secvenţă strictă a acestora, deşi întoarceri în fazele anterioare sunt permise în timpul etapelor
timpurii, în procesul de dezvoltare.
Prin Modelul spirală, un proiect include analiza, proiectarea, implementarea, cu
specificarea , prototipizarea şi analiza riscurilor fiecărei faze.
Modelul incremental aplică OOA, OOD şi OOP într-un număr mai mic de paşi. Pentru
fiecare increment, un profesionist poate utiliza toate cele trei activităţi la momente diferite de
timp.
OOA şi OOD (metoda Coad-Yourdon) pot fi utilizate cu oricare din cele trei modele,
menţionate mai sus.

2. Se utilizează rezultatele OOA, îmbunătăţindu-le în timpul OOD

Rezultatele OOA se preiau direct în componenta domeniului problemei cu eventuale


modificări datorate schimbărilor cerinţelor utilizatorului sau neînţelegerii problemelor de către
analist, etc.

3. Se utilizează rezultatele OOA şi se adaugă în timpul OOD

Criteriile de adăugare în CDP sunt:


• reutilizarea claselor proiectate şi programate;
• gruparea claselor specifice domeniului problemei;
• stabilirea unui protocol prin adăugarea unei clase de generalizare;
• acomodarea nivelului de moştenire;
• îmbunătăţirea performanţei;
• suportarea componentei coordonării datelor;
• revizuirea adăugărilor rezultatelor OOA.
Ne vom referi în continuare la această din urmă strategie.
2.1 Proiectarea componentei domeniului problemei

2.1.1 Reutilizarea claselor proiectate şi programate

Se recomandă reutilizarea claselor deja proiectate sau programate. Ele se introduc în


componenta domeniului problemei, marcându-se Atributele şi Serviciile ce nu vor fi utilizate şi
introducându-se o structură Gen-Spec de la această clasă către o clasă din domeniul problemei.
În această clasă-specializare se vor identifica Atributele şi Serviciile care se moştenesc acum din
clasa adăugată. Se revizuiesc apoi structurile şi conexiunile dintre clase, mutându-le spre clasa
nou adăugată (mediile CASE ar trebui să ţină cont de aceste modificări).

2.1.2 Gruparea claselor specifice domeniului problemei.

Aceasta este o modalitate de a grupa anumite clase specifice domeniului problemei într-o
bibliotecă de clase, când un model mai sofisticat de grupare nu este disponibil. Acest mecanism
constă în introducerea unei noi clase ca vârf al unei ierarhii conţinând clasele ce se doresc
grupate împreună.

2.1.3 Stabilirea unui protocol prin adăugarea unei clase de generalizare

La un moment dat, e posibil ca un număr de clase specializate să necesite un protocol


similar (trebuie deci să definească un set similar de Servicii şi de Atribute). În acest caz, poate fi
introdusă o nouă clasă pentru a stabili acest protocol pentru definirea setului comun de Servicii,
care vor fi definite în detaliu în clasele specializate.

2.1.4 Acomodarea nivelului de moştenire

Dacă Structurile Gen-Spec ale modelului OOA includ multiple moşteniri, trebuie făcute
câteva modificări ale acestora atunci când se va utiliza un limbaj de programare OO care nu
suportă mecanismul moştenirii sau care nu suportă decât moştenirea simplă.
Tipuri de moştenire multiplă:
1. Diamantul mic
2. Diamantul mare

Acomodarea cu limbajele care suporta doar simpla moştenire

În acest caz se pot aplica 2 metode de a transforma o moştenire multiplă într-o moştenire
simplă:
1) formarea unor ierarhii separate, mapate între ele prin structuri Întreg-Parte sau prin
Conexiunea Instanţelor
Exemplu:
Alternativ, aceasta poate fi modelată prin Conexiunea Instanţelor:
Exemplu:

2) Transformarea multiplei ierarhii intr-o ierarhie cu simplă moştenire - caz în care anumite
Atribute şi Servicii vor fi repetate în clasele specializate.

Acomodarea cu limbajele care nu suporta mecanismul de moştenire


Fiecare structură Gen-Spec se va descompune în clasele-obiecte componente:

2.1.5 Îmbunătăţirea performanţei

Creşterea vitezei - e necesară când între Obiecte există in 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.

2.1.6 Suportarea componentei coordonării datelor

Pentru suportarea componentei de coordonare a datelor, fiecare Obiect care trebuie


memorat trebuie să ştie cum să se auto-memoreze:
• fie i se adaugă un Atribut şi un Serviciu în acest scop
• fie acest Obiect trimite un mesaj către componenta coordonării datelor cerându-i
memorarea sa
2.2 Proiectarea Componentei de Interacţiune cu Factorul Uman (interfeţei
utilizator)

Interfaţa utilizator necesită o examinare detaliată atât în faza analizei cât şi a proiectării.
În OOD, Interfaţa utilizator referă la proiectarea formatelor ferestrelor şi rapoartelor.
Prototipizarea este utilizată pentru a ajuta la selecţia şi dezvoltarea mecanismelor de interacţiune.
Unele companii consideră important de a proiecta porţiuni ale interfeţei utilizator paralel cu faza
analizei Aplicarea unei strategii sistematice suportate de prototipizare este vitală în acest
domeniu.
Această componentă se referă la modul în care factorul uman va comanda sistemul şi
modul în care sistemul va furniza informaţii utilizatorului.

2.2.1 Clasificarea utilizatorilor


Se recomandă a se apela şi la un specialist în studiul interacţiunilor umane şi în testarea
acestora. Se vor studia viitorii utilizatori ai sistemului, urmărindu-i cum îşi desfăşoară
activitatea.
Se urmăreşte:
• ce probleme anume vor utilizatorii să rezolve;
• ce utilitare li se pot furniza pentru a-i ajuta;
• cum pot fi ele realizate fără a-i obstrucţiona în activitatea lor.
Utilizatorii pot fi clasificaţi după următoarele criterii:
• nivelul de îndemânare: începător, ocazional, intermediar, avansat;
• nivelul organizaţional: executiv, conducere, supervizor, funcţionar;
• după apartenenţa la diferite grupuri: conducere, client.

2.2.2 Descrierea utilizatorilor


Pentru fiecare categorie de utilizatori din pasul anterior, se consideră următoarele:
• profesiunea
• scopul
• caracteristici (vârsta, educaţie, restricţii)
• factori critici de succes (ce îi place, ce îi displace)
• nivelul de îndemânare
• scenariul de lucru

2.2.3 Proiectarea ierarhiei comenzilor


Pentru aceasta, se recomandă:
• dacă ierarhia comenzilor trebuie să se integreze într-un sistem de interacţiuni deja
existent, acesta trebuie mai întâi studiat.
• stabilirea unei ierarhii iniţiale de comenzi care poate fi prezentată utilizatorilor în mai
multe moduri:
- o serie de ecrane meniu
- o bară meniu
- o serie de imagini (icons)
Se poate începe cu următoarea ierarhie de bază a comenzilor (Serviciilor):
File Edit Format Calculate Monitor Window
• rafinarea ierarhiei comenzilor prin:
- ordonarea serviciilor din fiecare ramură a ierarhiei:
1) cele mai frecvente Servicii să apară primele în listă,
2) în ordinea logică în care trebuie să se execute.
- lăţimea şi adâncimea ierarhiei: evitarea supraîncărcării memoriei pe termen
scurt a factorului uman.
- minimizarea numărului de paşi, de acţiuni (apăsări ale butonului mouse,
combinaţii de chei) pe care trebuie să le efectueze utilizatorul pentru a-şi îndeplini
sarcinile.

2.2.4 Proiectarea detaliată a interacţiunilor


Interacţiunile cu factorul uman pot fi proiectate pe baza următoarelor criterii:
• consistenţa: se recomandă utilizarea unor termeni consistenţi şi acţiuni consistente.
• număr mic de paşi - trebuie să se minimizeze numărul de acţiuni pe care trebuie să le
îndeplinească utilizatorul
• evitarea “aerului mort”- “dead air”; aer mort este un termen semnificând faptul că
utilizatorul nu trebuie lăsat singur, fără nici un semnal, atunci când trebuie să aştepte
ca sistemul să execute o acţiune. Utilizatorului trebuie să i se semnaleze:
- faptul că sistemul execută o acţiune;
- cât din acţiunea respectivă s-a realizat.
• undo: se recomandă a se furniza acest serviciu sistemului, datorită erorilor
utilizatorului.
• timpul şi efortul de învăţare trebuie să fie scurt; în general, utilizatorii nu vor citi
documentaţia. Se recomandă a se furniza referinţe on-line.
• prezentarea interfeţei - factorul uman utilizează un software care este plăcut şi
amuzant.

2.2.5 Prototipizarea
Un bun punct de start este de a se folosi ca model un software deja existent cu o interfaţă
bine realizată. Dacă exemplul ales face parte din domeniul problemei, este cu atât mai bine. Se
consideră meniurile, submeniurile şi prescurtările deja existente. Se folosesc utilitare pentru
prototipizări vizuale sau generatoare de aplicaţii. Se recomandă realizarea mai multor prototipuri
care vor fi puse la dispoziţia utilizatorilor, urmărindu-le reacţiile în timp ce le folosesc.

2.2.6 Proiectarea claselor pentru Interfaţa Utilizator


Pentru a proiecta clasele pentru interfaţa utilizator se începe prin a organiza interacţiunile
cu factorul uman în ferestre şi componente:
Fiecare clasă conţine definiţia pentru: menu-bar, pull-down menu, şi pop-up menu
pentru o fereastră. Fiecare clasă defineşte Serviciile necesare pentru a crea meniurile, pentru a
evidenţia un element selectat şi pentru a invoca răspunsul corespunzător. Fiecare clasă este
răspunzătoare pentru prezentarea informaţiei în interiorul ei şi încapsulează toate informaţiile
pentru dialog.

2.3 Proiectarea Componentei de Coordonare a Taskurilor

Taskul este un alt nume pentru proces. Execuţia concurentă a mai multor taskuri se
numeşte multi-tasking.
Taskurile multiple sunt necesare în unele cazuri:
• pentru sistemele pentru achiziţie de date;
• pentru anumite interfeţe utilizator - acele care au multiple ferestre selectate pentru
intrare;
• pentru sistemele multi-user;
• pentru arhitecturi multi-subsistem;
• pentru cazul mai multor taskuri şi un singur procesor; un task trebuie să coordoneze
şi să comunice cu alte taskuri în timpul execuţiei; asemenea taskuri se execută prin
partajarea timpului procesor, creând iluzia că se execută în paralel;
• pentru arhitecturile multi-procesor.
Taskurile separă acţiunile care trebuie să aibă loc în paralel. Această comportare
concurentă poate fi implementată pe procesoare separate sau poate fi simulată pe un singur
procesor în conjuncţie cu un sistem de operare multi-tasking. O alternativă este de a considera un
program secvenţial ciclic. După executarea fiecărei părţi de program el verifică ce s-a întâmplat
cât timp a fost ocupat răspunzând corespunzător.
O abordare neeficientă ar fi de a intercala comportări concurente într-un singur program
secvenţial, rezultând un program foarte mare şi necesitând teste la fiecare câteva linii de cod,
teste care să verifice intrarea datelor sau diverse cereri. Utilizarea taskurilor va simplifica
proiectarea şi implementarea acţiunilor concurente.

Scopul acestei strategii este de a identifica şi de a proiecta taskurile şi Serviciile incluse


în fiecare task:
1. identificarea taskurilor determinate de evenimente (taskuri responsabile pentru comunicarea
cu un dispozitiv, una sau mai multe ferestre pe ecran, un alt task, sub-sistem sau procesor.
Taskul poate fi proiectat pentru a se declanşa la un anumit eveniment, deseori semnalând
apariţia unor date).
2. identificarea taskurilor determinate de ceas (aceste taskuri se declanşează la anumite
intervale de timp).
3. identificarea taskurilor prioritare şi critice. E posibil ca unele Servicii să fie de prioritate
maximă. Acestea trebuie izolate într-un task separat de prioritate mare. Alte Servicii sunt de
mică prioritate, iar altele sunt critice.
4. identificarea unui task coordonator - acest task coordonează executarea celorlalte taskuri
5. definirea fiecărui task:
• numele taskului şi o scurtă descriere
• adaugă fiecărui Serviciu identificat un nume de task. Fiecare Serviciu este mapat
unui task.
• specifică dacă taskul este coordonat de eveniment (şi indică evenimentul respectiv)
sau ceas (indică intervalul de timp la care se declanşează)
• specifică modul de comunicare (de unde îşi ia intrarea şi unde trimite rezultatele)
Paragrafele anterioare pot fi rezumate prin următoarea schemă privind componenta de
coordonare a taskurilor:

2.4 Proiectarea Componentei de Coordonare (Gestiune) a Datelor

Componenta de coordonare a datelor furnizează infrastructura pentru depozitarea şi


regăsirea obiectelor dintr-un sistem de coordonare a datelor.
Există trei abordări majore pentru coordonarea datelor:
• coordonarea datelor utilizând fişiere
• sistem de coordonare a datelor prin baze de date relaţionale.
Din categoria sistemelor de gestiune a bazelor de date relaţionale enumerăm:
dBASE, FOXBASE, FOXPRO, ORACLE, INGRES, INFORMIX, DB2, CAMPUS,
ACCESS. Un astfel de sistem coordonează datele printr-un număr de tabele, fiecare
având un nume. Fiecare coloană are un nume şi conţine o singură valoare (atomică).
Fiecare rând reprezintă un set de valori în tabel. Rândurile sunt unic identificabile.
Una sau mai multe coloane pot fi definite drept chei primare-unicul identificator
pentru fiecare rând din tabel. Una sau mai multe coloane pot fi definite drept chei
externe (străine) pentru a facilita accesul la rândurile corespunzătoare din alt tabel.
Tabelele şi coloanele pot fi reorganizate pentru a reduce redundanţa datelor şi deci
numărul de paşi pentru modificarea consistentă a datelor. Această reorganizare poartă
numele de normalizare. Gradul de eliminare a redundanţei datelor e definit ca “forme
normale”.
• sistem de coordonare a datelor prin baze de date orientate obiect
Sistemele de gestiune a bazelor de date orientate-obiect reprezintă o tehnologie
încă în curs de implementare. Primele produse comerciale au apărut în 1986. Există 2
mari abordări:
- produsele relaţionale extinse
- produsele limbajelor de programare extinse orientate-obiect
Produsele relaţionale extinse extind sistemele de gestiune a bazelor de date
relaţionale, adăugând tipuri de date abstracte, mecanismul de moştenire şi câteva
Servicii pentru crearea şi manipularea Claselor şi Obiectelor.
Produsele limbajelor de programare extinse orientate-obiect extind un limbaj de
programare orientat-obiect cu sintaxă şi capabilităţi de gestiune a Obiectelor într-o
bază de date.

Proiectarea componentei de coordonare a datelor constă în proiectarea machetelor de date


şi a Serviciilor corespunzătoare.
Proiectarea machetelor datelor se realizează funcţie de abordarea aleasă, din cele trei
expuse mai sus, pentru coordonarea datelor.
Definirea Serviciilor corespunzătoare constă în adăugarea unui Atribut şi Serviciu
fiecărei Clase&Obiect căreia îi corespund Obiecte ce trebuie memorate. În acest fel un Obiect
trebuie să ştie singur cum să se înregistreze. Un Obiect trebuie să ştie ce fişier (tabel) trebuie să
deschidă, cum să poziţioneze fişierul pe înregistrarea corectă, cum să regăsească vechi valori, şi
cum să se actualizeze. Se defineşte o Clasă&Obiect, ObiectServer, cu Servicii pentru:
1) a semnala fiecărui Obiect să se salveze (în fişier);
2) a regăsi Obiecte înregistrate (căutări, creări şi iniţializări de Obiecte).
Modele de proiectare

Modelele (şabloanele) de proiectare se utilizează în toate domeniile care implică o


activitate de proiectare. În domeniul sistemelor software orientate-obiect soluţiile sunt exprimate
în termeni de obiecte şi interfeţe. Esenţa noţiunii de şablon este aceeaşi: un şablon (pattern)
reprezintă o soluţie comună a unei probleme într-un anumit context.
Modelele de proiectare utilizate în sistemele orientate-obiect se pot clasifica în:
• idiomuri – legate de anumite limbaje de programare şi care se referă la practici sau
obiceiuri „bune”, care se indică a se utiliza când utilizăm limbajul
respectiv.
• mecanisme – structuri în cadrul căreia obiectele colaborează în vederea obţinerii
unui anumit comportament care satisface o anumită cerinţă a
problemei.
- decizii de proiectare privind modul în care operează colecţiile de
obiecte.
- se mai numesc şabloane de proiectare (design patterns)
• cadre (frameworks) – descriu şi menţin cu ajutorul unui set de clase abstracte relaţii
între obiecte. De exemplu, un editor grafic este specializat
pentru diferite domenii: editor muzical, editor CAD etc.
Scheletul unei aplicaţii „editor grafic” poate fi gândit fără să se
ţină cont de domeniul particular în care se va folosi. Indiferent
de tipul editorului, se poate „inventa” un set de clase abstracte,
relaţii între ele şi cod generic ce va fi reutilizat în fiecare caz
concret.

Un proiectant experimentat ştie că nu trebuie să rezolve fiecare problemă începând de la


zero, ci reutilizând soluţii (bune) din proiecte anterioare. Atunci când descoperă o soluţie bună o
va folosi mereu.
Pentru descrierea modelelor de proiectare se utilizează notaţii grafice (în UML) şi un
limbaj care permite o descriere uniformă a tuturor modelelor. Formatul de descriere variază, dar
în general cuprinde secţiunile:
• numele modelului – descrie sintetic problema rezolvată de model şi soluţia; când este
un „pattern” clasic se precizează şi categoria din care face parte.
• scopul – se precizează pe scurt ce problemă rezolvă.
• problema – descriere mai largă a problemei rezolvate şi a contextului în care ea
apare.
• soluţia – descriere a elementelor de proiectare utilizate şi a relaţiilor dintre ele.
Soluţia nu descrie un proiect particular sau o implementare concretă, ci un ansamblu
abstract de clase şi obiecte care rezolvă un anumit gen de probleme de proiectare.
• consecinţele implicate de folosirea modelului – acestea pot privi impactul asupra
flexibilităţii, extensibilităţii sau portabilităţii sistemului, după cum pot să se refere la
aspecte ale implementării sau limbajului de programare utilizat.

În funcţie de scopul lor, modelele de proiectare se pot clasifica în:


• modele creaţionale – privesc modul de descriere al obiectelor;
(abstract factory, builder, factory method, prototype, singleton)
• modele structurale – se referă la compoziţia claselor sau obiectelor;
(adapter, bridge, composite, decorator, façade, flyweight, proxy)
• modele comportamentale – caracterizează modul în care obiectele şi clasele
interacţionează şi îşi distribuie responsabilităţile.
(chain of responsibility, command, interpreter, iterator, mediator, memento,
observer, state, strategy, template method, visitor)

În funcţie de domeniul de aplicare, modelele de proiectare se pot aplica obiectelor sau


claselor.
¾ Modele de proiectare pentru obiecte se referă la relaţiile dintre instanţe, relaţii care au
un caracter dinamic.

• Modelele creaţionale ale obiectelor acoperă situaţiile în care o parte din


procesul creării unui obiect cade în sarcina unui alt obiect.

• Modelele structurale ale obiectelor descriu căile prin care se asamblează


obiecte.

• Modelele comportamentale ale obiectelor descriu modul în care un grup de


obiecte cooperează pentru a îndeplini o sarcină ce nu ar putea fi efectuată de
un singur obiect

¾ Modelele de proiectare ale claselor se referă la relaţiile dintre clase, relaţii stabilite
prin moştenire şi care sunt statice (fixate la compilare).

• Modelele creaţionale ale claselor acoperă situaţiile în care o parte din


procesul creării unui obiect cade în sarcina subclaselor.

• Modelele structurale ale claselor descriu modul de utilizare al moştenirii în


scopul compunerii claselor.

• Modelele comportamentale ale claselor utilizează moştenirea pentru


descrierea unor algoritmi şi fluxuri de control.

Scop-Domeniu aplicare Creaţionale Structurale Comportamentale


Clasă factory method adapter (class) interpreter
template method
Obiect abstract factory adapter (object) chain of responsibility
builder bridge command
prototype, composite iterator
singleton decorator mediator
façade memento
flyweight observer
proxy state
strategy
visitor
3.1 Modele creaţionale

3.1.1 Modelul Singleton / Unicat


Scop: Garantarea existenţei unei singure instanţe a unei clase. Se asigură o modalitate de
a accesa instanţa respectivă. Se furnizează un punct global de acces la ea.
Motivaţie: există situaţii în care unele clase trebuie să aibă exact o singură instanţă.
– Un singur spooler
– Un singur sistem de fişiere
– Un singur gestionar de ferestre
Aplicabilitate : pattern-ul Singleton se foloseşte atunci când:
– trebuie să existe o singură instanţă a unei clase şi trebuie să fie accesibilă
clienţilor din diferite puncte de acces;
– o singură instanţă ar trebui extinsă în subclase şi clienţii ar trebui să folosească
instanţa extinsă fără să-şi modifice codul.
Structura:

3.1.2 Modelul abstract factory / fabrică abstractă


Scop: Oferă o interfaţă pentru crearea unei familii de obiecte înrudite sau dependente,
fără a specifica explicit clasele lor concrete.
Motivaţie: Să considerăm o aplicaţie care suportă mai multe moduri de prezentare.
Acestea implică diferite imagini şi comportamente pentru componentele interfeţei utilizator, cum
ar fi scrollbar-uri, ferestre şi butoane
Aplicabilitate:
– Sistemul trebuie să fie independent de modul în care produsele cu care lucrează
sunt create, compuse şi reprezentate.
– Sistemul trebuie să fie configurat în una sau mai multe familii de produse.
– O familie de produse sunt proiectate să funcţioneze doar împreună.
– Se doreşte proiectarea unei familii de produse înrudite care să lucreze împreună şi
această constrângere ar trebui păstrată.
– Se doreşte furnizarea unei librării de produse şi dorim să facem publică interfaţa
acestora, dar nu şi implementarea.
Participanţi:
- AbstractFactory – declară o interfaţă pentru operaţii de creare a produselor
abstracte
- ConcreteFactory – implementează operaţiile de creare a produselor concrete
- AbstractProduct – declară o interfaţă pentru o categorie de produse
- Client – utilizează doar interfeţele AbstractFactory şi AbstractProdus
Consecinţe:
- izolează clasele (se izolează clienţii de implementarea claselor); clienţii
manipulează instanţele prin interfeţele lor abstracte; deoarece o clasă factory
abstractă creează o familie de produse, întreaga familie se schimbă simultan;
- asigură consistenţa între produse;
- suportarea noilor tipuri de produse este dificilă (implică extinderea interfeţei).
Structura:

3.1.3 Modelul builder


Scop: Separă construirea unui obiect complex de reprezentarea sa, astfel ca procesul de
construire să poată crea diferite reprezentări.
Aplicabilitate:
– Algoritmul de creare a unui obiect complex este independent de părţile care
compun efectiv obiectul.
– Sistemul trebuie să permită diferite reprezentări pentru obiectele care sunt
construite.
Motivaţie:

Structura:
3.1.4 Modelul Factory Method / Metodă fabrică
Scop: Defineşte o interfaţă pentru crearea unui obiect, dar lasă în sarcina subclaselor
alegerea tipului acestuia.
Motivaţie:
– O bibliotecă foloseşte clase abstracte pentru a defini şi menţine relaţii între
obiecte.
– Un tip de responsabilitate este crearea de astfel de obiecte.
– Biblioteca ştie când trebuie creat un obiect, dar nu şi ce tip de obiect trebuie creat
(acesta este specific aplicaţiei care foloseşte biblioteca)
Exemplu

Aplicabilitate:
– Când o clasă nu poate anticipa tipul obiectelor care trebuie să le creeze
– Când o clasă vrea ca subclasele să specifice tipul obiectelor de creat
Structura:

3.1.5 Modelul Prototype / Prototip


Scop. Specifică ce obiecte trebuie create folosind o instanţă pe post de prototip. Noile
obiecte sunt create prin copierea acestui prototip.
Motivaţie: aplicaţie grafică / drag-and-drop
Aplicabilitate
– Sistem independent de cum sunt create produsele şi
– Una dintre următoarele
• Clasele de instanţiat sunt specificate în momentul rulării
• Se doreşte evitarea creării unei ierarhii de fabrici
• Este mai convenabil să copiez o instanţă existentă decât să creez una nouă.

3.2 Modele structurale

3.2.1 Modelul Façade / Faţadă


Scop: oferă o interfaţă unificată pentru un set de interfeţe dintr-un sistem.
Motivaţie:
– subsisteme = reducerea complexităţii
– Minimizarea comunicaţiei dintre subsisteme

Aplicabilitate:
– Se doreşte utilizarea unei interfeţe simplificate la un sistem complicat
– Există multe dependenţe între clienţi şi implementările concrete ale conceptelor
– Se doreşte stratificarea sistemului
3.2.2 Modelul Adapter / Adaptor
Scop
– converteşte interfaţa unei clase la interfaţa pe care clienţii acesteia o aşteaptă.
– permite inter-operabilitatea claselor care altfel nu ar fi compatibile
Motivaţie
– O clasă dintr-o bibliotecă, proiectată să fie reutilizabilă, nu este reutilizabilă din
simplul motiv că interfaţa acesteia nu se potriveşte cu una specifică domeniului în
care se doreşte utilizată.
Aplicabilitate
– Se doreşte utilizarea unei clase cu o interfaţă incompatibilă
– Se doreşte crearea unei clase reutilizabile ce colaborează cu clase neprevăzute
– (adaptor de obiecte) Se doreşte folosirea a câtorva subclase, dar adaptarea prin
derivare este nepractică.

3.2.3 Modelul Proxy / Substitut


Scop
– Oferă un surogat sau înlocuitor pentru un obiect, prin care se controlează accesul
la acel obiect.
Motivaţie
– Creare / iniţializare la cerere a obiectelor
Aplicabilitate
– Substitut pentru depărtat, ambasador
– Substitut virtual
– Substitut protector
– Referinţă deşteaptă
• Indicator deştept
• Încărcarea unui obiect persistent la prima accesare
• Asigurarea excluderii mutuale
Tipuri de obiecte proxy:
– Cache Proxy: salvează resurse memorând rezultate temporare
– Count Proxy: face si alte operaŃii înainte/după apelarea subiectului real
– Protection Proxy: controlează accesul la obiectul real
– Remote Proxy: reprezentant local al unui obiect aflat la o altă adresă
– Virtual Proxy: creează obiecte la cerere (când este nevoie de ele)

3.2.4 Modelul Decorator


Scop:
– Obiectelor li se pot ataşa responsabilităţi în mod dinamic.
– Alternativă flexibilă la derivare pentru extinderea funcţionalităţii
Motivaţie:

Aplicabilitate:
– Adăugarea responsabilităţilor în mod dinamic şi transparent
– Retragerea responsabilităţilor
– Atunci când derivarea devine nepractică
Structura:

3.2.5 Modelul Composite / Amestec


Scop:
– Se doreşte gruparea obiectelor în structuri arborescente pentru a reprezenta relaţii
de tip parte-întreg.
– Obiectele şi amestecurile de obiecte sunt tratate uniform.
Motivaţie:

Aplicabilitate:
– Reprezentarea relaţiei parte-întreg
– Ignorarea diferenţei dintre obiecte individuale şi amestecuri de obiecte
Structura
Participanţi:
• Component (Grafic)
- declară interfaţa pentru obiectele compuse
- declară o interfaţă pentru accesarea şi maparea componentelor copil
- defineşte o interfaţă pentru accesarea componentelor părinte într-o
structură recursivă
• Leaf (Rectangle, Line, Text etc.) – reprezintă obiectele frunză
• Composite (Picture)
- defineşte comportamentul pentru componentele care au copii
- memorează componentele copil
• Client – manipulează obiectele

3.2.6 Modelul Flyweight / Muscă


Scop:
– Folosirea partajării pentru gestionarea unui număr mare de obiecte cu granulaţie
mică. Numărul total de obiecte este mult mai mare decât numărul de obiecte
distincte. Starea unui obiect poate fi divizată în două părţi distincte: stare
extrinsecă şi stare intrinsecă. Starea intrinsecă reprezintă partea constantă (nu este
modificată de alte acţiuni). Starea extrinsecă este dependentă de context şi este
transmisă ca argument al unei metode.
Motivaţie

Aplicabilitate (îndeplinirea simultană a condiţiilor):


– Aplicaţia foloseşte un număr mare de obiecte.
– Spaţiul necesar stocării este foarte mare din cauza numărului mare de instanţe
– Cea mai mare parte a stării unui obiect poate fi făcută extrinsecă
– Multe grupe de obiecte pot fi înlocuite cu relativ puţine obiecte partajate
– Aplicaţia nu depinde de identitatea obiectelor

Structura:

Observaţie: FlyweightFactory creează şi gestionează obiectele „flyweight”

3.2.7 Modelul Bridge / Punte


Scop
– Decuplarea unei abstractizări de implementarea sa astfel încât cele două să poată
varia independent
Exemplu:

O arhitectură îmbunătăţită se poate vedea mai jos:


Motivaţie:
– Când o abstractizare are câteva implementări posibile, se foloseşte de obicei
moştenirea
– Folosirea moştenirii în această situaţie duce la cod greu de modificat şi de extins.
– Punerea abstractizărilor şi a implementărilor în ierarhii diferite duce la crearea de
cod mai uşor de întreţinut
Structura:

3.3 Modele comportamentale

3.3.1 Modelul Chain of Responsibility / Lanţ de responsabilităţi


Scop:
– evită cuplarea emiţătorului unei cereri de receptorul acesteia dând posibilitatea
mai multor obiecte să trateze cererea.
– obiectele sunt înlănţuite, iar cererea este trimisă de-a lungul lanţului din obiect în
obiect până când un obiect o va trata.
Motivaţie: help contextual
Structura:

Aplicabilitate:
– Mai multe obiecte pot trata o cerere, iar obiectul care o va trata nu este cunoscut a
priori, el va fi determinat in mod automat.
– Se doreşte ca cererea să fie făcută unui grup de obiecte fără a specifica în mod
explicit receptorul acesteia.
– Mulţimea obiectelor care pot trata cererea trebuie specificată în mod dinamic.

Alt tip de structură:


3.3.2 Modelul Command / Comandă
Scop:
– încapsulează o cerere sub forma unui obiect;
– permite parametrizarea clienţilor cu cereri diferite;
– permite memorarea cererilor într-o coadă;
– suportă operaţii reversibile (undoable operations)
Motivaţie:
– asignarea unor funcţionalităţi opţiunilor dintr-un meniu
Exemplu:
- definirea macrourilor = secvenţă de comenzi
Exemplu:

Aplicabilitate:
– Parametrizarea obiectelor în funcţie de acţiunea care trebuie să o facă;
– Specificarea, adăugarea într-o coadă şi executarea cererilor la momente diferite;
– Suportă operarţii reversibile (metoda Execute poate memora starea şi permite apoi
revenirea la această stare);
– Structurează sistemul în operaţii de nivel înalt care sunt construite pe baza unor
operaţii primitive (ex. tranzacţii).

Structura:
3.3.3 Modelul Iterator
Scop: Oferă o modalitate de a accesa obiecte agregate în mod secvenţial fără a cunoaşte
modul de agregare.
Motivaţie – exemple:
Structura:

3.3.4 Modelul Mediator


Scop:Defineşte un obiect care încapsulează modul de interacţiune al unui set de obiecte.
Promovează cuplarea slabă.
Motivaţie: În aplicaţiile reale, putem ajunge în situaţii în care fiecare obiect este legat cu
fiecare (ceea ce nu e indicat).
Aplicabilitate:
– Un set de obiecte interacţionează într-un mod determinat dar complicat
– Reutilizarea unui obiect este dificilă din cauza comunicării cu multe alte obiecte.
– Comportamentul distribuit prin multe clase să fie refolosit fără folosirea excesivă
a moştenirii
Structura
Exemplu
3.3.5 Modelul Memento
Scop: Fără violarea încapsulării, captarea şi externalizarea stării interne a unui obiect
astfel ca obiectul să îşi poată reface starea mai târziu.
Motivaţie: refacere
Aplicabilitate:
– Un instantaneu al unei (sub)stări ale unui obiect trebuie luat pentru a fi restaurat şi
– O interfaţă directă pentru obţinerea stării ar expune detalii de implementare şi ar
compromite încapsularea
Structura:

Consecinţe:
– Păstrează graniţele încapsulării
– Simplifică obiectul Originator
– Folosirea memento-urilor poate fi costisitoare si poate induce costuri ascunse

3.3.6 Modelul Observer / Observator


Scop: defineşte o dependenţă de tip 1-la-n între obiecte, astfel ca atunci când un obiect se
modifică, cele care depind de el sunt notificate şi actualizate automat.
Motivaţie

Aplicabilitate
– Când o abstractizare are două aspecte. Încapsularea fiecăruia în obiecte diferite
permite modificarea şi refolosirea lor independentă.
– Când o schimbare a unui obiect are ca efect schimbări ale altor obiecte, fără a şti
de la început câte obiecte sunt în această situaţie.
– Când se doreşte decuplarea obiectelor notificate de obiectul care se schimbă.
– Când un obiect are posibilitatea să anunţe ceva altor obiecte (notify) fără să
cunoască cine sunt aceste obiecte.
– Fiecare obiect observabil (subiect) poate să aibă unul sau mai mulţi observatori
Structura:
În UML interacţiune dintre obiectele participante în model poate fi descrisă cu diagrame
de secvenţe:

3.3.7 Modelul State / Stare


Scop: Permite unui obiect să îşi schimbe comportamentul atunci când starea sa se
modifică.
Motivaţie
Aplicabilitate:
– Comportamentul unui obiect depinde de stare. Schimbarea comportamentului
trebuie să fie făcută în timpul rulării.
– Se doreşte evitarea existenţei unei structuri decizionale identice în fiecare metodă.
Structură

3.3.8 Modelul Strategy / Strategie


Scop: Se defineşte o familie de algoritmi; se încapsulează fiecare membru; algoritmii se
fac interschimbabili.
Motivaţie:

Aplicabilitate:
– Multe clase înrudite diferă doar prin comportament
– Se folosesc diverse variante ale unui algoritm
– Algoritmii folosesc date irelevante pentru client
– O clasă defineşte comportamente multiple, definite de condiţii gardă la începutul
metodelor.
Structură

Contextul este configurat cu una din strategiile concrete. Menţine o referinţă către
interfaţa Strategy, prin care accesează operaţiile publice ale strategiei cu care este configurat.
Poate să pună la dispoziţia strategiilor o interfaţă de acces la datele prelucrate de strategia
concretă.
Strategy introduce interfaţa comună strategiilor concrete.
3.3.9 Modelul Template Method / Metodă şablon
Scop: Se defineşte scheletul unui algoritm într-o metodă, lăsând implementarea paşilor
algoritmului în seama subclaselor.
Motivaţie:

Aplicabilitate:
– Implementarea structurii fixe a unui algoritm, lăsând clasele derivate să
implementeze funcţionalitatea care variază
– Când funcţionalitatea comună a unor clase este relocată într-o clasă de bază
Structură

3.3.10 Modelul Visitor / Hoinar


Scop: Reprezintă o operaţie ce se efectuează asupra structurii obiectului. Permite
definirea de noi operaţii fără a schimba clasele elementelor asupra cărora se lucrează.
Motivaţie – exemple:
Structura:
IMPLEMENTAREA SISTEMELOR SOFTWARE

În faza de implementare este produs codul corespunzător proiectului furnizat de faza


proiectării, î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ă.
O problemă majoră a fazei de implementare constă în dificultatea translării proiectului
în cod sursă. Oricât de bun ar fi proiectul este necesar un efort oarecare de a scrie codul
corespunzător, ori aceasta este o sursă de a introduce erori.
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 proiectului.
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ă
oarecare timp şi efort se vor consuma pentru corectarea acestor erori. Din păcate, corectarea
erorilor nu este o sarcină uşoară. S-a constatat că un programator are 50% şanse să descopere
eroarea într-un interval de 5-10 linii de cod şi numai 20% şanse să o descopere într-un
domeniu de 40-50 linii.
Documentele de bază produse în această fază sunt:
• codul sursă şi obiect comentate într-o formă standard sau respectând anumite
convenţii
• pliante (dosare) ale software-ului, prezentând modulele software individuale
• manualul de utilizare a produsului software, prezentând convenţiile utilizate în
programe, o prezentare generală a implementării, descrierea particularităţilor
• planul pentru coordonarea configuraţiei
• planul pentru testarea produsului

Un aspect important în managementul fazei de implementare este cel al


managementului configuraţiei sistemului software. Motivul constă în faptul că produsul se
găseşte în diverse faze pe măsură ce echipele de programatori implementează diferite părţi ale
sale şi nu există un produs “întreg” până la integrarea tuturor modulelor. De aceea, la anumite
intervale de timp, toate modulele vor fi reunite formând o anumită versiune a produsului, baza
de la care programatorii vor lucra în continuare.

1. Scrierea codului

Codul este produs în module. Fiecare modul trebuie să fie inteligibil pentru cel care îl
verifică şi pentru programatorul care ulterior îl va întreţine.
Fiecare modul :
• include un header introductiv (titlu, nume autor, data creării, istoria modificărilor)
• declară toate variabilele şi le documentează
• foloseşte nume semnificative şi neambigue pentru variabile, funcţii, etc.

1
• foloseşte comentarii (acestea se recomandă pentru zonele dificile ale codului şi nu
pentru cele evidente. Se recomandă să se separe comentariile de codul sursă prin
linii vide)
• evită ascunderea logicii modulului de către codul de diagnoză (Codul de diagnoză
se utilizează, de obicei, pentru a afişa conţinutul unor variabile sau pentru a
evidenţia starea programului. Există tendinţa de a şterge acest cod din versiunea
finală a sistemului, totuşi ele este folositor şi în faza de întreţinere a sistemului. De
aceea, se recomandă ca acest cod să fie comentat sau compilat condiţionat (inclus
ca linii de depanare)).
• utilizează regulile programării structurate
• este consistent în folosirea limbajului de programare (păstrează acelaşi stil)
• menţine un cod cât mai scurt şi mai simplu (Un modul trebuie să aibă o astfel de
dimensiune încât să fie vizibil dintr-o dată. Lungimea maximă recomandată este de
50 linii, fără a considera header-ul şi codul de diagnoză. În ceea ce priveşte
simplitatea codului, numărul de elemente care trebuie cuprinse mental examinând
o parte a modulului nu trebuie să depăşească aproximativ 7 elemente.)
• are o prezentare care să evidenţieze uşor logica de control (Tehnica obişnuită este
de a separa blocurile între ele prin linii vide şi de a alinia blocurile din diversele
construcţii ale limbajului. Se recomandă a nu se utiliza mai multe instrucţiuni pe
aceeaşi linie.)

Standardele codării trebuie să fie stabilite pentru fiecare limbaj utilizat şi prevăzut în
documentaţia sistemului. Aceste standarde trebuie să furnizeze reguli pentru:
• prezentarea informaţiilor (din header) şi forma comentariilor
• denumirea programelor, subprogramelor, fişierelor, variabilelor şi datelor
• limitarea dimensiunii modulelor
• folosirea rutinelor de bibliotecă
• definirea constantelor
• definirea tipurilor de date
• folosirea datelor globale
• tratarea erorilor

2. Metode pentru implementare

Î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, dezvoltarea sistemelor Jackson, metode
formale).
Următorul pas este a defini procesarea fiecărui modul prin metode ca: hărţi de fluxuri
(fowcharts), rafinarea pas cu pas, programarea structurată, limbaje de proiectare a
programelor (PDL), pseudo-codul, programarea structurată Jackson.
Producerea codului implică scrierea codului într-un limbaj de programare, verificarea
şi integrarea sa cu alte coduri pentru obţinerea unui sistem final.
În scopul scrierii codului se vor utiliza limbajele de programare.

Limbaje de programare
Următoarele clase de limbaje de programare sunt larg recunoscute:
a) limbaje procedurale (imperative sau algoritmice)
b) limbaje orientate-obiect

2
c) limbaje funcţionale
d) limbaje de programare logică
Limbajele funcţionale şi logice sunt numite şi declarative deoarece permit
programatorului să declare “ce” trebuie executat şi nu “cum”.

a) Limbaje procedurale - suportă următoarele caracteristici:


• secvenţa (permite specificarea ordinii execuţiei instrucţiunilor)
• selecţia (permite evaluarea unei condiţii ţi luarea unei decizii)
• iteraţia (permite structurilor repetitive)
• diviziunea în module (permite descompunerea funcţională)
Limbajele procedurale tradiţionale sunt COBOL şi FORTRAN.
Unele limbaje procedurale suportă:
• structurarea în blocuri - impune ca un modul să aibă un singur punct de intrare şi
un singur punct de ieşire. Pascal, Ada şi C suportă această caracteristică.
• 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. Această caracteristică ajută compilatorul în evidenţierea erorilor şi în
compilarea eficientă. Ada şi Pascal sunt limbaje puternic tipizate.
• recursivitatea - permite unui modul să se autoapeleze. Pascal, Ada şi C suportă
recursivitatea.

b) Limbaje orientate-obiect - suportă toate caracteristicile limbajelor de programare


structurată şi, în plus:
• moştenirea - tehnica prin care modulele pot prelua capabilităţi de la modulele de
nivel superior.
• polimorfismul - abilitatea unui proces de a lucra cu diferite tipuri de date sau a
unei entităţi de a se referi la momentul execuţiei la instanţe ale diferitelor clase.
Ideal ar fi ca un limbaj să fie complet polimorf, astfel încât nu ar mai fi necesare
porţiuni de cod pentru fiecare tip de dată. Polimorfismul implică suportul pentru
“dynamic binding”. “Dynamic binding” înseamnă legarea metodelor obiectelor de
selectarea mesajului la momentul execuţiei şi nu al compilării.
• mesajele - Limbajele orientate-obiect utilizează mesaje pentru implementarea
interfeţelor. Un mesaj încapsulează detaliile acţiunii care trebuie realizată. Un
mesaj este trimis de către un obiect către un alt obiect-receptor pentru a invoca
serviciul celui din urmă.
Exemple de astfel de limbaje sunt: Smalltalk şi C++.

c) Limbaje funcţionale (LISP) - suportă structurarea declarativă, construcţiile procedurale


fiind inutile. În particular, construcţia secvenţă nu mai este utilizată în logica programului. Un
model informaţional de bază (arbore sau listă) este utilizat pentru a defini această logică.
Dacă o anumită informaţie este necesară pentru o operaţie, ea este automat obţinută din
modelul informaţional. În programarea funcţională operatorii (funcţiile) se aplică
argumentelor (parametrii). Înşişi parametrii pot fi expresii funcţionale astfel încât un program
funcţional poate fi gândit ca o singură expresie aplicând o funcţie alteia.
Programele scrise în limbaje funcţionale par diferite faţă de cele scrise în limbaje procedurale
deoarece asignările sunt absente. Programele în limbaj funcţional sunt scurte, clare şi sunt
potrivite atât pentru specificaţii cât şi pentru implementarea rapidă.

3
d) Limbaje de programare logică - implementează o anumită formă a logicii clasice. Ca şi
limbajele funcţionale, ele au o structură declarativă. În plus, suportă:
• backtracking - abilitatea de a reveni la un punct anterior în lanţul de deducţii, utilă
în momentul traversării unui arbore de cunoştinţe
• backward chaining - începe cu o ipoteză şi raţionează înapoi spre factorii care au
determinat ca această ipoteză să fie adevărată. De exemplu, dacă factorul A şi
ipoteza B sunt înlănţuite în expresia IF A THEN B, backward chaining permite
să se deducă faptul că A este adevărat din ipoteza că B este adevărat.
• forward chaining - opusul conceptului backward chaining. Începe de la o colecţie
de factori şi raţionează spre o concluzie. De exemplu, dacă factorul A şi concluzia
B sunt înlănţuite în expresia IF A THEN B, backward chaining permite să se
deducă din faptul că A este adevărat concluzia că B este adevărat
Cel mai important limbaj de programare logică este Prolog.

3. Utilitare pentru implementare şi test

O gamă largă de utilitare sunt valabile pentru dezvoltarea programelor, depanarea şi


testarea acestora:
• utilitare de modelare (Modelling Tools) - generează nucleul (“scheletul”)
modulelor.
Se 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
d.p.d.v. sintactic. Cele mai simple astfel de editoare recunosc parantezele şi realizează o
aliniere automată care fac programul mai uşor de înţeles şi reduc erorile. E posibil, de
asemenea, de a furniza scheme (tipare) pentru construirea programelor, conţinând headere
standard, secţiuni obligatorii pentru declararea constantelor şi tipurilor de date. Acestea pot fi
generate automat de utilitarele de modelare. Editoarele care recunosc aceste scheme pot
reduce timpul de dezvoltare şi pot preveni erorile.

4
• analizoare statice - examinează codul sursă.
Analiza statică este procesul de scanare a textului unui program pentru detectarea unor
erori:
1. identifică variabile neutilizate sau utilizate înainte de a fi asignate
2. verifică dacă valoarea variabilei este în intervalul admis
3. furnizează o prezentare a structurii aplicaţiei
4. măsoară complexitatea codului în termenii unei metrici
5. transformă codul sursă într-un limbaj intermediar pentru verificare formală
6. 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 caracteristica 1). 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:
1. listare completă
2. cross-referenţierea
3. dimensiunea datelor şi modulelor
4. diagnosticare
5. verificare completă
6. switch-uri (ex: verificarea limitelor vectorilor; limbaj strict sau extensii)
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ă (exemplu:
directiva pragma în Ada). 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

5
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 (coverage test). Unele
analizoare dinamice verifică dacă programul utilizează corect memoria, de exemplu, verifică
dacă apelurile pentru alocarea memoriei au corespondent în apeluri pentru dealocare,
determinând astfel golurile de memorie (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:
1. generarea şi gestiunea datelor de test
2. verificarea automată a rezultatelor
3. diagnoza erorilor şi depanarea
4. realizarea testelor driver şi stubs
Utilitarele generale de test pot genera mari cantităţi de date de intrare
• procesoare de text- 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) şi 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.

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

6
TESTAREA SISTEMELOR SOFTWARE
Testarea software determină dacă un sistem software este gata de livrare şi estimează nivelul
de performanŃă al acestuia. Testarea furnizează o bază pentru interacŃiunea cu persoanele implicate
în proiect. Creşterea complexităŃii sistemelor software a dus la o creştere a bugetului alocat acestei
faze din procesul de dezvoltare al unui proiect (între 30 şi 50%). O mare parte a efortului necesar
realizării sistemelor software este alocată dezvoltării modelelor de testare şi a aplicaŃiilor de testare
automată.
Testarea este procesul execuŃiei programului cu scopul de a pune în evidenŃă erorile.
Detectarea erorilor este scopul principal al testării. În acelaşi timp, prezintă interes atât dezvoltarea
unor seturi de date de test adecvate care să conducă la activarea erorilor precum şi modalităŃile de
alocare a timpului necesar testării, în special în sistemele de mare complexitate.
În mod obişnuit, spunem că un sistem software eşuează atunci când nu îndeplineşte cerinŃele
impuse. FuncŃionarea defectuoasă poate fi rezultatul unuia dintre următoarele motivele:
• SpecificaŃiile sunt greşite sau unele cerinŃe ale clienŃilor nu sunt specificate;
• SpecificaŃiile pot conŃine cerinŃe care nu pot fi implementate cu softul disponibil;
• Proiectarea sistemului poate fi greşită.
• Implementarea codului are defecte; unii algoritmi sunt greşit sau incomplet implementaŃi.
ExperienŃa acumulată de-a lungul timpului în domeniul testării software a dus la elaborarea
unor politici de testare. Spre exemplu, Myers (1976) a propus următoarele reguli de bază pentru
realizarea testării sistemelor software:
• Se determină momentul în care se opreşte testarea;
• Responsabilitatea testării programului revine unui tester, nu celui ce realizat programul;
• Se descriu rezultatele aşteptate pentru fiecare caz de test;
• Se scriu cazuri de test pentru condiŃii de intrare valide şi nevalide;
• Se verifică rezultatele fiecărui test.
• Testarea se atribuie celor mai creative persoane din echipă
Pentru proiectele complexe, specificaŃiile de test pentru sistem şi pentru acceptarea acestuia
nu trebuie scrise de către analişti, proiectanŃi şi programatori care au lucrat la proiectul respectiv
(pentru sistemele mici şi medii e acceptabil ca aceste specificaŃii să fie scrise de către cei ce
dezvoltă sistemul) ci de către utilizatorii sistemului.
Testarea software cuprinde o multitudine de strategii de testare. În general, metodele de
testare sunt specializate, în sensul că cele mai multe proiecte creează propriile metode de testare
depinzând de produsul respectiv.
Metodele de testare dinamică presupun executarea programului folosind aşa numitele date
de test. Datele de test se construiesc conform cerinŃelor funcŃionale specificate iar rezultatele
furnizate de program se compară cu cele prezentate în specificaŃii.
Metodele de testare statică cuprind verificarea programului, analiza anomaliilor, inspecŃia
codului. Verificarea programului necesită specificarea precondiŃiilor la intrare şi a postcondiŃiilor la
ieşire. Analiza anomaliilor caută eventuale comportări anormale ale programului (spre exemplu,
porŃiuni de cod care nu sunt executate niciodată). Scopul testării statice este de a analiza sistemul
software şi de a deduce operaŃiile sale curente ca o consecinŃă logică a deciziilor de proiectare.
Această modalitate de testare nu necesită execuŃia programului.

1. Clasificarea metodelor de testare


Metodele de testare pot fi clasificate în două mari categorii:
• Testarea black box (cutie neagră). Această abordare se concentrează asupra intrărilor,
ieşirilor şi funcŃionalităŃii modulelor software.

1
• Testarea white box (cutie albă). Această abordare presupune inspectarea structurii
codului modulelor software.
Testarea black box se mai numeşte testare funcŃională. Punctul de plecare este fie o
specificaŃie, fie codul. În cazul codului, datele de test trebuie să permită verificarea funcŃionalităŃii
programului. Testerul nu este interesat de modul în care este implementat programul respectiv, ci de
modul în care acesta furnizează răspunsul dorit.
Testarea white box, cunoscută şi sub numele de testare structurală, presupune analizarea
implementării programului (modulului). Se verifică executarea corectă a tuturor căilor şi ramurilor
codului programului testat. În tabelul 1 se prezintă câteva metode de testare ce fac parte din cele
două mari categorii prezentate mai sus, metode ce vor fi prezentate pe larg în secŃiunile ulterioare.
Tabelul 1 Clasificarea formelor de testare
Testare funcŃională Testare structurală
Dinamică Testare aleatoare Testare computaŃională
Testare pe domenii Testare pe domenii
Graf cauză-efect Testare căi
Generare date
Analiza schimbărilor
Statică Verificare specificaŃii Inspectare cod
Verificare program
ExecuŃie simbolică
Analiză anomalii
În cazul testării sistemelor software există şi probleme ce nu Ńin de tehnică. Spre exemplu, se
pot pune următoarele întrebări: Cine face testarea? Testarea poate fi realizată de un programator
care a fost implicat în scrierea codului? Se apelează la un tester independent, se schimbă porŃiuni
de cod între membrii aceleiaşi echipe sau se lasă testarea în seama utilizatorului final? Fiecare
dintre alternativele propuse are argumente pro şi contra.
O altă problemă fundamentală este legată de durata activităŃilor de testare, când apar două
puncte de vedere cel puŃin contradictorii care trebuie luate în considerare. Primul este al
proiectanŃilor care fac tot posibilul pentru a realiza un produs fără defecte. Cel de-al doilea este al
managerilor de proiect care iau în considerare constrângerile de timp impuse proiectului.
Înainte de a intra într-o prezentare mai detaliată a diferitelor variante de testare, este
importantă precizarea paşilor principali care intervin în orice schemă de testare.
Etapele principale ale unei scheme de testare
• SelectaŃi ce trebuie măsurat (cuantificat) de testul respectiv. Înainte de realizarea testului,
trebuie identificate scopurile acestuia. Scopurile pot fi diferite (spre exemplu, testarea fiabilităŃii,
testarea completitudinii cerinŃelor).
• DecideŃi cum faceŃi testarea a ceea ce trebuie testat. După ce aŃi stabilit ce este de testat, trebuie
să decideŃi cum realizaŃi testele relevante.
• DezvoltaŃi cazurile de test. Pentru tipurile de testare deja acceptate, trebuie creată o colecŃie de
cazuri de test (situaŃii de test) pentru antrenarea sistemului supus testării.
• DeterminaŃi rezultatele aşteptate ale testului respectiv.
• ExecutaŃi cazurile de test.
• ComparaŃi rezultatele obŃinute cu cele aşteptate.

2. Niveluri ale testării software


Testarea software se realizează la diferite nivele de-a lungul întregului ciclu de viaŃă al
produsului. Testarea începe la nivel de componente software individuale. Se verifică
funcŃionalitatea şi structura fiecărei componente, după care se face testarea la integrare a
componentelor. Standardul IEEE de verificare şi validare software (IEEE Std. 1059-1993) identifică
patru nivele de testare, după cum se poate observa în tabelul 2. CorespondenŃa între nivelele de
testare şi etapele ciclului de viaŃă a sistemului este prezentată în figura 1.
2
Tabel 2
Testare DefiniŃie Scop
Componentă Se verifică implementarea elementelor Logica programului este completă şi corectă.
software (ex. FuncŃii, module). Componentele funcŃionează conform proiectării.
Integrare Componentele software şi hardware sunt Obiectivele proiectării sunt satisfăcute
combinate şi testate până la integrarea
întregului sistem
Sistem Se testează sistemul integrat Proiectul software este o entitate completă în
concordanŃă cu cerinŃele operaŃionale.
Acceptare Se verifică dacă rezultatele testelor Obiectivele clienŃilor sunt satisfăcute
satisfac criteriile de acceptare ale
clienŃilor

Figura 1. Nivele de observabilitate a testării

3. ActivităŃi de test
În cadrul testării software se pot delimita următoarele activităŃi cheie:
• Elaborarea planului de testare
• Proiectarea testului
• Stabilirea cazurilor de test
• Determinarea procedurii de testare
• Executarea testului
• Realizarea raportului de testare
Planul de testare trebuie să precizeze scopul, tipul abordării, resursele necesare şi un orar al
activităŃii de testare. Este necesară de asemenea identificarea surselor şi nivelelor de risc şi stabilirea
persoanele ce realizează testarea. Planificarea testării poate începe din momentul în care cerinŃele
sistemului sunt complete. Este dificilă determinarea momentului în care se opreşte testarea. Din
acest motiv trebuie introduse criterii pentru a stabili dacă un test este complet sau nu.
Proiectarea testului rafinează abordarea propusă în planul de testare. În această etapă se
identifică principalele caracteristici ce trebuie testate şi se definesc cazurile de test asociate. Este
recomandat ca proiectarea testelor să se facă pentru o testare de regresiune (testele executate
anterior să poată fi repetate la un punct ulterior în procesul de dezvoltare şi întreŃinere).
Cazurile şi procedurile de test se construiesc în faza de implementare. Se urmăreşte
realizarea unei colecŃii de cazuri de test cât mai redusă care să poată duce la îndeplinirea scopurilor
propuse. Cazurile de test bine realizate au o mare probabilitate de a detecta erorile. Procedura de
testare identifică toŃi paşii necesari operării sistemului şi rulării cazurilor de test ce implementează
proiectul de test obŃinut în etapa anterioară.
Executarea testului începe la nivel de componente şi continuă cu testarea la integrare,
testarea sistemului şi testarea de acceptare.
Raportul de testare rezumă toate ieşirile testului şi subliniază discrepanŃele detectate.
ActivităŃile de testare se distribuie de-a lungul întregului ciclu de viaŃă al produsului.
4. Tipuri de teste software
łinând cont de multitudinea metodelor de testare existente este avantajoasă luarea în

3
considerare a testelor în funcŃie de modalitatea în care acestea devin accesibile proiectantului.
Testele funcŃionale sunt utilizate pentru a rula codul cu intrări nominale pentru care valorile
aşteptate sunt disponibile. Pentru aceste intrări sunt cunoscute de asemenea condiŃiile la limită. Spre
exemplu, pentru a testa funcŃional înmulŃirea matricelor se vor lua în considerare matrice al căror
produs este dinainte cunoscut.
Testele de performanŃă ajută la determinarea unor performanŃe ale sistemului cum ar fi
timpul de execuŃie al unor părŃi de cod şi timpul de răspuns (în cazul sistemelor încorporate). Scopul
acestui tip de testare constă în identificarea punctelor slabe ale sistemului software, cuantificarea
neajunsurilor în scopul unei viitoare îmbunătăŃiri.
Testele la stres sunt proiectate pentru a urmări comportarea sistemului atunci când acesta
cedează. Acest tip de testare evaluează limitările sistemului software.
Testele structurale sunt folosite pentru a verifica logica internă a sistemului.
Testele pe componente se efectuează asupra modulelor individuale, proceduri şi funcŃii.
Testele la integrare sunt proiectate pentru a testa comportarea sistemului pe măsură ce se
adaugă modulele componente.
Testarea interfeŃelor are ca obiectiv detectarea erorilor cauzate de interfeŃele dintre obiecte.
Pot apărea următoarele tipuri de erori: utilizare greşită a interfeŃei (parametri cu un alt tip sau într-o
altă ordine decât sunt specificaŃi în interfaŃă), înŃelegerea greşită a interfeŃei (presupuneri greşite
legate de ce ar trebui să facă o componentă), erori de timing (au loc în sistemele în timp real care
folosesc memorie partajată sau transmitere de mesaje).

5. Testarea funcŃionala (black box)


După cum am precizat anterior, testarea de tip black box nu necesită cunoaşterea codului
pentru realizarea unor teste semnificative. În continuare vom discuta despre câteva categorii
reprezentative ale acestei metode de testare cum ar fi testarea derivată din sintaxă, testarea bazată pe
tabele de decizie şi abordarea bazată pe grafuri cauză efect.
T5.1 Testarea derivată din sintaxă
Această clasă de teste funcŃionale se aplică sistemelor ale căror specificaŃii sunt descrise de o
anumită gramatică. Această constă, spre exemplu, în compilatoare şi clasificatori sintactici.
Deoarece specificarea formală a unor astfel de sisteme se exprimă prin reguli de producŃie,
generarea cazurilor de test urmăreşte un ca fiecare regulă de producŃie să fie aplicată (testată) cel
puŃin o dată.
Exemplu. Să considerăm o clasă de expresii aritmetice descrisă de următoarele reguli de producŃie:
<expresie>::=<expresie>+<termen>|<expresie>-<termen>|<termen>
<termen>::=<termen>+<factor>|<termen>-<factor>|<factor>
<factor>::=<identificator>|(<expresie>)
<identificator>::=a|b|c|d|e|...|z

Un set de cazuri de test pentru testarea derivată din sintaxă conŃine expresii ce verifică
regulile de mai sus. Un exemplu de expresie împreună cu regula corespunzătoare este dată în figura
următoare.
<expresie>::=<expresie>+<termen>
|
<expresie>-<termen>|
<termen>
<termen>::=<termen>+<factor>|
<termen>/<factor>|
<factor>
<factor>::=<identificator>|
(<expresie>)
<identificator>::=a|b\c|d|e|...|
z

Figura 2. Caz de test şi regulă de producŃie (subliniată)

4
5.2 Testarea bazată pe tabele de decizie
Această formă de testare prezintă interes atunci când cerinŃele produsului software sunt
formulate cu ajutorul regulilor de decizie de tipul „if-then”. Exemplele care se pretează la acest tip
de testare sunt sistemele bazate pe reguli. O regulă este de forma:
If cond1 and cond2 and cond3 and ... condn then actiune

Un tabel de decizie este compus dintr-un număr de coloane ce conŃin toate situaŃiile de test.
Partea superioară a coloanei conŃine condiŃiile ce trebuie satisfăcute. Partea de jos a tabelului
specifică acŃiunile care au loc dacă sunt satisfăcute condiŃiile din regulă. Un exemplu de tabel de
decizie este prezentat în figura 3.
CondiŃii
... 1
CondiŃia i 1
... 0
AcŃiuni
... 0
AcŃiunea j 1
... 0
Figura 3. Exemplu de tabel de decizie
Exemplu: Controlul nivelului de lichid. Să considerăm o problemă simplă de control care are
specificaŃiile exprimate sub forma unor tabele de decizie. Se consideră un rezervor de lichid cu doi senzori şi
două valve (figura 4). Senzorul 1 detectează un nivel maxim acceptabil al lichidului iar senzorul 2 detectează
un nivel minim acceptabil al lichidului. Fiecare senzor generează valoarea 1 logic dacă lichidul depăşeşte
nivelul corespunzător. Altfel, senzorii generează 0 logic. Valva de intrare se deschide doar dacă nivelul scade
sub limita minimă (senzorul 2 devine activ) iar valva de ieşire se deschide dacă senzorul 1 se activează. sunt
următoarele:
Reguli:
• Dacă senzorul 1 este activ (prea
mult lichid în rezervor), atunci se
deschide valva de ieşire.
• Dacă senzorul 2 este activ ( prea
puŃin nivel în rezervor) atunci se
deschide valva de intrare.

Figura 4. Rezervor de lichid


În plus, se adaugă o acŃiune de avertizare în momentul în care senzorul 2 furnizează rezultate
eronate. Tabelul de decizie complet are 22 = 4 coloane (figura 5A). Dacă nu interesează mesajele
de avertizare în caz de eroare, se poate reduce dimensiunea tabelului de decizie aşa cum se poate
observa în figura 5B.
În general, pentru „n” condiŃii se va ajunge la 2n combinaŃii, deoarece fiecare coloană a
tabelului trebuie folosită cel puŃin o dată. Prin urmare vor exista 2n cazuri de test. Se observă că şi
pentru valori mici ale lui „n”, tabelul de decizie ajunge la dimensiuni mari. Această creştere
exponenŃială se explică prin faptul că nu au fost luate în considerare constrângerile între variabilele
din condiŃii, constrângeri ce presupun limitări fizice şi conceptuale între variabile. A treia coloană
din tabelul de decizie din figura 5B poate fi astfel eliminată.

5
În secŃiunea următoare vom prezenta metoda de testare bazată pe grafuri cauză efect care
elimină neajunsul major al metodei bazate pe tabele de decizie, şi anume creşterea exponenŃială a
dimensiunii tabelelor odată cu creşterea numărului de reguli.

S1 0 0 1 1 CondiŃii S1 0 1 1 CondiŃii
S2 0 1 0 1 S2 0 0 1
Deschide 0 0 0 1 Deschide 0 0 1
valvă ieşire valvă ieşire
Deschide 1 0 0 0 AcŃiuni Deschide 1 0 0 AcŃiuni
valvă intrare valvă intrare
Trimite 0 1 0 0 Trimite
mesaj eroare mesaj eroare
Figura 5A. Tabel de decizie complet Figura 5B. Tabel de decizie redus
5.3 Testarea funcŃională bazată pe grafuri cauză-efect
Principalul dezavantaj al metodei bazate pe tabele de decizii este că intrările sunt luate în
considerare separat chiar dacă problema necesită o altă abordare. Garfurile cauză-efect iau în
considerare relaŃiile dintre combinaŃii specifice de intrări (cauze) şi ieşiri (efecte). Se evită în acest
mod creşterea exponenŃială a dimensiunii tabelelor de decizie. Cauzele şi efectele sunt reprezentate
ca noduri ale grafului. Un astfel de graf include un număr de noduri intermediare care leagă cauzele
şi efectele prin expresii logice.
Exemplu. Se consideră un bancomat (ATM) simplu. Lista cauzelor şi efectelor este următoarea:
Cauze Efecte
C1: comanda este „creditare” E1: tipăreşte „comandă nevalidă”
C2: comanda este „debitare” E2: tipăreşte „număr cont nevalid”
C3: numărul de cont este valid E3: tipăreşte „sumă debit nevalidă”
C4: tranzacŃia este validă E4: cont debit
E5: cont credit
Graful cauză-efect este prezentat în figura 6.
Nodurile intermediare realizează operatorii logici „and” şi „or”. Simbolul de negare ¬ stabileşte
faptul că efectul are valoarea de adevăr „true” doar dacă nodul asociat este „false”.
Graful cauză-efect ajută la determinarea cazurilor de test corespunzătoare. Aceste cazuri de test se
obŃin parcurgând în sens invers (de la efect la cauză) porŃiuni din graful prezentat în figura 6. dacă spre
exemplu suntem interesaŃi în determinarea cauzelor efectului E3 se urmăreşte porŃiunea de graf prezentată în
figura 7.

Figura 6. Graf cauză-efect Figura 7. Determinarea cauzelor


efectului E3

6
Se observă că C2, C3 şi ¬C4 determină E3, în timp ce cauza C1 nu influenŃează E3. Cauza C1 poate fi
privită drept o condiŃie de tip „nu contează”, ce va fi notată cu „x”. Această observaŃie reduce dimensiunea
tabelului de decizie corespunzător. Coloana din tabelul de decizie care va fi folosită la generarea cazurilor de
test este:
C C C C
1 2 3 4
X 1 1 0
Dacă nu se iau în considerare condiŃiile de tip „nu contează”, porŃiunea rezultată din tabelul de
decizie va conŃine 21 = 2 coloane ce implică o enumerare a valorilor cauzei C1.
Tabelul de decizie corespunzător grafului cauză-efect al unui ATM constă în cinci coloane cu un
număr substanŃial de condiŃii de tip „nu contează” (figura 8). Dacă nu ignorăm aceste condiŃii, va rezulta un
tabel cu 24 = 16 coloane.

C1 0 1 x x 1
C2 0 x 1 1 x
C3 x 0 1 1 1
C4 x x 0 1 1
E1 1 0 0 0 0
E2 0 1 0 0 0
E3 0 0 1 0 0
E4 0 0 0 1 0
E5 0 0 0 0 1
Figura 8. Tabel de decizie ATM

În general, dacă se doreşte generarea unui tabel de decizie redus, se va adopta un mecanism
de tip „backtracking” pentru traversarea unui graf de la efecte spre cauze (Ghezzi et al., 1991):
• La parcurgerea inversă a unui nod „or” a cărui ieşire este „true” se vor utiliza doar combinaŃii de
intrări care au valoarea de adevăr „true”. Dacă, spre exemplu, avem trei cauze (a, b, c) ce
afectează un nod de tip „or” a cărui ieşire este „true”, se vor considera doar combinaŃiile
<a=true, b=false, c=false>, <a=false, b=true, c=false>, <a=false, b=false, c=true>.
• La parcurgerea inversă a unui nod „and” a cărui ieşire este „false” se vor utiliza doar combinaŃii
de intrări care au doar o singură valoare „false”. Pentru exemplul precedent, se vor considera
combinaŃiile <a=false, b=true, c=true>, <a=true, b=false, c=true>, <a=true, b=true, c=false>.
În grafurile cauză-efect pot fi introduse constrângeri suplimentare între intrări, cum ar fi cele
de tipul „a implică b”. Acestea reduc numărul cazurilor de test deoarece unele combinaŃii de intrări
nu vor mai fi luate în considerare în procedura de testare.

6. CondiŃii de testare la limită


Testarea de tip white box, care necesită cunoaşterea codului, nu se poate face pentru cazurile
care nu sunt vizibile în mod explicit, sau care nu sunt suficient reliefate. Să considerăm spre
exemplu o instrucŃiune de tipul if (x>y) then S1 else S2. Această formă a unei instrucŃiuni
condiŃionale este generică şi se întâlneşte în multe tipuri de probleme. Să considerăm spre exemplu
doi senzori ale căror citiri (x şi y) sunt folosite pentru luarea unor decizii, notate S1 şi S2. CondiŃia
x>y determină două clase de echivalenŃă:
• E1 – clasa de echivalenŃă a valorilor lui x şi y pentru care x > y
• E2 – clasa de echivalenŃă a valorilor lui x şi y pentru care x ≤ y
Cele două clase de echivalenŃă sunt formate din perechi de valori citite (x,y) care determină
valoarea de adevăr a condiŃiei relaŃionale asociate. Criteriul de acoperire a ramurilor (care presupune
testarea tuturor ramurilor din program) duce la selectarea a două combinaŃii de intrări: una

7
provenind din E1 şi cealaltă din E2 (vezi figura 9). Se testează căile x > y şi x < y . Cazul x = y ,
care este parte a clasei de echivalenŃă E2, are o probabilitate nulă de a fi testat, deci nu va fi selectat
niciodată pentru testare. Cu toate acestea, cazul la limită x = y prezintă interes şi din acest motiv, se
poate prevedea o cale de realizare.

Figura 9. CondiŃia de testare la limită x = y

7. Testarea exhaustivă
Testarea exhaustivă face parte din metoda de testare black box. Cu toate că este
impracticabilă, ea furnizează o imagine asupra complexităŃii procesului de testare. Un test exhaustiv
trebuie să arate că programul funcŃionează corect pentru toate intrările posibile. Când ne referim la
intrări trebuie să avem în vedere şi aspectele hardware ale realizării codului.
Exemplu. Se consideră ecuaŃia de gradul doi de forma ax 2 + bx + c = 0 , unde a, b, c sunt
parametrii ecuaŃiei. Din punct de vedere funcŃional, programul transformă spaŃiul tridimensional al
parametrilor (a, b, c) într-un spaŃiu bidimensional al rădăcinilor ecuaŃiei (r1, r2). Testarea exhaustivă pleacă de
la reprezentarea internă a parametrilor. Dacă presupunem că reprezentarea internă este pe 16 biŃi, pentru
fiecare intrare vom avea 216 cazuri de test. Considerând toate cele trei intrări, vom avea în final 248 cazuri
de test ce trebuie executate, ceea ce este nefezabil.

8. Testarea structurală
Testarea structurală presupune dezvoltarea de cazuri de test ce iau în considerare structura
codului (logica internă). Există mai multe categorii de testarea structurală, funcŃie de cât de complet
trebuie să fie procesul de testare şi de restricŃiile de timp.
Testarea la nivel de instrucŃiune este o formă de testare primară, care necesită executarea
fiecărei instrucŃiuni cel puŃin o dată.
Exemplu. Se consideră următoarea porŃiune de cod care se presupune că determină modulul lui y.
begin
if (y ≥ 0) then y = 0-y; abs = y;
end;

Cazul de test y = 0 este suficient pentru a testa toate instrucŃiunile din porŃiunea de cod prezentată.
Este evident faptul că această formă de testare nu detectează o eroare evidentă a codului şi anume aceea că nu
se calculează de fapt modulul numărului y.

Testarea la nivel de ramură. Cazurile de test sunt alese astfel încât fiecare ramificaŃie a
codului să fie executată cel puŃin o dată.
Exemplu. Dacă ne referim la instrucŃiunea anterioară, cazurile de test y = 0 şi y = −4 sunt
suficiente pentru a executa ambele variante ale blocului de decizie. Pentru y = 0 ⇒ abs = 0 . Pentru

8
y = −4 ⇒ abs = −4 , ceea ce indică o eroare. Indiscutabil, această metodă de testare conduce la detectarea
erorilor.

Testarea la nivel de ramură/condiŃie. Fiecare ramură trebuie parcursă cel puŃin o dată şi
toate combinaŃiile posibile ale condiŃiilor de decizie trebuie executate.
Exemplu. Se consideră instrucŃiunea:
if ((x<val_1) && (y>val_2)){z=calculeaza_1(x,y);
else z= calculeaza_2(x,y);}

Să considerăm cazurile de test x = −4, y = 10 , x = −6, y = 12 . În primul caz, blocul de decizie


returnează „false” şi atunci se execută o ramură a codului, iar în cel de-al doilea caz, blocul de decizie
returnează „true” şi atunci se execută cealaltă ramură a codului. Se observă faptul că, dacă o eroare este
asociată cu o condiŃie compusă dintr-un bloc de decizie, ea rămâne nedetectată. Din acest motiv, testarea
trebuie completată cu verificarea tuturor subcondiŃiilor ce apar în blocul de decizie. Deoarece în exemplul
prezentat avem două subcondiŃii în blocul de decizie, trebuie adăugate două perechi suplimentare de teste şi
anume („true”, „false”) respectiv („false”, „true”).
ObservaŃie. Dacă fiecare subcondiŃie este privită ca o intrare singulară, atunci acest tip de
testare este analogă testării exhaustive, „n” condiŃii necesitând 2n cazuri de test. În cazul în care
„n” creşte, testarea poate deveni nefezabilă.
Testarea la nivel de cale. Criteriul de acoperire a căilor consideră toate căile logice dintr-un
program şi duce la cazuri de test care verifică programul de-a lungul fiecărei astfel de căi. Acest
criteriu poate deveni impracticabil, mai ales în situaŃiile în care programul are un număr mare de
căi. Cu toate acestea, este o metodă de testare folosită, mai ales pentru că permite detectarea
defectelor nedescoperite prin testarea la nivel de ramură.
Exemplu. Se consideră schema logică din figura 10.
Dacă se urmăreşte o testare la nivel de ramură se
pot alege următoarele cazuri de test:
x = 1, y = 5 , x = 0, y = 12
Aceste două cazuri de test acoperă toate ramurile
schemei logice (vezi figura 11), dar execuŃia lor corectă
nu garantează lipsa erorilor.
Să considerăm situaŃia în care la un anumit moment
x = 0 . În acest moment raportul y / x eşuează. Acest
scenariu nu a fost luat în considerare prin cazurile de
test anterioare.
Figura 10. Exemplu de schemă logică

Figura 11. Testarea la nivel de ramură

9
Testarea la nivel de cale evită situaŃia prezentată anterior. Se pot considera următoarele cazuri de
test:
x = 1, y = 5 ; x = 2, y = 15 ; x = 0, y = 7 ; x = 0, y = 13 .

Figura 11. Testarea la nivel de cale

ObservaŃie. Se observă că şi în cazul unui exemplu relativ simplu, numărul de căi ce trebuie
parcurse este mai mare decât numărul ramurilor.
Din cauza complexităŃii principiului acoperirii căilor este esenŃială numărarea căilor dintr-un
program.
În cazul lipsei buclelor, numărul căilor este determinat de numărul de noduri de decizie şi
de distribuŃia lor. Cele două extreme ale numărului de căi dintr-un graf se pot observa în exemplul
din figura 12. În cazul în care se unesc ramurile, blocurile de decizie sunt „stivuite” unele peste
altele (figura 12a). Unirea ramurilor poate duce la 2n căi posibile. Numărul maxim de căi dintr-un
graf este deci 2n . În cazul în care nu se face unirea ramurilor există n + 1 căi posibile (vezi figura
12b), deci:
n + 1 ≤ număr de căi ≤ 2n
Este de remarcat faptul că şi în cazul valorilor mici pentru „n”, între cele două limite există o
diferenŃă semnificativă. Pentru a micşora această diferenŃă se poate face o descompunere a grafului
original în părŃi mai mici şi se determină extremele separat. Să considerăm spre exemplu un graf cu
6 noduri de decizie. În acest caz limitele ar fi 7 şi 64. Dacă se împarte graful original în trei părŃi,
fiecare conŃinând 2 noduri, limitele pentru fiecare parte ar fi 3 şi 4. Numărul de căi de testat în
graful original este cuprins între 27(= 3*3*3) şi 64(= 4* 4* 4) .

10
(a) ramuri unite (b) ramuri neunite
Figura 12. Cazuri de acoperire a căilor
În cazul grafurilor ce conŃin bucle, acoperirea căilor va fi restricŃionată la bucle ce sunt
traversate doar o singură dată. Utilizând această abordare, se poate transforma orice graf cu o buclă
la unul fără nici o buclă. Ideea este de a împărŃi orice nod, să zicem A, care este nod terminal al unei
căi cu reacŃie în A şi A’. Noul nod este conectat cu nodul final sau cu orice alt nod de mai jos.
ConstrucŃiile de bază ale programării structurate pot fi transformate în grafuri fără bucle (figura 13).
Enumerarea căilor va fi dată de sumele produselor limitelor grafului fără bucle.
Exemplu. Să considerăm graful din figura 14. Se porneşte cu frontierele care fac parte din toate
căile posibile. Acestea sunt, evident, a şi h. Atunci:
FGC = aFGCα h ,
unde FGC este funcŃia de generare a căii iar α este funcŃia de generare a căii pentru sub-graful dintre B şi E.
În continuare se construieşte funcŃia de generare a căii pentru α, FGCα:

Figura 13. Transformarea schemei logice în grafuri fără bucle


FGCα = de + bFGCβ ,
unde semnul „+” se foloseşte pentru operaŃia „sau”. Pentru sub-graful cu nodurile C, B’, E’ şi E se
obŃine:
FGCβ = f + cbf .
Revenind la expresia originală se obŃine:

11
FGC = aFGCαh = a(de + bFGCβ )h = a(de + b(f + cbf ))h = adeh + abfh + abcbfh

Figura 14. Conversia unui graf într-un graf echivalent fără bucle

Se poate observa că numărul de căi poate creşte foarte mult, mai ales pentru porŃiunile de
cod cu fluxuri de control semnificative. Acest fapt limitează aplicabilitatea acestei metode de
testare.
Criteriul de testare a căilor nu poate detecta toate defectele, deoarece se bazează pe faptul că
specificaŃiile software sunt transformate cu exactitate în cod. Lipsa unor elemente îngreunează
semnificativ testarea.

9. Testarea regresivă
Scopul acestei metode de testare este de crea posibilitatea repetării testelor în momentul în
care apare o modificare a produsului software. Există două activităŃi principale ale testării regresive:
• Stabilirea unui test (sau a unei serii de teste) pentru reluare. Regula este să se meargă pe
teste puternice (şi anume acele teste cu nivel mare de acoperire).
• Compararea ieşirilor noi cu cele vechi (de bază) pentru a fi siguri că nu există schimbări
nedorite.
Pentru testarea regresivă efectivă, trebuie realizate prelucrări suplimentare ale seriilor de
test.
Eficacitatea testării regresive se exprimă în termenii următoarelor condiŃii: (a) cât de grea
este construirea şi menŃinerea seriilor de test şi (b) cât de fezabil este sistemul de testare regresivă.

10. Testarea software statică


Testarea statică, spre deosebire de cea dinamică, nu se concentrează asupra execuŃiei
codului. Există două variante de tehnici de testare statică:
• tehnici informale care includ trecerile rapide prin cod şi inspectarea propriu zisă a
codului;
• tehnici formale ce cuprind demonstraŃii de corectitudine şi execuŃia simbolică.
10.1 Tehnici informale
Tehnicile informale au fost introduse la sfârşitul anilor ’70 şi diferă prin faptul că, dacă
inspectările softului sunt orientate pe reacŃie, trecerile prin cod se bazează pe interacŃiunea dintre
testeri, membrii echipei proiectului şi alte persoane implicate în proiect.
Inspectările sunt activităŃi realizate de o echipă experŃi care verifică proiectul sau codul cu
scopul de a identifica erorile. Au fost propuse în 1976 de Fagan şi au fost dezvoltate de IBM. O
inspectare conŃine cinci paşi principali:
• Privirea de ansamblu asupra documentelor ce trebuie inspectate
• Pregătirea. ParticipanŃii încearcă să înŃeleagă documentul în detaliu. Tipurile de erori şi
frecvenŃa acestora găsite în inspecŃiile recente sunt utile pentru ca atenŃia să se concentreze asupra
ariilor predispuse în măsură mai mare la erori

12
• Inspectarea. Pentru început, un participant parcurge codul împreună cu echipa de
inspecŃie, asigurându-se că fiecare problemă este acoperită şi că fiecare ramură e parcursă cel puŃin
o dată. Fiecare participant continuă cu propria listă de verificare. Scopul este de a găsi şi documenta
erorile, nu de a le înlătura. Liderul echipei de inspecŃie (moderatorul) are ca sarcini pregătirea
raportului scris şi asigurarea unei rezolvări potrivite
• Refacerea. Erorile şi problemele din raportul scris sunt rezolvate
• Continuarea. Moderatorul se asigură că toate problemele ridicate au fost rezolvate. Toate
rezolvările trebuie verificate pentru a fi siguri că nu au fost introduse noi erori. În cazul în care mai
mult de 5% din materialul inspectat a fost refăcut, echipa se reuneşte pentru o nouă inspectare.

Echipa responsabilă cu inspectarea este formată în general din 3-6 persoane. Spre exemplu,
dacă se inspectează proiectul, echipa va conŃine un moderator, un proiectant, un dezvoltator şi un
tester. Fiecare inspecŃie trebui să utilizeze o listă de verificare a defectelor potenŃiale (cum ar fi
corespondenŃa între specificaŃii şi proiectare, între parametrii actuali şi cei formali ai interfeŃelor,
compatibilitatea softului cu hardware-ul disponibil etc.). Rezultatele inspecŃiei sunt înregistrate
funcŃie de nivelul de severitate şi tip. Pentru ca inspecŃia să aibă succes ea trebuie să aibă un caracter
iterativ. În cazul inspectării codului activităŃile au scopul de a descoperi erorile obişnuite sau pe cele
provenind din clase specifice cum ar fi:
• Variabile neiniŃializate;
• Salturile în interiorul buclelor;
• Bucle fără condiŃie de terminare;
• Indexarea vectorilor în afara limitelor;
• Nepotriviri între parametrii actuali şi cei formali.
• Trecerea rapidă prin cod presupune organizarea unor întâlniri de lucru pentru analizarea
unui produs software, unui modul de cod, unui plan de testare etc. AcŃiunea este condusă de un
persoană ce face o prezentare a produsului ce trebuie analizat. Erorile sunt înregistrate de un
coordonator. Grupul de lucru se concentrează pe detecŃia erorilor, nu pe corecŃia lor. În general, o
astfel de echipă este formată din următorii membri: un prezentator al problemelor ce trebuie
revăzute, un coordonator al echipei de lucru, un secretar ce se ocupă cu pregătirea tuturor
materialelor necesare, un reprezentant al persoanelor ce se ocupă cu întreŃinerea produsului, o
persoană responsabilă cu îndeplinirea standardelor interne şi un reprezentant al clientului.
10.2 Tehnici formale
Tehnicile formale constau în demonstraŃii de corectitudine şi execuŃie simbolică.
DemonstraŃiile de corectitudine se bazează pe observaŃia că orice porŃiune de cod (program)
este un obiect formal, în timp ce orice specificaŃie poate fi realizată printr-o metodă formală.
Aceasta înseamnă că se pune accentul pe echivalenŃa dintre program şi specificaŃii. EchivalenŃa
poate fi pusă în evidenŃă printr-o demonstraŃie matematică.
ExecuŃia simbolică utilizează simboluri în locul valorilor numerice, adică se bazează pe
clase de date. Pentru a ilustra esenŃa acestei idei să considerăm următorul exemplu:
x = 7*y;
if (x>a) then a = a+x-3 else y = sin(x/2);
b = x+a*y;

În figura 15 literele mari cum ar fi X sau Y semnifică valori simbolice, acestea corespunzând
variabilelor originale utilizate în cod). Să presupunem că la începutul execuŃiei simbolice, x = X şi a = A .
Prima instrucŃiune duce la X = 7 * Y . Celelalte variabile rămân neschimbate. Blocul de decizie poate fi
activat pe oricare din cele două direcŃii (comparaŃia 7 * Y > A poate fi adevărată sau falsă). Vom selecta
unul din cele două fire de execuŃie ca un triplu de forma:
<valori variabile simbolice, cale de execuŃie, condiŃie>

13
Cele două căi de execuŃie sunt asociate grafului de control al fluxului.

Figura 15 Exemplu de execuŃie simbolică

11. Testarea la integrare


Componentele majore rezultate din scrierea codului se integrează formând sistemul final.
Stilul de testare ales depinde de strategia aleasă pentru proiectarea sistemului software. În
cazul strategiei de proiectare top-down (care presupune o serie de rafinări succesive) se utilizează
module înlocuitoare sau substitute (stubs) care au rolul de a emula modulele ce nu au fost dezvoltate
încă (vezi figura 16). În cazul strategiei de proiectare bottom-up sistemul se dezvoltă plecând de la
module detaliate, implementând funcŃii din ce în ce mai complexe. Se utilizează modulele driver
pentru reprezentarea modulelor de nivel mai înalt ale căror nume le poartă (vezi figura 17).

Figura 16. Strategia top-down de Figura 17. Strategia bottom-up de


testare a sistemului testare a sistemului
Plecând de la strategiile de proiectare se pot defini următoarele strategii de testare la
integrare:
• Testarea bottom-up – pleacă de la module detaliate şi se extinde spre nivele mai înalte
ale ierahiei; această testarea necesită drivere.
• Testarea top-down - porneşte de la modulul de pe cel mai înalt nivel şi necesită
substitute.
• Testarea big-bang - modulele se integrează într-un singur pas iar testarea se face asupra
întregului sistem.
• Testarea în straturi suprapuse - combină testarea bottom-up şi top-down definind un
anumit strat Ńintă în ierarhia modulelor; modulele inferioare stratului Ńintă se testează printr-o
abordare bottom-up iar cele superioare printr-o metodă top-down.
ObservaŃie. Există situaŃii în care unele module nu sunt testate separat la testările bottom-up şi top-
down.

14
Exemplu. Pentru a ilustra aceste
idei de testare, să considerăm o
ierarhie simplă de module ca în figura
18.
În figura 19 sunt prezentate
clasele de testare la integrare pentru
Figura 18. Ierarhie de module exemplul anterior.
software

Testare bottom-up Testare top-down

Testare în straturi
Testare big-bang
Stratul de test este situat între
A şi (B, C, D)
Figura 19. Exemple de testări la integrare

15
Estimarea costurilor unui sistem software
Estimarea costurilor necesită o atenŃie specială în cadrul procesului de dezvoltare a unui
proiect software. łinând cont de faptul că un produs software este unic, experienŃa unor proiecte
anterioare trebuie utilizată cu precauŃie. CircumstanŃele în care se realizează noul produs pot fi
diferite, plecând de la specificaŃii noi, alte platforme hardware şi software şi continuând cu noi
aplicaŃii de dezvoltare şi metodologii de proiectare.
Odată cu creşterea complexităŃii sistemelor software, orice estimare poate duce la o alocare
inadecvată a resurselor. Resursele limitate determină întârzierea proiectului, iar alocarea
supradimensionată duce la o scădere substanŃială a profitului.
În practică se iau în considerare trei valori pentru costuri: valoarea cea mai probabilă, limita
maximă şi limita minimă. Aceste valori reflectă factorul de incertitudine în ceea ce priveşte
estimarea costului unui proiect software. În general, nivelul de incertitudine scade pe parcursul
dezvoltării produsului.
Necesitatea introducerii de modele pentru estimarea costului este justificată de un număr de
cerinŃe comune ale proiectelor software:
• Identificarea, stabilirea priorităŃilor şi justificarea resurselor necesare.
• Negocierea bugetului adecvat şi stabilirea echipei proiectului.
• Optimizarea costului, productivităŃii, calităŃii şi funcŃionalităŃii.
• Nevoia de cuantificare a impactului riscului.
• Stabilirea impactului schimbărilor.
• Modificarea bugetelor în funcŃie de apariŃia unor evenimente neprevăzute.
Există doi predictori principali ai costului unui proces software: efortul aşteptat şi timpul
consumat pentru realizarea acestuia. Metodele de estimare a costului întâlnite în practică sunt
următoarele:
1. Opiniile experŃilor – se bazează pe experienŃa experŃilor acumulată în dezvoltarea altor
proiecte software;
2. Analogia. Cheia acestei metode este de a realiza judecăŃi bazate pe proiecte anterioare, de
a utiliza estimările obŃinute în proiecte anterioare şi de ale aplica în proiectul curent. Estimarea
iniŃială este ajustată pe baza diferenŃei dintre proiectele anterioare şi cel curent. Rezultatul estimării
depinde de posibilitatea cuantificării nivelului de similaritate dintre proiectele anterioare şi cel
curent. Judecarea greşită a similarităŃilor şi diferenŃelor poate fi o sursă a estimărilor eronate.
3. Descompunerea. Această metodă presupune împărŃirea proiectului în sub-taskuri,
estimarea costului componentelor şi sumarea acestora într-o estimare generală. Sumarea se face
printr-o ponderare ce Ńine cont de complexitatea componentelor.
4. Modele PERT (Program Evaluation and Review Technique- Tehnica evaluării repetate a
programului). Efortul necesar se estimează plecând de la cea mai bună valoare posibilă, cea mai rea
valoare posibilă şi cea mai probabilă valoare conform următoarei formule:
A + 4⋅M + B
efort = , (1)
6
unde efort = nr ore muncă/activitate calculat în ipoteza că un singur om este alocat;
A = estimare pesimistă;
B = estimare optimistă;
M = estimarea cea mai frecventă.
Metoda permite compensarea riscului prin realizarea unei estimări avantajoase. Estimările A,
B şi M se realizează fie prin metode analogice, fie prin metoda Delphi, metode ce vor fi discutate
ulterior.
5. Modele matematice. Cele mai cunoscute modele din această categorie sunt COCOMO
(COnstructive COst Model – Modelul constructiv al costurilor), modele bazate pe curba Rayleigh şi

1
modele bazate pe analiza punctelor funcŃionale. Parametrii acestor modele se determină prin metode
statistice.

1. Modele bazate pe analiza punctelor funcŃionale


Această clasă de modele, ce are la bază punctele funcŃionale ale lui Albrecht, este în strânsă
legătură cu documentul de specificare a cerinŃelor, în care este determinată funcŃionalitatea
sistemului software. În esenŃă, această metodă de estimare a costurilor pleacă de la acele
caracteristici ale sistemului care pot conduce la obŃinerea unui rezultat bun. IntenŃia este de a
construi o măsură a dimensiunii proiectului ce poate fi disponibilă încă de la începutul procesului de
dezvoltare. Metoda este independentă de tehnologie. Pentru a putea construi un astfel de model se
identifică numărul de puncte cheie ce apar în sistem, care sunt în mod obişnuit derivate din
documentul de specificare a cerinŃelor şi anume:
• Intrările externe (IE) sunt intrările de la utilizator care furnizează date distincte orientate
pe aplicaŃie. Exemple de astfel de intrări sunt numele de fişiere şi selecŃiile din meniuri.
• Ieşirile externe (OE) sunt orientate spre utilizator; acestea sunt sub formă de rapoarte şi
mesaje.
• Cererile externe (CE) sunt intrări interactive ce necesită un răspuns.
• Fişierele externe (FE) asigură interfaŃa cu alte sisteme; utilizatorul nu este responsabil de
întreŃinerea datelor.
• Fişierele interne (FI) sunt fişierele principale ale sistemului; permit utilizatorilor să
folosească datele pe care trebuie să le întreŃină
Cu toate că aceste caracteristici ale sistemului software sunt definite mai degrabă informal,
identificarea lor nu este dificilă când se lucrează cu sisteme specifice. Pentru a ajusta nivelele de
complexitate pentru fiecare sub-funcŃie, se face o evaluare a complexităŃii. Această evaluare conŃine
trei nivele de complexitate: simplu, mediu şi complex.
În funcŃie de complexitatea tipurilor de date, se disting o serie de valori pentru aceste puncte
funcŃionale, prezentate mai jos:
Nivel de complexitate (pondere)
Tip
Simplu Mediu Complex
IE 3 4 6
OE 4 5 7
CE 3 4 6
FE 7 10 15
FI 5 7 10
Se observă faptul că fişierele externe au cea mai mare pondere. Fişiere interne au, de
asemenea, o contribuŃie semnificativă la complexitatea programului. Ieşirile externe, intrările
externe şi cererile externe au un nivel scăzut în această schemă. Acest nivel de evaluare a fişierelor
externe şi interne este o consecinŃă a ponderii mari pe care o au integrarea modulelor unui sistem şi
interfaŃarea cu alte sisteme în cadrul dezvoltării proiectului software.
Într-o primă fază se determină numărul de puncte funcŃionale neajustate (PFN) folosind
următoarea relaŃie:
PFN = ∑ tipi ⋅ ponderei . (2)
În faza a doua, numărul de puncte funcŃionale se rafinează folosind aşa numitul factor de
complexitate tehnică (FCT) care multiplică valoarea iniŃială a PFN producând numărul de puncte
funcŃionale ajustate (PFA):
PFA = PFN ⋅ FCT . (3)
Factorul de complexitate tehnică se calculează cu următoarea formulă obŃinută pe cale
experimentală:

2
14
FCT = 0.65 + 0.1∑ ci , (4)
i =1

unde ci reprezintă următoarele caracteristici detaliate: funcŃii distribuite, comunicaŃii de date,


performanŃă, intrări de date on-line, actualizări on-line, prelucrări complexe, uşurinŃă la instalare,
facilităŃi de schimbare, refolosire, amplasări multiple, interfaŃă complexă, configuraŃii utilizate
masiv, utilizare operaŃională, capacitate de recuperare. InfluenŃa fiecărei caracteristici este evaluată
pe o scară de la 0 (nerelevantă) la 5 (influenŃă mare). Gradul de influenŃare este suma acestor puncte
pentru toate caracteristicile.
ObservaŃie. Factorul de complexitate tehnică poate varia între 0.65 şi 1.35, deci o variaŃie de ±35%
faŃă de valoarea nominală.
Exemplu. Se consideră un regulator care acceptă două intrări discrete ce provin de la un traductor
de temperatură şi de la unul de debit. În cazul în care semnalele eşantionate provenite de la traductoare sunt
între nişte limite admisibile, regulatorul furnizează comenzi adecvate pentru reglarea celor două mărimi. În
cazul în care măsura depăşeşte limitele impuse, trebuie activat un scenariu de alarmă; valorile parametrilor
regulatorului în caz de alarmă se preiau dintr-un fişier separat (fişier alarmă). Acest fişier mai conŃine şi alte
versiuni ale valorilor parametrilor regulatorului astfel încât să se poată aplica o comandă adecvată. Valorile
citite de la traductoare şi comenzile aplicate sunt arhivate într-un fişier. Operatorul uman este informat în
permanenŃă despre starea sistemului (valorile intrărilor, comenzile aplicate, informaŃii auxiliare cum ar fi
dispersia semnalelor etc.). Se furnizează de asemenea un raport separat despre cazurile de alarmă.
Analiza specificaŃiilor de mai sus duce la o schiŃă a funcŃionalităŃii sistemului ce poate fi observată
în figura 1. Se identifică următoarele puncte funcŃionale:
A – numărul de intrări externe = 2 (temperatură, debit);
B – numărul de ieşiri externe = 4 (raport alarmă, stare sistem, comandă, citiri traductoare);
C- număr de cereri externe = 0;
D – număr de fişiere externe = 1 (arhivă);
E – număr de fişiere interne =1 (fişier alarmă).

Figura 1. FuncŃionalitatea sistemului de reglare


Vom considera pentru început că fiecare tip are complexitate medie. În acest caz se obŃine:
PFN = 4A + 5B + 4C + 10D + 7E = 8 + 20 + 0 + 10 + 7 = 45 . (5)
În cazul în care se consideră cele două limite de complexitate se obŃin următoarele valori pentru
punctele funcŃionale neajustate:
PFN = 3A + 4B + 3C + 7D + 5E = 6 + 16 + 0 + 7 + 5 = 34 , (6)
respectiv PFN = 6A + 7B + 6C + 15D + 10E = 12 + 28 + 0 + 15 + 10 = 65 . (7)
Dacă se presupune că un punct funcŃional necesită un efort de 2 persoane / zi , atunci cele două
limite se convertesc într-un domeniu de timp de la 68 la 130 zile. În cazul unei complexităŃi medii, va fi
nevoie de 90 de zile pentru implementare.
ObservaŃii:
• Modelul punctelor funcŃionale are avantajul că poate fi folosit încă din faza de pornire a
proiectului, deoarece se bazează pe documentul specificaŃiilor software.

3
• Rezultatele furnizate de acest model pot subestima realitatea deoarece în documentul
specificaŃiilor software nu este elaborat la nivel de detalii de implementare.
• Abordarea bazată pe puncte funcŃionale nu se poate aplica în cazul în care se utilizează
mediile de dezvoltare integrate de tipul CASE, în cazul programării orientate obiect sau reutilizării
librăriilor.

2. Modelul COCOMO
Modelul COCOMO (COnstructive COst Model – Modelul constructiv al costurilor) este cel
mai bun şi documentat model utilizat în estimarea efortului. COCOMO a fost dezvoltat de Boehm
pe baza analizei a 63 de proiecte software. Modelul furnizează o formulă detaliată de determinare a
orarului dezvoltării proiectului, a efortului de dezvoltare general, de descompunere pe faze şi
activităŃi precum şi a efortului de întreŃinere. COCOMO estimează efortul în luni-om. Estimarea se
bazează pe numărul de linii de cod (SLOC – source lines of code) exprimat în mii de instrucŃiuni de
cod livrat (KDSI – thousands of delivered source instructions). Aceste instrucŃiuni includ toate
instrucŃiunile programului, instrucŃiunile de formatare, declaraŃiile de date. Nu sunt numărate
comentariile. Modelul COCOMO presupune că modelul de dezvoltare ales pentru proiectul
software este cel în cascadă şi că proiectul are asigurat un management performant.
Modelul COCOMO este dezvoltat în trei versiuni, în funcŃie de nivelul de rafinare a
estimării: de bază, intermediar şi detaliat. În cele ce urmează vom discuta despre primele două.
Boehm ia în considerare trei clase de siteme:
1. Încorporate. Aceste sisteme sunt caracterizate de constrângeri severe. Produsul este
integrat într-un mediu strict şi are constrângeri de timp, fiind relativ nou pentru companie. Un
exemplu tipic este cel al sistemelor de timp real (în aeronautică, medicină etc.).
2. Organice. Această clasă cuprinde sistemele ce sunt dezvoltate de echipe mici, într-un
mediu cunoscut, cu o interfaŃă relaxată. În această categorie intră proiectele relativ mici.
3. Semidetaşate. Sistemele software din această clasă sunt intermediare celor două
prezentate anterior. Exemple de astfel de sisteme sunt sistemele de operare şi sistemele de
management al bazelor de date.

2.1 Modelul COCOMO de bază


În forma de bază a modelului COCOMO se pleacă exclusiv de la dimensiunea programului
exprimată în KDSI, pe care o vom nota cu KDLOC (kilo delivered lines of code). Legătura între
efort şi dimensiunea programului este dată de:
Efort = a ⋅ KDLOCb , (8)
unde a şi b sunt parametrii modelului, ale căror valori depind de clasa din care sistemul software
face parte, după cum se poate observa în tabelul 1:
Tabel 1. Valorile parametrilor pentru COCOMO de bază
Clasa de sisteme a b
Încorporate 3.6 1.2
Organice 2.4 1.05
Semidetaşate 3.0 1.12
ObservaŃii:
• Forma funcŃiei din (8) precum şi valorile parametrilor sunt obŃinute experimental, pe baza
unor proiecte software anterioare. Din acest motiv, în cazul în care proiectul în lucru diferă foarte
mult de proiectele pe baza cărora s-a realizat modelul COCOMO, s-ar putea folosirea acestui model
să nu fie relevantă.
• În reprezentarea grafică din figura 2 se poate observa că pentru valori mari ale KDLOC
apar diferenŃe semnificative între cele trei clase de sisteme prezentate.

4
Modelul COCOMO determină de asemenea orarul de dezvoltare al proiectului, M (exprimat
în luni) folosind efortul calculat anterior:
M = c ⋅ Efort d , (9)
unde parametrii c şi d au valorile precizate în tabelul 2.
ObservaŃiile anterioare rămân valabile şi în cazul determinării orarului de dezvoltare a
proiectului. În figura 3 este prezentată dependenŃa orarului de numărul de linii de cod sursă livrate.
Tabel 2. Valorile parametrilor orarului de dezvoltare
Clasa de sisteme c d
Încorporate 2.5 0.32
Organice 2.5 0.35
Semidetaşate 2.5 0.32
COCOMO se foloseşte şi pentru estimarea costurilor de întreŃinere. Formula se bazează pe
eforturile anterioare estimate:
Efort int retinere = TMA ⋅ Efort , (10)
unde TMA este traficul de modificare anual, adică acea fracŃiune din KDLOC care se schimbă pe
parcursul unui an.

Figura 2. DependenŃa efort(KDLOC)

Figura 3. DependenŃa orar(KDLOC)

5
2.2 Modelul COCOMO intermediar
Acest model, cel mai des utilizat, se obŃine prin rafinarea modelului de bază. ÎmbunătăŃirea
constă în luarea în considerare a 15 atribute ale produsului. Pentru fiecare astfel de atribut,
utilizatorul modelului trebuie să precizeze o pondere din gama: foarte mică (engl. very low, VL),
mică (engl. low, LO), nominală (NM), mare (engl. high, HI), foarte mare (engl. very high, VH) şi
extrem de mare (engl. extra high, XH).
Lista atributelor este compusă din caracteristici ale produsului, sistemului de calcul,
personalului şi proiectului.
Atributele produsului:
• Fiabilitate cerută (RELY). Este utilizată pentru a exprima efectul defectelor software,
într-o gamă de la inconvenient minor (VL) la defect major (VH).
• OcteŃi de date pe instrucŃiune sursă livrată(DATA).
• Complexitate (CPLX). Atributul exprimă complexitatea codului, de la un cod simplu
(VL) la un cod de timp real (XH).
Atributele sistemului de calcul:
• Timp de execuŃie (TIME) şi constrângeri de memorie (STOR). Acest atribut identifică
procentul de resurse ale calculatorului (timp şi memorie) utilizate de sistem. În starea nominală
procentul este mai mic de 50% iar ponderea extrem de mare este dată de un procent de 95%.
• Volatilitatea platformei de dezvoltare (VIRT) este utilizată pentru a indica frecvenŃa
schimbărilor hardware-ului, sistemului de operare şi mediului de programare. Schimbările frecvente
şi semnificative au o pondere mai mare.
• Timp de răspuns (TURN) reprezintă timpul scurs de la începerea aplicaŃiei până la
obŃinerea rezultatelor. LO indică un mediu cu interactivitate mare iar VH cuantifică situaŃia în care
timpul este mai mare de 12 ore.
Atributele personalului
• Capacitatea analiştilor (ACAP) şi a programatorilor (PCAP) descriu aptitudinile echipei
de dezvoltare.
• ExperienŃa în domeniul aplicaŃiei (AEXP), în limbajul de programare (LEXP) şi în
mediul de dezvoltare (VEXP). Sunt utilizate pentru a cuantifica experienŃa echipei de dezvoltare în
domeniile precizate anterior.
Atributele proiectului
• Practicile de dezvoltare moderne (MODP) sunt legate de utilizarea practicilor software
moderne cum ar fi programarea structurată şi abordarea orientată obiect.
• Utilizarea aplicaŃiilor software automate (TOOL). Acestea sunt folosite pentru măsurarea
ponderii pe care respectivele aplicaŃii le au în dezvoltarea proiectului şi la integrare. O pondere mare
presupune nivele înalte în ambele aspecte precizate.
• Modificări ale orarului planului de dezvoltare (SCED) ce constau în comprimări (HI sau
VH) sau extindere (LO sau VL) în raport cu orarul nominal (NM).
În tabelul 3 sunt prezentate ponderile caracteristicilor descrise anterior. În funcŃie de proiect,
aceste rezultate parŃiale se înmulŃesc, obŃinându-se multiplicatorul final (P). Efortul se exprimă cu
formula:
Efort = Efort no min al ⋅ P , (11)
unde efortul nominal are diferite expresii, funcŃie de tipul de sistem:
• în cazul sistemelor încorporate: Efort no min al = 2.8KDLOC1.20 ;
• în cazul sistemelor semidetaşate : Efort no min al = 2.8KDLOC1.12 ;
• în cazul sistemelor organice: Efort no min al = 2.8KDLOC1.05 .

6
ObservaŃii:
• relaŃiile anterioare sunt asemănătoare celor din modelul de bază, diferenŃa apărând la
parametrul a, parametrul b rămânând nemodificat;
• dacă atributul are valoare nominală, nu influenŃează multiplicatorul P;
• unele atribute se pot compensa: creşterea valorii unor atribute este anihilată de scăderea
valorii altor atribute;
• estimarea timpului de dezvoltare a produsului este aceeaşi ca în modelul COCOMO de
bază;
• efortul de întreŃinere se calculează cu formula:
Efort int retinere = TMA ⋅ Efort no min al ⋅ P . (12)

Tabelul 3. Atributele modelului COCOMO intermediar


VL LO NM HI VH XH
RELY 0.75 0.88 1.00 1.15 1.40
DATA 0.9 1.00 1.08 1.16
CPLX 0.70 0.85 1.00 1.15 1.30 1.65
TIME 1.00 1.11 1.30 1.66
STOR 1.00 1.06 1.21 1.56
VIRT 0.87 1.00 1.15 1.30
TURN 0.87 1.00 1.07 1.15
ACAP 1.46 1.19 1.00 0.86 0.71
AEXP 1.29 1.13 1.00 0.91 0.82
PCAP 1.42 1.17 1.00 0.86 0.70
LEXP 1.14 1.07 1.00 0.95
VEXP 1.21 1.10 1.00 0.90
MOD 1.24 1.10 1.00 0.91 0.82
P
TOOL 1.24 1.10 1.00 0.91 0.83
SCED 1.23 1.08 1.00 1.04 1.10

Exemplu. Să considerăm un proiect software cu o dimensiune estimată la 300 KDLOC. Proiectul


este parte a unui sistem de control a unui autovehicul, sistem ce preia informaŃii de la diferiŃi senzori, le
procesează şi iniŃiază acŃiuni de control. Evident proiectul face parte din clasa de sisteme încorporate.
Folosind modelul COCOMO de bază, obŃinem Efort = 3.6 ⋅ 3001.20 = 3379 luni-om. Timpul de dezvoltare
este M = 2.5 ⋅ 33790.32 = 33.66 luni. În cazul modelului COCOMO intermediar, avem următoarele valori
ale caracteristicilor:
RELY HI 1.15 VIRT NM 1.00 LEXP NM 1.00
DATA HI 1.08 TURN LO 0.87 VEXP NM 1.00
CPLX NM 1.00 ACAP HI 0.86 MODP NM 1.00
TIME VH 1.30 AEXP HI 0.91 TOOL LO 1.10
STOR VH 1.21 PCAP NM 1.00 SCED VH 1.10

Multiplicând valorile de mai sus se obŃine P = 1.6095 . Eforul nominal este 2.8 ⋅ 3001.20 = 2628
luni-om. Aplicând factorul de corecŃie P se obŃine un efort de 4229 luni-om, semnificativ mai mare decât
cel estimat folosind modelul de bază.

3. Metoda Delphi de estimare a costului


În metodele de analiză anterioare era necesară determinarea unor parametri pe baza
estimărilor unor experŃi. AcurateŃea acestor determinări este esenŃială în estimarea costului. Este de
aşteptat ca rezultatele furnizate de un grup de experŃi să fie bune decât cele ale unei singure

7
persoane. Metoda Delphi ajută la coordonarea procesului de obŃinere de informaŃii de la un grup de
experŃi şi are următorii paşi esenŃiali:
• Coordonatorul prezintă fiecărui expert specificaŃiile proiectului şi alte informaŃii
relevante.
• Coordonatorul convoacă o întâlnire unde experŃii discută estimările.
• ExperŃii completează un formular cu estimările personale ale efortului de dezvoltare a
proiectului; experŃii furnizează cea mai probabilă valoare precum şi limitele inferioară şi superioară.
• Coordonatorul pregăteşte şi pune în circulaŃie un raport ce indică estimările individuale şi
de grup.
• Coordonatorul convoacă o nouă întâlnire în care experŃii discută estimările curente.
Aceste etape se repetă până când se ajunge la un consens. Estimarea de grup se obŃine ca o
medie ponderată a estimărilor individuale cu următoarea formulă:
LI + 4VP + LS
estimare = (13)
6
unde LI – limita inferioară a estimării; VP – valoarea probabilă a estimării; LS – limita superioară a
estimării.
VarianŃa unei estimări individuale este definită ca:
LS − LI
var ianta = . (14)
6
VarianŃa estimării de grup este media varianŃelor individuale.

Exemplu. Se consideră un sistem software de management al traficului. Sistemul trebuie să livreze


informaŃii despre condiŃiile curente de trafic, să facă predicŃii şi să identifice punctele unor congestionări
potenŃiale ale traficului. Există cinci experŃi implicaŃi în estimarea costului (exprimat în luni). Coordonatorul
furnizează experŃilor informaŃiile necesare. Într-o primă fază, experŃii furnizează estimările din tabelul 4.
Coordonatorul calculează media estimărilor şi prezintă experŃilor rezultatele, conform tabelului 5. La
următoarea întâlnire, experŃii discută estimările curente şi trec la rafinarea acestora (tabelul 6). Se calculează
din nou estimarea medie şi varianŃa conform tabelului 7.
VarianŃa grupului este un indicator al convergenŃei procesului de estimare a costului. VarianŃa ar
trebui să scadă de la iteraŃie la iteraŃie.
Tabelul 4. Prima iteraŃie a estimării Tabelul 5. Calculul estimării de grup
timpului de dezvoltare şi a varianŃei – prima iteraŃie
LI VP LS Expert Estimare VarianŃă
Expert 1 20 50 60 1 46.7 6.7
Expert 2 25 40 70 2 42.5 7.5
Expert 3 10 55 75 3 50.8 10.8
Expert 4 30 60 70 4 56.7 6.7
Expert 5 15 35 85 5 40.0 11.7
Medie 47.3 8.6

Tabelul 6. A doua iteraŃie a estimării Tabelul 7. Calculul estimării de grup


timpului de dezvoltare şi a varianŃei – a doua iteraŃie
LI VP LS Expert Estimare VarianŃă
Expert 1 30 50 60 1 48.3 5.0
Expert 2 30 45 65 2 45.8 5.8
Expert 3 20 50 70 3 48.3 8.3
Expert 4 30 55 70 4 53.3 6.7
Expert 5 25 40 75 5 50.0 8.7
Medie 49.1 6.8

8
Bibliografie

1. B.P. Douglas, Real Time UML, 3rd edition, Addison-Wesley, 2004


2. B.P. Douglas, Real Time Design Patterns, Addison-Wesley, 2003
3. J. Rumbaugh, I. Jacobson, G. Booch, The Unified Modeling Language Reference manual, 2nd
edition, Addison Wesley,2004.
4. S. L. Pfleeger, Software engineering. Theory and practice, Prentice Hall, 1998.
5. Gheorghies, O., Apetrei, A., Ingineria programãrii, http://thor.info.uaic.ro/~ogh/ip/index.php,
2002
6. F. Leon, Ingineria Programarii, note de curs, http://eureka.cs.tuiasi.ro/~fleon.
7. J. F. Peters, W. Pedrycz, Software engineering. An engineering Approach, J.Wiley&Sons, 2000.
8. S. Bennet, S. McRobb, R. Farmer, Object-Oriented Systems Analysis and Design Using UML,
McGraw-Hill, 2002
9. Pressman R., Software Engineering: A practitioner's Approach, (4th ed.), McGraw-Hill, 1997.
10. Sommerville I. Software Engineering (5th ed.), Addison-Wesley, 1996.
11. Van Vliet H., Software Engineering -Principles and Practice , J.Wiley&Sons, 1993