Sunteți pe pagina 1din 104

UNIVERSITATEA “ALEXANDRU IOAN CUZA”

FACULTATEA DE INFORMATICĂ
DEPARTAMENTUL DE ÎNVĂŢĂMÂNT LA DISTANŢĂ

VICTOR FELEA
ADRIANA GHEORGHIEŞ
OVIDIU GHEORGHIEŞ

PRACTICĂ

2006-2007
Practică – Modul I
Note de curs

Prof.dr. Victor Felea


PRACTICĂ Prof.dr.Victor Felea Pag.1
Asist.drd.Ovidiu Gheorghieş

I. Interogări de tip SQL ...................................................................................... 2


1. Comanda SELECT – SQL ............................................................................................. 2
2. Join interior şi exterior în VISUAL FOX şi ORACLE................................................ 12
3. Considerarea valorii NULL.......................................................................................... 14
4. Prelucrarea arborescenţelor cu SELECT –SQL ORACLE ......................................... 15
5. Exprimarea interogărilor complexe sub forma unei fraze SELECT ........................... 21
PRACTICĂ Prof.dr.Victor Felea Pag.2
Asist.drd.Ovidiu Gheorghieş

I. Interogări de tip SQL

1. Comanda SELECT – SQL

Formatul general pentru sistemul de gestiune de baze de date FOX pentru DOS sau
WINDOWS este:

ALL 
SELECT   exp1[AS c1], ..., expq[AS cq]
DISTINCT 

FROM T1 a1, ..., Th ah

INTO <destinaţie> 
 
TO FILE <nume fişier>[ADDTITIVE]
TO PRINTER 
 

WHERE <expesie logică 1>

GROUP BY grup1, ..., grup p

HAVING <expesie logică 2>

UNION <frază SELECT>

ORDER BY g1, g2, ..., gs

[NO CONSOLE] [NOWAIT] [PLAIN]

Să discutăm modul de interpretare a acestor componente.


Informaţia de intrare în acestă comandă este preluată din tabelele Tj , 1 ≤ j ≤ h, precizate în
componenta FROM.
Subcomponenta aj este un alias pentru tabela Tj, numit alias local (deci valabil numai în
cadrul acestei comenzi SELECT). Evident, aj sunt distincte, pe când o tabelă poate apărea de
mai multe ori în componenta FROM cu alias-uri diferite. Aliasul aj nu este obligatoriu, dar
dacă s-a declarat pentru tabela Tj, atunci orice referire la un <câmp> definit in tabela Tj se va
face prin aj.câmp şi nu prin Tj.câmp.
Să considerăm conţinutul tabelelor Tj în momentul executării acestei comenzi. Fie Tj =
( ) m
I1j ,..., I j j vectorul înregistrărilor din tabela Tj, pentru fiecare j, 1 ≤ j≤ h. Informaţia de intrare
din comanda SELECT – SQL este dată de produsul cartezian al tabelelor Tj, deci T1 × T2 ×
{ }
...× Th = ( I1α1 , Iα2 2 ,..., Iαh h ) |1 ≤ αi ≤ mi ,1 ≤ i ≤ h .
PRACTICĂ Prof.dr.Victor Felea Pag.3
Asist.drd.Ovidiu Gheorghieş
Să notăm prin v α1 ...αh elementul ( I1α1 ,..., Iαh h ) al produsului cartezian.
Astfel, informaţia de intrare este prelucrată în maniera următoare:
FOR α1 = 1 TO m1
FOR α2 = 1 TO m2
......
FOR αh = 1 TO mh
Tratează v α1 ...αh
ENDFOR αh
.......
ENDFOR α2
ENDFOR α1.
Elementele produsului cartezian se parcurg în ordinea specificată mai sus; numărul
elementelor fiind m1 × m2 × ... × mh. Rezultă că, dacă există i, astfel încât tabela Ti este vidă,
deci mi = 0, comanda are la intrare zero elemente, deci rezultatul va fi constituit din 0
înregistrări (dacă rezultatul va fi plasat într-o tabelă, aceasta va fi vidă).
Expresiile expi, 1 ≤ i ≤ q definesc expresiile de ieşire ale comenzii. O astfel de expresie este
formată sintactic cu termeni de bază: constante, variabile, funcţii, câmpuri ale tabelelor de
intrare, iar ca operatori: aritmetici, relaţionali, logici.
Fie situaţia în care expj nu conţine funcţii de grupare (MIN, MAX, etc.). Definim ce înseamnă
evaluarea sa pentru elementul v α1 ...αh , notată expj( v α1 ...αh ).
a) Fie cazul în care expj are forma ai .cimp.
Atunci cimp trebuie să apară în structura tabelei Ti. Fie această structură STi
=( cimp1 ,..., cimp ni ). Există s, 1 ≤ s≤ ni, astfel încât să avem: cimp = cimps. Intregul ni este
numărul câmpurilor (atributelor) din tabela Ti. Evidenţiem indicele i din vectorul v α1 ...αh :
( )
v α1 ...αh = I1α1 , Iα2 2 ,..., Iiαi ,..., Iαh h . Înregistrarea Iiαi este din tabela Ti, ce intervine în
cadrul elementului v α1 ...αh . Fie înregistrarea Iiαi specificată prin vectorul de valori, câte una
pentru fiecare câmp: Iiαi = (w1, w2, ..., ws, ..., w ni ). Atunci definim: expj( v α1 ...αh ) = ws. Deci
valoarea expj pentru vectorul v α1 ...αh este valoarea câmpului respectiv din înregistrarea
corespunzătoare tabelei în care este definit câmpul.
b) Dacă expj este o constantă c, atunci expj ( v α1 ...αh ) = c.
c) Dacă expj este o variabilă v, atunci expj( v α1 ...αh ) = v (valoarea variabilei v).
d) Dacă expj este un apel de funcţie f(e1, ..., er) atunci: expj( v α1 ...αh )=f(e1,..., er).
e) Pentru cazul expresiilor oarecare, valoarea lui expj se defineşte recursiv.
Presupunem că exp1 şi exp2 au fost evaluate pentru v α1 ...αh , deci s-au calculat exp1( v α1 ...αh ) şi
exp2( v α1 ...αh ). Fie θ un operator permis între exp1 şi exp2, atunci (exp1 θ exp2) ( v α1 ...αh ) =
exp1( v α1 ...αh ) θ1 exp2( v α1 ...αh ), unde θ1 este operatorul corespunzător lui θ, definit pe domeniul
valorilor pentru exp1 şi exp2. Operatorul θ poate fi aritmetic, relaţional sau logic. În cazul θ
operator aritmetic, va rezulta exp1 θ exp2 de tip aritmetic, iar în celelalte cazuri, expresia exp1
θ exp2 va fi de tip logic. În absenţa funcţiilor de grupare din expresiile de ieşire expj, 1 ≤ j ≤ q,
se va forma o linie corespunzătoare vectorului de înregistrări v α1 ...αh de forma: L α ...α =
1 h

(exp1( v α1 ...αh ), ..., expq( v α1 ...αh )). Componenta WHERE serveşte la filtrarea acestor linii: se
PRACTICĂ Prof.dr.Victor Felea Pag.4
Asist.drd.Ovidiu Gheorghieş
evaluează <expresie logică1> pentru vectorul v α1 ...αh , notată <expresie logică1>( v α1 ...αh ). Dacă
rezultatul este .F., atunci linia L α ...α este ignorată în continuare, altfel ea este considerată.
1 h

Ieşirea comenzii va fi formată dintr-o tabelă cu q coloane (câmpuri sau atribute). Dacă pentru
expresia de număr j se precizează expj AS cj, atunci cj este un nume atribuit coloanei j din
tabela rezultată(de ieşire). Această coloană primeşte caracteristicile expresiei expj
(identificator, tip, lungime, număr de zecimale). Dacă pentru expresia expj nu se precizează
AS cj, atunci numele coloanei de număr j şi caracteristicile ei se atribuie după anumite reguli
(numite reguli standard), precizate in continuare.
a) Dacă expj este formată numai din <cimp> ce apare o singură dată în lista expresiilor
de ieşire, atunci coloana j a tabelei va avea numele <cimp> şi caracteristicile acestuia.
b) Dacă expj este formată numai din <cimp>, ce apare de mai multe ori în lista
expresiilor de ieşire (poate fi precedat de alias-uri diferite), atunci pentru prima apariţie
numele coloanei va fi <cimp_A>, pentru a doua <cimp_B>, etc.
c) Dacă expj este diferită de <cimp>(conţine în afara de <cimp> şi alte elemente),
atunci pentru prima apariţie a unei astfel de expresii se consideră identificatorul EXP_A,
pentru a doua EXP_B, etc.
d) Dacă expj are forma SUM(expresie), atunci pentru prima apariţie a acesteia,
identificatorul coloanei va fi SUM_A, pentru a doua SUM_B, etc. În mod similar, pentru
celelalte funcţii agregat.
Dacă comanda SELECT, va crea o tabelă, atunci vom da componenta INTO DBF <nume
tabelă_ieşire>. La terminarea comenzii tabela de ieşire rămâne activă în zona curentă, deci
utilizând DISP STRU putem vizualiza numele şi caracteristicile câmpurilor de ieşire. Tabelele
de intrare T1, ..., Th sunt activate, la interpretarea comenzii SELECT-SQL în zone de lucru
libere, iar la terminarea normală a comenzii sunt închise.
Rezultă că singurele componente obligatorii ale comenzii SELECT – SQL sunt: lista
expresiilor de ieşire şi componenta FROM.
În cazul în care comanda SELECT- SQL are în plus numai componenta WHERE<expresie
logică1>, atunci executarea ei se realizează astfel:

FOR α1 = 1 TO m1
....
FOR αh = 1 TO mh
- se evaluează <expresie logică1>( v α1 ...αh ), fie w–valoarea rezultată,
- IF(w)
FOR i = 1 TO q
- se evaluează expi( v α1 ...αh ), fie wi – valoarea rezultată,
ENDFOR
- se plasează la ieşire L α ...α =(w1, ...,wq)
1 h

ENDIF
ENDFOR αh
....
ENDFOR α1
Componenta <destinaţie> are următoarele forme sintactice:
1) ARRAY <numetablou>
2) CURSOR <numetabelă>
3) DBF <numetabelă>
4) TABLE <numetabelă>
PRACTICĂ Prof.dr.Victor Felea Pag.5
Asist.drd.Ovidiu Gheorghieş
In cazul 1) o linie de ieşire L α ...α =(w1, ...,wq) va fi înregistrată într-o linie a tabloului
1 h

<numetablou>. În cazurile 2), 3) sau 4) o linie de ieşire va constitui o înregistrare în tabela


<numetabelă>. În cazul 2) <numetabelă> poate fi utilizată în cadrul programului, dar la
terminarea programului aceasta se va şterge. În cazurile 3) şi 4) <numetabelă> rămâne
memorată pe suport şi după terminarea programului. În cazul utilizării componentei TO FILE
<numefişier> toate liniile de ieşire vor fi memorate în <numefişier> într-o anumită formă
depinzând de sistemul de operare. Forma TO FILE <numefişier> ADDITIVE semnifică faptul
că există deja fişierul <numefişier> al sistemului de operare şi liniile de ieşire ale frazei
SELECT – SQL vor fi adăugate la sfârşit în acest fişier.
Forma TO PRINTER implică afişarea la imprimantă a informaţiei de ieşire a comenzii
SELECT – SQL.
Să analizăm acum componenta WHERE <expresie logică1>, cea mai complexă dintre
componente. Distingem întâi, mai multe tipuri de expresii elementare:
a) e = ai.cimp1 θ aj.cimp2
Vom nota cu e( v α1 ...αh ) valoarea expresiei e pentru vectorul de înregistrări de intrare v α1 ...αh .
θ este un operator de comparare: =, <, >, <=, >=, =.
Am văzut cum se evaluează ai.cimp1 şi aj.cimp2 pentru v α1 ...αh .
Atunci valoarea expresiei e pentru v α1 ...αh se defineşte:

( ) ( ) ( )
.T. dacă a i .cimp1 v α1 ...α h θ a j .cimp 2 vα1 ...α h ,
e v α1 ...α h = 
.F. altfel.
Deci, pe scurt, semantica expresiei e pentru v α1 ...αh este: expresia e este TRUE, dacă valorile
înregistrărilor respective pentru cimp1 şi cimp2 sunt în relaţia θ.
În privinţa comparării a două şiruri de caractere s1 şi s2 există diverse situaţii pentru valoarea
de adevăr a expresiei s1θs2, unde θ este operator de comparare. De exemplu SET EXACT ON
implică în cazul s1 = s2, valoarea TRUE, dacă cele două şiruri au aceeaşi lungime şi acelaşi
caractere. Dacă avem SET EXACT OFF, atunci s1=s2 este TRUE dacă valoarea unui şir este
prefix al valorii celuilalt.
b) e = ai.cimp θ expresie, unde expresie nu este formată numai dintr-un câmp.
Valoarea expresiei e pentru v α1 ...αh este:

( ) ( ) ( )
.T. dacă a i .cimp v α1 ...αh θ exp resie vα1 ...α h ,
e v α1 ...αh = 
.F. altfel.
c) e = ai.cimp θ ALL (SELECT cimp2 FROM T2...).
Expresia după cuvântul rezervat ALL se numeşte subselecţie.
În general, vom numi subselecţie o frază SELECT inclusă între paranteze rotunde. Aici
subselecţia are o singură expresie de ieşire: cimp2. Rezultatul subselecţiei va fi o tabelă T' cu
un singur atribut. Fie T' cu m linii şi w1, ..., wm valorile atributului de ieşire pentru cele m
linii. Identificatorii cimp1 şi cimp2 trebuie să fie de acelaşi tip. Valoarea expresiei e pentru
vectorul v α1 ...αh de înregistrări este:

( )
e v α1 ...αh = 
( )
.T. dacă a i .cimp v α1 ...αh θ w i , ∀i = 1, m,

.F. altfel.
c') e = ai.cimp1 θ ALL (SELECT exp2 FROM T2...).
exp2 este o expresie de acelaşi tip cu cimp1. Evaluarea este similară ca în cazul c) considerând
exp2 în loc de cimp2.
PRACTICĂ Prof.dr.Victor Felea Pag.6
Asist.drd.Ovidiu Gheorghieş
ANY 
d) e = ai.cimp1 θ   (SELECT cimp2 FROM T2...).
SOME 
Folosind notaţiile de la c), definim:

( )
e v α1 ...αh = 
( )
.T. dacă ∃i,1 ≤ i ≤ m, astfel încât a i .cimp1 vα1 ...αh θ w i ,

.F. altfel.
ANY 
d') e = ai.cimp1 θ   (SELECT exp2 FROM T2...).
SOME 
exp2 este o expresie de acelaşi tip cu cimp1. Evaluarea este similară ca în cazul d).
e) e = ai.cimp [NOT] BETWEEN exp1 AND exp2.
Atributul cimp trebuie să aibă acelaşi tip cu expresiile exp1 şi exp2. Fără cuvântul rezervat
NOT, evaluarea lui e va fi:

( )
e v α1 ...αh = 
( )
.T. dacă a i .cimp v α1 ...αh este în intervalul de valori [exp1 , exp 2 ],

.F. altfel.
Expresia cu NOT înseamnă o evaluarea inversă ca mai sus.
f) e = [NOT] EXISTS ( SELECT exp1, ..., expq FROM ...).
Fie T' tabela ce constituie rezultatul subselecţiei. Fără cuvântul NOT evaluarea este definită
prin:
.T. dacă T ' ≠ ∅ (T' are cel puţin o înregistrare),
( )
e v α1 ...αh = 
.F. altfel.
Dacă există cuvântul NOT, atunci:
.T. dacă T ' = ∅,
( )
e v α1 ...αh = 
.F. altfel.
Obs. Evaluarea tabelei T' poate fi independentă de vectorul v α1 ...αh (adică are aceeaşi valoare
pentru fiecare element al produsului cartezian T1 × ...× Th), sau dependentă de v α1 ...αh .
Exemplul 1: Fie tabela PERSONAL cu câmpurile MARCA – cheie unică şi NUME,
PRENUME, SALARIU, SECŢIE, şi tabela VANZARI cu câmpurile MARCAV – marca
persoanei ce vinde, CODSV – codul sectiei în care se vinde produsul respectiv, CODPV –
codul produsului vândut, CANTV – cantitatea vândută de MARCAV din produsul CODPV.
Fie tabela PRODUS cu câmpurile CODP – codul produsului (cheie unică), DENP –
denumire, PRET – preţul unitar.
Fie interogarea:
I) Să se afişeze vânzările realizate de persoanele înregistrate în tabela PERSONAL sub forma:
MARCAV, CODPV, CANTV
O comandă SELECT – SQL, ce realizează interogarea, poate fi:

SELECT A.MARCAV, A.CODPV, A.CANTV;

FROM VANZARI A;

WHERE EXISTS;

(SELECT B.MARCA FROM PERSONAL B;


PRACTICĂ Prof.dr.Victor Felea Pag.7
Asist.drd.Ovidiu Gheorghieş
WHERE B.MARCA = A.MARCAV)

Notând cu T' tabela definită de subselecţie, se observă că T' depinde de valorile câmpului
A.MARCAV, deci subselecţia este dependentă de v α = (Iα), unde 1 ≤ α ≤ m1 şi vectorul de
înregistrări al tabelei VANZARI este (I1, ..., I m1 ).
Deoarece MARCA este cheie unică, rezultă că T' are fie o singură înregistare, fie T' = ∅.
II) Fie interogarea: să se afişeze vânzările realizate de persoane care nu există în PERSONAL.

SELECT A.MARCAV, A.CODPV, A.CANTV;

FROM VANZARI A;

WHERE NOT EXISTS;

(SELECT B.MARCA FROM PERSONAL B;

WHERE B.MARCA = A.MARCAV)

III) Să se găsească persoanele din PERSONAL, care vând produsul cu codul 'P1'.

SELECT A.MARCA, A.NUME FROM PERSONAL A;

WHERE EXISTS;

(SELECT B.MARCAV FROM VANZARI B;

WHERE (B.MARCAV = A.MARCA) AND;

(B.CODPV = 'P1') )

Rezultă că subselecţia este dependentă de A.MARCA.


Interogarea II) se poate realiza şi prin comanda (forma h)):

SELECT A.MARCAV, A.CODPV, A.CANTV;

FROM VANZARI A;

WHERE A.MARCAV NOT IN;

(SELECT B.MARCA FROM PERSONAL B)


PRACTICĂ Prof.dr.Victor Felea Pag.8
Asist.drd.Ovidiu Gheorghieş
Se observă în acest caz faptul că subselecţia este independentă de vectorul v α . Tabela T'
conţine toate valorile câmpului MARCA din PERSONAL.
g) e = aj.cimp [NOT] IN (v1, ..., vm)
unde vi sunt valori de acelaşi tip cu aj.cimp, 1 ≤i≤m.
Fie v' = aj.cimp( v α1 ...αh ). Fără cuvântul NOT avem:
.T. dacă v ' ∈ {v1 ,..., v m } ;
( )
e vα1 ...αh = 
.F. altfel.
Dacă există cuvântul NOT, atunci:
.T. dacă v ' ∉ {v1 ,..., v m } ;
( )
e v α1 ...αh = 
.F. altfel.
h) e = aj.cimp [NOT] IN ( SELECT b.cimp2 FROM T2 b...).
Rezultatul subselecţiei este o tabela T' cu o singură coloană (atribut). Fie valorile acestei
coloane w1 ,..., w m . Fără NOT, valoarea expresiei e pentru v α1 ...αh este:

( )
e v α1 ...α h = 
( )
.T. dacă a j .cimp v α1 ...αh ∈ {w1 ,..., w m } ;

.F. altfel.
( )
Dacă există NOT, atunci e v α1 ...αh este inversa celei de mai sus.
Exemplul 2: Considerăm tabelele PERSONAL şi VANZARI definite mai sus. Se cere să se
afişeze persoanele (MARCA, NUME şi PRENUME) care au cel puţin o operaţie de vânzare.

SELECT A.MARCA, A.NUME, A.PRENUME ;

FROM PERSONAL A;

WHERE A.MARCA IN;

(SELECT B.MARCAV FROM VANZARI B);

INTO DBF F1

Exemplul 3: Fie date următoarele tabele:


CONTRACTE cu atributele CODC – cod contract (cheie unică), CODB – cod beneficiar,
CODF – cod furnizor, NRC- număr contract, DATC – data contractului.
ARTICOLE reprezentând produsele specificate în contracte, având câmpurile: CODCA –
codul contractului, CODPA – codul produsului, CANTA – cantitatea respectivă, PRETA –
preţul unitar contractat.
REALIZARI – reprezentând livrări de produse la diverse contracte, cu câmpurile: CODCR –
cod contract, CODPR – cod produs, CANTR – cantitatea livrată din produsul CODPR la
contractul CODCR.
Să se verifice consistenţa datelor: orice valoare a câmpului CODCA trebuie să apară în
CONTRACTE, orice pereche de valori pentru (CODCR, CODPR) din REALIZĂRI trebuie
să apară în ARTICOLE.
În SGBD – ORACLE sunt permise condiţii elementare de forma:
PRACTICĂ Prof.dr.Victor Felea Pag.9
Asist.drd.Ovidiu Gheorghieş
e = (a1.cimp1,..., aq.cimpq) [NOT] IN (SELECT b1 .cimp1′ ,..., b q .cimp′q ... ).
Fie T' tabela definită de subselecţie, ce are q coloane şi fie liniile lui T' notate w1, ..., ws. Fie
L α ...α linia definită pentru vectorul v α1 ...αh pentru ai.cimpi, 1≤i≤q, astfel:
1 h

L α ...α =(a1.cimp1( v α1 ...αh ),..., aq.cimpq ( v α1 ...αh )).


1 h

Fără cuvântul NOT valoarea expresiei e pentru v α1 ...αh este:


.T. dacă L α ...α ∈ {w1 ,..., w s } ,
( )
e v α1 ...αh =  1 h

.F. altfel.
Deoarece în FOX sunt permise numai astfel de expresii pentru q = 1, atunci pentru q > 2,
putem proceda astfel:
a) Fie situaţia când toate aj.cimpj sunt de tip caracter (la fel şi bj.cimp'j). Putem
construi următoarea expresie:
e1 = (a1.cimp1+...+ aq.cimpq) [NOT] IN (SELECT b1 .cimp1′ + ... + b q .cimp′q ... ).
Avem e1( v α1 ...αh ) = e( v α1 ...αh ).
b) În cazul când aceste câmpuri nu sunt de tipul caracter se folosesc funcţiile de
conversie (STR, DTOC) pentru conversia la tipul C.
i) e = aj.cimp [NOT] LIKE <expresie sablon>
aj.cimp trebuie să fie de tip C, deoarece <expresie sablon> defineşte o mulţime de cuvinte:
caracterul % din expresia şablon reprezintă orice cuvânt, iar caracterul '_' reprezinta orice
caracter.
Exemplul 5. <expresie şablon> = ALFA% înseamnă toate cuvintele ce încep cu caracterele
ALFA.
Fie M mulţimea cuvintelor definite de <expresie şablon>.
Fără NOT valoarea expresiei e pentru v α1 ...αh este:

( )
e v α1 ...αh = 
( )
.T. dacă a j .cimp v α1 ...αh ∈ M,

.F. altfel.
O condiţie oarecare ce poate apare în componenta WHERE se defineşte cu ajutorul
operatorilor AND, OR şi NOT. Rezultă că avem următoarele reguli pentru definirea unor
condiţii:
1. orice condiţie elementară este o condiţie.
2. dacă e1 şi e2 sunt condiţii, atunci e1 AND e2, (e1 AND e2), e1 OR e2, (e1 OR e2),
NOT e1, ( NOT e1) sunt condiţii.
3. orice condiţie se obţine numai prin regulile 1 şi 2.
Deoarece condiţiile elementare c, c', d, d', f, h au subselecţii, rezultă necesitatea definirii
nivelelor de selecţie.
1) Cuvântul SELECT, primul din fraza SELECT se consideră pe nivelul 1 şi spunem
că iniţiază o selecţie de nivel 1.
2) Dacă luăm condiţiile elementare c, c', d, d', f, h pentru fraza SELECT de nivel 1,
spunem că orice verb SELECT din subselecţii este pe nivelul 2 şi defineşte o selecţie de nivel
2.
3) Fie cuvântul SELECT situat pe nivelul h şi în componenta WHERE
corespunzătoare avem o subselecţie de tipul c, c', d, d', f, sau h. Atunci spunem că această
subselecţie este de nivelul h + 1.
4) Cuvântul UNION se aplică numai la selecţii de nivel 1, deci SELECT ce urmează
lui UNION este considerat de nivel 1.
5) Pot exista, în general, mai multe selecţii pe un acelaşi nivel s.
PRACTICĂ Prof.dr.Victor Felea Pag.10
Asist.drd.Ovidiu Gheorghieş
6) În FOX pot exista maxim 2 nivele de selecţie, pe când în SGBD – ORACLE sau
DB2 practic, până la 50 nivele de selecţie.
Să considerăm acum componentele GROUP BY şi HAVING.
Componenta GROUP BY are forma:
GROUP BY grup1, ..., grupp, unde grupj sunt fie câmpuri ce apar în lista expresiilor de ieşire,
fie sunt numere naturale cuprinse între 1 şi q (numărul total al coloanelor de ieşire), ce
reprezintă numere de coloane.
Folosirea cuvântului ALL ce urmează cuvântului SELECT înseamnă considerarea tuturor
liniilor rezultate în urma interpretării componentei WHERE, iar folosirea lui DISTINCT
 t1 
 
t
înseamnă eliminarea liniilor duplicate. Să notăm cu M mulţimea acestor linii. Fie M =  2  ,
M
 
 tm 
unde tj sunt liniile de ieşire,
1 ≤ j ≤ m.
Se evaluează grup1 pentru toţi tj, 1 ≤ j≤ m, fie grup1(tj) valoarea lui grup1 pentru linia tj. Fie
{ }
M1 ={ grup1(tj), 1 ≤j ≤m}= v1 ,..., v m1 . Se împarte mulţimea de linii M în clase, notate
1
C ,..., C
1
1
m1 câte o clasă pentru fiecare valoare vi din M1, 1 ≤i≤m1, anume:
C1i = {t α | t α ∈ M, grup1 ( t α ) = vi } . Deci clasa C1i este formată din toate liniile lui M ce au
aceeaşi valoare vi pentru câmpul grup1.
Exemplul 6. Pentru tabela VANZARI ne interesează pentru fiecare valoare a câmpului
MARCAV, care este valoarea de vânzare respectivă.

SELECT A.MARCAV, SUM(A.CANTV*B.PRET) AS SUMA;

FROM VANZARI A, PRODUS B;

WHERE A.CODPV=B.CODP;

GROUP BY A.MARCAV;

INTO DBF F1

Fie următorul conţinut al tabelei VANZARI:


t1 100 S1 P1 10
t2 200 S2 P2 15
t3 100 S3 P3 12
t4 200 S3 P3 10
t5 100 S2 P2 5

Fie tabela PRODUS conţinând înregistrările:


(P1, ’DEN1’, 1), (P2, ’DEN2’, 2), (P3, ’DEN3 ’, 3)
PRACTICĂ Prof.dr.Victor Felea Pag.11
Asist.drd.Ovidiu Gheorghieş
Atunci M=(t1, t2, t3, t4, t5), M1={100, 200}, m1 = 2, C11 = {t1 , t 3 , t 5 } , C21 = {t 2 , t 4 } . Pentru clasa
C11 se calculează suma dintre CANTV şi PRET, rezultând 10*1 + 12*3 + 5*2 = 56, iar pentru
C12 avem: 15*2 + 10*3 = 60. Rezultatul comenzii va consta din 2 linii:

1 100 56
2 200 60

Evident dacă Ci1 şi C1j sunt două clase diferite (i≠j), atunci rezultă că vi ≠ vj, deci clasele sunt
disjuncte. Avem şi faptul că ele sunt nevide, deci C11 ,..., Cm1 1 este o partiţie a lui M.
Procedăm inductiv. Presupunem că, după considerarea lui grupj, s-a obţinut o partiţie a lui M
j j
în clasele C1 ,..., Cm j (j <p).
j
Fie acum considerarea lui grupj+1. Fiecare clasă Cs se rafinează conform grupului grupj+1
exact în aceeaşi manieră cum M s-a rafinat conform grupului grup1. Fie rafinarea obţinută
j s s
astfel a lui Cs prin grupj+1 notată T1 ,..., Tps , 1≤ s≤mj. Atunci rafinarea lui M în clase
1 1 2 2 m m
considerând grup1, ..., grupj+1 va fi: T1 ,..., Tp1 , T1 ,..., Tp2 , T1 j ,..., Tpm j . Fie aceste clase
j

mj
j +1 j +1
renotate prin: C1 ,..., Cm j +1 unde m j +1 = ∑p s =1
s . Astfel, după considerarea tuturor grupurilor

p p
grup1,..., grupp, obţinem rafinarea lui M în clasele C1 ,..., Cm p . Rezultă următoarea
proprietate a acestor clase:
Toate uplele unei clase au aceleaşi valori pentru câmpurile grup1, ..., grupp, ceeace se exprimă
p
prin: (∀ t1, ∀t2) {t1, t2∈ C j ⇒ t1 [ grup1 ,...,grup p ] =
t2[ grup1 ,...,grup p ]}, pentru orice j, 1 ≤ j≤ mp, parantezele [, ] reprezentând proiecţia. Două
uple din clase distincte au vectori de valori distincţi pentru grup1 ,...,grup p .
Expresiile de ieşire ale comenzii SELECT: exp1, ..., expq pot conţine funcţii de grupare:
COUNT, MIN, MAX, SUM, VAR, ce se vor aplica asupra claselor definite de componenta
GROUP BY. In acest caz, rezultă că numărul de uple ale tabelei de ieşire este egal cu numărul
de clase, deci mp.
Exemplul 7. Să se afişeze pentru fiecare valoare a câmpului MARCAV din VANZARI,
numărul de vânzări realizate:

SELECT A.MARCAV, COUNT(*) AS NRV;


FROM VANZARI A;
GROUP BY A.MARCAV;
INTO DBF F1
În cazul când fraza SELECT are componenta HAVING <expresie logică2>, atunci <expresie
p
logică2> poate conţine funcţii de grupare şi deci se evaluează pentru fiecare clasă C j ,
PRACTICĂ Prof.dr.Victor Felea Pag.12
Asist.drd.Ovidiu Gheorghieş
1≤j≤mp. Dacă rezultatul este .T., atunci se produce o linie la ieşire formată din valorile exp1,
p
..., expq pentru clasa respectivă C j . Altfel, se ignoră această clasă.
Exemplul 8. Ne interesează pentru fiecare MARCAV din VANZARI, numărul de vânzări
realizate, dar numai cele care au cel puţin 3 vânzări.

SELECT A.MARCAV, COUNT(*) AS NRV;

FROM VANZARI A;

GROUP BY A.MARCAV;

HAVING COUNT(*) >= 3;

INTO DBF F1

Componenta UNION. Forma generală:


UNION [ALL]
SELECT exp1′ ,exp′2 ,...,exp′q FROM ...
Fie M – mulţimea de linii returnată de comanda SELECT iniţială şi M' – mulţimea de linii
returnată de această comandă SELECT din UNION. Atunci se realizează reuniunea M ∪ M'.
De aici, rezultă că cele două comenzi SELECT trebuie să aibă acelaşi număr de coloane (q) şi
expj trebuie să aibă acelaşi tip cu exp′j , j = 1, q .
Pot exista mai multe componente UNION intr-o comandă SELECT – SQL. Dacă dorim
ordonarea la ieşire a înregistrărilor, atunci utilizăm componenta ORDER BY g1, ..., gs, în care
gi sunt expresii de ieşire ale comenzii. În cazul utilizării de componente UNION şi GROUP
BY, atunci ultima comandă SELECT din UNION va trebui să conţină GROUP BY şi în acest
caz gj sunt numere de coloană ale tabelei de ieşire. Fără cuvântul ALL din UNION se elimină
duplicatele în M∪M'. Utilizarea lui ALL în UNION păstrează toate liniile din M şi M'.
Folosirea cuvântului NOCONSOLE interzice afişarea rezultatelor pe ecran. Clauza NOWAIT
implică derularea ieşirii interogării pe ecran, fără a se aştepta umplerea unui ecran. Cuvântul
PLAIN implică eliminarea numelor de coloane din afişarea rezultatului interogării.

2. Join interior şi exterior în VISUAL FOX şi ORACLE

Sintactic în componenta FROM a frazei SELECT putem avea:


INNER 
LEFT OUTER 
 
FROM <baza1> | <tabela1> <alias1>   [JOIN]
 RIGHT OUTER 
FULL OUTER 
<baza2> | <tabela2> <alias2> ON <expresie–join>
PRACTICĂ Prof.dr.Victor Felea Pag.13
Asist.drd.Ovidiu Gheorghieş
<baza1> şi <baza2> sunt nume de baze de date, iar <tabela1>, <tabela2> sunt nume de
tabele existente respectiv în cele două baze. Subcomponenta <alias1> va defini aliasul
temporar al tabelei <tabela1>, similar pentru <alias2>.
<expresie–join> este o expresie logică formată cu câmpurile celor două tabele, variabile sau
constante. Fie date următoarele conţinuturi ale celor două tabele: <tabela1> = (I1, ..., Im), iar
<tabela2> = (J1, ..., Jp). Fie <expresie–join>(Ij, Jl), 1≤ j ≤ m, 1≤ l ≤ p, evaluarea expresiei
<expresie–join> pentru perechea de înregistrări Ij, Jl.
În cazul INNER JOIN (join interior) se consideră toate perechile (Ij, Jl) cu proprietatea că
<expresie–join>(Ij, Jl)=.T., comanda SELECT respectivă va considera în continuare numai
aceste perechi.
În cazul LEFT OUTER JOIN (join exterior stâng) se vor considera perechile (Ij, Jl) ca în cazul
join-ului interior şi în plus, pentru fiecare Ij din <tabela1>, ce are proprietatea că nu există Jl în
<tabela2>, astfel încât <expresie–join>(Ij, Jl) = .T., se va considera o pereche de forma (Ij, J0)
unde J0 este o înregistrare vidă corespunzătoare tabelei <tabela2> (toate câmpurile au valoarea
NULL).
În cazul RIGHT OUTER JOIN (join exterior drept) se consideră perechile (Ij, Jl), ca în cazul
join-ului interior, în plus pentru fiecare Jl din <tabela2>, cu proprietatea că nu există
Ij∈<tabela1>, astfel încât <condiţie–join> (Ij, Jl) = .T., se va considera perechea (I0, Jl), unde
I0 este o înregistrare vidă corespunzătoare tabelei <tabela1>.
În cazul FULL OUTER JOIN se consideră perechile de înregistrări definite atât de joinul
exterior stâng, cât şi cele definite de joinul exterior drept.
Exemplul 9. Fie tabela PERSONAL1 cu câmpurile MARCA şi NUME şi tabela VANZAR
cu câmpurile MARCAV (marca persoanei ce vinde), CODPV (codul produsului vândut).
Considerăm următoarele înregistrări pentru cele două tabele:
PERSONAL1 VANZAR
MARCA NUME MARCAV CODPV
100 A 100 P1
200 B 100 P2
300 C 100 P3
400 D 300 P1
500 E 300 P2
350 P1
350 P2
Considerăm următoarele interogări:
a) Să se afişeze persoanele şi codurile de produse vândute de acestea.

SELECT A.MARCA, A.NUME, B.CODPV;

FROM PERSONAL1 A INNER JOIN VANZAR B;

ON A.MARCA = B.MARCAV

Rezultatul:
MARCA NUME CODPV
PRACTICĂ Prof.dr.Victor Felea Pag.14
Asist.drd.Ovidiu Gheorghieş
100 A P1
100 A P2
100 A P3
300 C P1
300 C P2

b) Să se afişeze persoanele şi codurile de produse vândute. În plus pentru o persoană ce nu are


vânzări să apară în rezultat cu codul produs vândut NULL (afişarea pe ecran se va face cu
spaţii).

SELECT A.MARCA, A.NUME, B.CODPV;


FROM PERSONAL1 A LEFT OUTER JOIN VANZAR B;
ON A.MARCA = B.MARCAV
Rezultatul este cel de la punctul a) la care se adăugă înregistrările:
200 B
400 D
500 E

c) Să se afişeze persoanele şi codurile de produse vândute. În plus, pentru o vânzare cu


proprietatea că MARCAV nu apare în PERSONAL1 să se afişeze spaţii.

SELECT A.MARCA, A.NUME, B.CODPV;

FROM PERSONAL1 A RIGHT OUTER JOIN VANZAR B;

ON A.MARCA = B.MARCAV

Rezultatul este cel de la punctul a) la care se adăugă înregistrările:


P1
P2

d) În cazul în care folosim FULL OUTER JOIN între cele două tabele, atunci rezultatul va fi
format din liniile de la a), la care se adaugă cele în plus de la b) şi c).

3. Considerarea valorii NULL

Cuvântul rezervat NULL semnifică valoarea nedefinită (un câmp nu este iniţializat). El poate
să apară în comanda CREATE TABLE, ce are forma:
 NULL 
CREATE TABLE <nume tabelă> (cimp1 tip1   , ...)
 NOT NULL 
Apariţia NOT NULL pentru cimp1 specifică faptul că toate înregistrările trebuie să fie definite
pentru cimp1, deci cimp1 trebuie să aibă valoare pentru toate înregistrările, iar NULL – cazul
contrar. tip1 este tipul atributului cimp1.
PRACTICĂ Prof.dr.Victor Felea Pag.15
Asist.drd.Ovidiu Gheorghieş
Cuvântul NULL poate să apară în expresii aritmetice ca operand, rezultatul este evaluat la
NULL. De asemenea, el poate apare în expresii logice ca operand. Aceste expresii au forma:
 IS 
<operand1>   <operand2>, unde <operand1> sau <operand2> este cuvântul NULL.
 IS NOT 
Rezultă de aici considerarea, pe lângă valorile de adevăr TRUE, FALSE şi a unei alte valori,
notată UNKNOWN.
Ca un exemplu, expresia <cimp is NULL> evaluată pentru o înregistrare I are valoarea .T.,
dacă avem cimp(I) este nedefinit şi .F. altfel.
Expresia <cimp = NULL> are valoarea UNKNOWN, indiferent dacă avem cimp(I) definit
sau nedefinit. Valoarea UNKNOWN, notată pe scurt U, este considerată între valorile
TRUE(.T.) şi FALSE(.F.).
Conjuncţia şi disjuncţia a 2 valori logice se defineşte astfel:

X Y X AND Y X OR Y
.T. .T. .T. .T.
.T. U U .T.
.T. .F. .F. .T.
U .T. U .T.
U U U U
U .F. .F. U
.F. .T. .F. .T.
.F. U .F. U
.F. .F. .F. .F.

Negaţia este definită ca mai jos:

x NOT x
.T. .F.
U U
.F. .T.

Funcţiile de grupare (SUM, MIN, MAX, COUNT, AVG) nu consideră expresiile cu valoarea
NULL. Dacă o expresie aritmetică sau relaţională are pentru vectorul v α1 ...αh un operand cu
valoarea NULL, atunci expresia are valoarea NULL.

4. Prelucrarea arborescenţelor cu SELECT –SQL ORACLE

Considerăm tabela PERSONAL cu atributele MARCA, NUME, SALARIU, SECTIE,


MARCASEF, în care MARCA este cheie unică, iar MARCASEF este marca persoanei, şef
direct al persoanei respective.
Pentru o persoană care nu are şef direct se poate lua drept valoare pentru MARCASEF –
valoarea zero (dacă toate valorile câmpului MARCA sunt strict pozitive) sau putem lăsa
necompletat câmpul MARCASEF (deci va avea valoarea NULL).
Fie următorul conţinut al tabelei PERSONAL:

MARCA NUME SALARIU SECTIE MARCASEF


10 P 4000 1 0
PRACTICĂ Prof.dr.Victor Felea Pag.16
Asist.drd.Ovidiu Gheorghieş
20 S1 1500 1 10
30 S2 2000 2 10
40 S3 1700 3 10
50 T1 1600 1 20
60 T2 1800 1 20
70 T3 1500 2 30
80 T4 2000 3 40
90 T5 1900 3 40
100 T6 1850 3 40
110 F1 1400 1 60
120 F2 1450 1 60
130 F3 1500 2 70
140 F4 1550 3 90
150 F5 1600 3 90
160 F6 1500 3 90
Conţinutul acestei tabele se poate reprezenta ca o arborescenţă în maniera următoare: Fie I şi
I' cu proprietatea că MARCASEF(I) = MARCA(I'). Atunci definim un arc de la înregistrarea
I' la înregistrarea I. Dacă pentru înregistrarea I avem MARCASEF(I) = 0, atunci I este
rădăcină. Deoarece MARCA este cheie unică, rezultă că pentru I, dacă există nodul imediat
predecesor, atunci acesta este unic. Pentru exemplul considerat, rezultă arborescenţa:

S1 S2 S3

T1 T2 T3 T4 T5 T6

F1 F2 F3 F4 F5 F6

Se remarcă faptul că ordinea înregistrărilor din PERSONAL nu are importanţă în definirea


arborescenţei. Mai mult, o tabelă poate defini mai multe arborescenţe, atunci când există mai
multe rădăcini (înregistrări cu MARCASEF = 0).
La modul general, fie tabela F cu înregistrările F= (I1, ..., Im) şi cimp1 cheie unică pentru F, iar
cimps un câmp ce reprezintă identificatorul şefului direct al lui cimp1. Fie Ij şi Ih două
înregistrări din F cu proprietatea cimps(Ih) = cimp1(Ij). Atunci Ij este şeful direct al lui Ih. Dacă
avem cimps(Ih) = 0, atunci Ih nu are şefi. Toate înregistrările Ih cu cimps(Ih) =0 vor fi rădăcini
de arborescenţe.
Mai general, putem avea o expresie E1 formată din câmpuri ale lui F cu proprietatea E1 –
cheie unică şi o expresie Es, formată, de asemenea, cu câmpuri ale lui F, ale cărei valori
reprezintă şeful direct al înregistrării respective. Pentru înregistrările Ij şi Ih cu proprietatea
Es(Ih) = E1(Ij), Ij va fi şeful direct al lui Ih (şi este unic deoarece E1 este cheie unică). În
mulţimea de arborescenţe vom avea un arc de la Ij la Ih. Dacă pentru Ih, avem Es(Ih) = 0,
atunci Ih nu are şef direct.
Pentru astfel de tabele, ce reprezintă mulţimi de arborescenţe, se pot cere următoarele
interogări:
a) Să se găsească toate persoanele ce nu au şefi,
PRACTICĂ Prof.dr.Victor Felea Pag.17
Asist.drd.Ovidiu Gheorghieş
b) Pentru o persoană să se găsească toţi subordonaţii săi (direcţi şi indirecţi),
c) Pentru o persoană să se găsească toţi şefii ierarhici,
d) Pentru o persoană să se găsescă suma salariilor pentru fiecare secţie a mulţimii
subordonaţilor săi,
e) Pentru o persoană să se găsească salariul maxim pe mulţimea subordonaţilor săi,
f) Pentru o persoană să se listeze mulţimea subordonaţilor săi pe nivele ale
arborescenţei cu rădăcina persoana respectivă.
Rezultă că, într-o astfel de interogare, trebuie să precizăm două elemente:
I) Nodul sau nodurile de plecare ale căutării în cadrul arborscenţelor. Acest lucru se va
face cu componenta:
START WITH <condiţie>
Dacă F = (I1, ..., Im), atunci se evaluează <condiţie> pentru Ij, 1 ≤ j ≤ m şi nodurile de plecare
vor fi toate acelea pentru care <condiţie> este adevărată. Să notăm cu <condiţie>(Ij) evaluarea
respectivă. Să notăm prin MSTART – mulţimea nodurilor de plecare care va fi: MSTART = {Ij |
1≤ j≤m, <condiţie>(Ij) = TRUE}.
II) Sensul de parcurgere al pădurii poate fi:
a) de la rădăcină spre nodurile terminale, sau
b) de la nodurile terminale spre rădăcină.
Acest sens va fi specificat de componenta CONNECT BY pentru sensul a): CONNECT BY
PRIOR E1 = E2, iar în cazul particular al câmpurilor cimp1 şi cimps prin: CONNECT BY
PRIOR cimp1 = cimps.
Pentru parcurgerea în sensul b) se va specifica componenta:
CONNECT BY E1 = PRIOR E2, respectiv în cazul particular al lui cimp1 şi cimps
prin: CONNECT BY cimp1 = PRIOR cimps.
Semantica considerării componentei CONNECT este următoarea:
În cazul a) fie Ij înregistrarea curentă. Fie v1 = cimp1(Ij) şi v2 = cimps(Ij). Se caută toate
înregistrările I α1 ,..., Iα p , cu proprietatea cimps( Iαi ) = v1, i = 1, p . Aceasta înseamnă că Iαi ,
i = 1, p sunt toţi subordonaţii direcţi ai lui Ij. Dacă p=0 (nu există înregistrări cu proprietatea
specificată), atunci Ij este nod terminal.
În cazul b) dacă Ij este înregistrarea curentă şi v2 = cimps(Ij), se caută Ih, astfel încât să
avem: cimp1(Ih)= v2. Dacă există, atunci Ih este unică (este şeful direct pentru Ij), dacă nu
există Ih, atunci Ij nu are şef.
Exemple:
1) Să se afişeze toţi subordonaţii persoanei cu numele 'P'.

SELECT A.MARCA, A.NUME, A.SALARIU,

A.SECTIE, A.MARCASEF

FROM PERSONAL A

CONNECT BY PRIOR A.MARCA = A.MARCASEF

START WITH A.NUME = 'P'


PRACTICĂ Prof.dr.Victor Felea Pag.18
Asist.drd.Ovidiu Gheorghieş
În absenţa componentei ORDER afişarea se realizează într-un mod standard (pe nivele), iar în
cazul unui nivel de la stânga la dreapta.
2) Să se afişeze toţi subordonaţii persoanei cu numele 'S1', inclusiv acesta.

SELECT A.MARCA, A.NUME, A.SALARIU,

A.SECTIE, A.MARCASEF

FROM PERSONAL A

CONNECT BY PRIOR A.MARCA = A.MARCASEF

START WITH A.NUME = 'S1'

În ORACLE există pseudocoloana cu numele LEVEL, o variabilă ce are ca valori numere de


nivel relative în cadrul unei interogări SELECT.
În exemplul 1) pentru linia lui P, LEVEL = 0, pentru linia lui S1, S2, S3, LEVEL are valoarea
1, etc.
Pseudocoloana LEVEL poate fi folosită în comanda SELECT pentru afişarea nivelului relativ
al liniei respective.
3) Aceiaşi interogare ca la 2):

SELECT LEVEL,A.MARCA, A.NUME, A.MARCASEF

FROM PERSONAL A

CONNECT BY PRIOR A.MARCA = A.MARCASEF

START WITH A.NUME = 'S1'

Vom obţine ca rezultat:


0 20 S1 10
1 50 T1 20
1 60 T2 20
2 110 F1 60
2 120 F2 60

4) Să se găsească şefii ierarhici ai lui T3.

SELECT LEVEL,A.MARCA, A.NUME, A.MARCASEF

FROM PERSONAL A

CONNECT BY A.MARCA = PRIOR A.MARCASEF


PRACTICĂ Prof.dr.Victor Felea Pag.19
Asist.drd.Ovidiu Gheorghieş
START WITH A.NUME = 'T3'

Rezultatul comenzii va fi:


0 70 T3 30
1 30 S2 10
2 10 P 0
În cadrul sensului a) (de la rădăcină spre noduri terminale) se furnizează toate
subarborescenţele cu rădăcinile nodurile de plecare (situate în mulţimea MSTART). Dacă dorim
să eliminăm noduri din aceste arborescenţe, vom putea utiliza componenta WHERE a
comenzii SELECT.
5) Să se afişeze toţi subordonaţii lui S3, cu excepţia celor cu marca între 90 şi 100
inclusiv.

SELECT A.MARCA, A.NUME, A.MARCASEF

FROM PERSONAL A

CONNECT BY PRIOR A.MARCA = A.MARCASEF

START WITH A.NUME = 'S3'

WHERE (A.MARCA < 90) OR (A.MARCA >100)

Rezultatul comenzii va fi:


0 40 S3 10
1 80 T4 40
2 140 F4 90
2 150 F5 90
2 160 F6 90

Componenta CONNECT BY poate conţine AND<condiţie3>. În acest caz, dacă Ij este o


înregistrare furnizată obişnuit de CONNECT BY PRIOR E1 = E2 sau CONNECT BY E1 =
PRIOR E2, atunci se evaluează <condiţie3> pentru Ij, notată <condiţie3>(Ij). Dacă
<condiţie3>(Ij)=.F., atunci din rezultat se elimină Ij şi subarborescenţa cu rădăcina Ij. Când
<condiţie3>(Ij) = .T., Ij se tratează în mod obişnuit.
6) Să se afişeze toţi subordonaţii lui S3, cu excepţia lui T5 şi a tuturor subordonaţilor
lui T5.

SELECT A.MARCA, A.NUME, A.MARCASEF

FROM PERSONAL A

CONNECT BY PRIOR A.MARCA = A.MARCASEF AND

(A.NUME ! = 'T5')
PRACTICĂ Prof.dr.Victor Felea Pag.20
Asist.drd.Ovidiu Gheorghieş
START WITH A.NUME = 'S3'

7) Folosind CONNECT cu AND şi componenta WHERE putem elimina atât


subarborescenţe cât şi noduri.
Să se afişeze toţi subordonaţii lui 'P', cu excepţia lui 'T3' şi a tuturor subordonaţilor lui 'T3',
precum şi cu excepţia lui 'T5' şi a tuturor subordonaţilor lui 'T5' şi cu excepţia nodurilor
corespunzătoare lui 'T1' şi 'T6'.

SELECT A.MARCA, A.NUME, A.MARCASEF

FROM PERSONAL A

CONNECT BY (PRIOR A.MARCA = A.MARCASEF) AND

(A.NUME ! = 'T3') AND(A.NUME!='T5')

START WITH A.NUME = 'P'

WHERE (A.NUME ! = 'T1') AND (A.NUME!= 'T6')

În sistemul ORACLE există funcţia LPAD cu sintaxa:


LPAD (expC, expN), unde expC este o expresie de tip caracter, iar expN este o expresie
numerică. Fie h – valoarea lui expN şi s valoarea lui expC. Funcţia dă ca rezultat concatenarea
lui s de h ori. Folosind această funcţie putem afişa indentat înregistrările pe nivele:
8)

SELECT LPAD('',20*LEVEL),LEVEL,A.MARCA, A.NUME

FROM PERSONAL A

START WITH A.NUME = 'T5'

Rezultatul:
0 90 T5
1 140 F4
1 150 F5
1 160 F6
Fraza SELECT poate conţine componenta GROUP BY şi funcţii de grupare:
9) Să se afişeze pentru subordonaţii lui 'P', suma salariilor pe fiecare nivel din
arborescenţa cu rădăcina P.

SELECT LEVEL, SUM(A.SALARIU)

FROM PERSONAL A
PRACTICĂ Prof.dr.Victor Felea Pag.21
Asist.drd.Ovidiu Gheorghieş
CONNECT BY PRIOR A.MARCA = A.MARCASEF

START WITH A.NUME = 'P'

GROUP BY LEVEL

ORDER BY LEVEL

10) Să se afişeze pentru toţi subordonaţii lui 'S1', suma salariilor din fiecare secţie.

SELECT A.SECTIE, SUM(A.SALARIU)

FROM PERSONAL A

CONNECT BY PRIOR A.MARCA = A.MARCASEF

START WITH A.NUME = 'S1'

GROUP BY A.SECTIE

ORDER BY A.SECTIE

Specificăm ordinea considerării în cadrul comenzii SELECT din ORACLE, a componentelor


CONNECT, START, WHERE, GROUP, ORDER, HAVING.
I) Definirea nodurilor de plecare cu ajutorul componentei START.
II) Definirea sensului de parcurgere a) sau b) prin componenta CONNECT BY şi
cuvântul PRIOR.
III) Definirea subarborescenţelor cu rădăcinile găsite la punctul I).
IV) Eliminarea subarborescenţelor din III) utilizând AND<condiţie3> în componenta
CONNECT.
V) Se parcurg arborescenţele rămase în urma lui IV).
VI) Se elimină nodurile din arborescenţele de la V) care nu satisfac <condiţie2> din
componenta WHERE <condiţie2>.
VII) Liniile obţinute se grupează în clase, folosind componenta GROUP BY şi se
calculează pentru fiecare clasă o linie la ieşire.
VIII) Dacă există componenta HAVING, se elimină o parte din aceste linii.
IX) Dacă există ORDER, se ordonează liniile de ieşire conform specificaţiilor din
ORDER.

5. Exprimarea interogărilor complexe sub forma unei fraze


SELECT

În continuare vom considera trei limbaje:


PRACTICĂ Prof.dr.Victor Felea Pag.22
Asist.drd.Ovidiu Gheorghieş
a) Limbajul natural, în care se exprimă interogarea asupra unui număr de tabele (Lnat).
b) Limbajul intermediar, format cu anumite expresii ce au ca operanzi câmpurile
tabelelor folosite în interogare (Lint).
c) Limbajul frazei SELECT, ale cărei cuvinte sunt secvenţe ale unei fraze SELECT
(LSEL).
Ne interesează modul de transformare a unei expresii din limbajul natural Lnat într-o expresie
din limbajul intermediar Lint şi modul de transformare a unei expresii din Lint într-o secvenţă a
unei fraze SELECT, deci LSEL. Scopul final este obţinerea unei fraze SELECT corectă pentru
o interogare în limbajul natural Lnat.
Evident, transformarea interogării din limbaj natural în limbaj intermediar trebuie să ţină cont
de semantica interogării, precum şi de elementele (tabelele, câmpurile), ce apar în interogare.
Transformarea din limbajul intermediar în limbajul frazei SELECT dorim să se realizeze
numai sintactic.
Exemplu: Fie interogarea în Lnat: să se găsească persoanele care au vânzări. În acest caz o
frază SELECT ce realizează interogarea are forma:

SELECT A.NUME, A.PRENUME FROM PERSONAL A

WHERE A.MARCA IN

(SELECT B.MARCAV FROM VANZARI B)

Exemplificăm câteva interogări complexe, care necesită utilizarea limbajului intermediar Lint.
Fie tabela VANZARI cu câmpurile MARCAV, CODSV – codul secţiei, CODPV – codul
produsului vândut, CANTV – cantitatea vândută şi tabela SECTII cu câmpurile: CODS –
codul secţiei, DENS – denumirea secţiei, ETAJ – etajul corespunzător secţiei. Fie date
următoarele interogări în Lnat:
1) Se găsească toate persoanele (MARCA, NUME, PRENUME) din tabela
PERSONAL, cu proprietatea că persoana respectivă vinde cel puţin un produs la toate secţiile
existente în tabela SECTII.
2) Să se găsească toate persoanele (MARCA, NUME, PRENUME) din tabela
PERSONAL, care vând cel puţin două produse la toate secţiile de la etajul 2 şi vând cel puţin
2 produse numai la secţiile de la etajul 2 (la alte secţii nu vând cel puţin 2 produse, adică nu
vând nimic sau vând un singur produs).
Să separăm 2 cazuri în ceea ce priveşte interogarea în limbaj natural: cazul când interogarea
nu implică utilizarea funcţiilor de grupare(i) şi desigur cazul contrar(ii).
(i).Să considerăm situaţia când interogarea nu implică funcţii de grupare. Atunci fraza
SELECT va trebui să conţină componenta de intrare, componenta de ieşire şi componenta
WHERE. Să notăm prin I(T1 a1, ..., Th ah) componenta de intrare a comenzii SELECT, unde
Tj, 1 ≤j ≤ h sunt tabelele de intrare, iar aj este aliasul local al tabelei Tj, 1 ≤j ≤ h. Tabelele Tj
nu trebuie neapărat să fie distincte, dar obligatoriu aliasurile aj sunt distincte.
Notăm prin O(exp1 col1, ..., expq colq) ieşirea comenzii SELECT, unde expi, 1 ≤ i ≤ q sunt
numele expresiilor de ieşire, iar coli este numele atribuit pentru câmpul de ieşire de număr i, 1
≤ i ≤ q.
Referirea la <cimp> definit în tabela Tj se va face prin <aj.cimp>. Notăm prin W<expresie
logică> componenta WHERE a frazei SELECT.
Forma generală a unei expresii în limbajul Lint va fi:
E ≡ I(T1 a1, ..., Th ah) O (exp1 col1,..., expq colq) W<expresie logică>.
PRACTICĂ Prof.dr.Victor Felea Pag.23
Asist.drd.Ovidiu Gheorghieş
Rămâne de exprimat <expresie logică> în limbajul Lint.
Să notăm prin TRAN(E) transformata expresiei E într-o frază SELECT. Avem TRAN(E):

SELECT exp1 AS col1, ..., expq AS colq

FROM T1 a1, ..., Th ah

WHERE TRAN (<expresie logică>)

Vom defini expresii în limbajul Lint şi pentru fiecare E ∈ Lint, definim TRAN(E), care va fi în
general o secvenţă (subcuvânt) al unei fraze SELECT. Pentru unele expresii E, TRAN(E) va
fi o expresie elementară a componentei WHERE a frazei SELECT.
Expresiile limbajului intermediar Lint sunt următoarele:
1) E ≡ aj.cimp ∈(T aj), în care T este o tabelă cu aliasul local aj, iar cimp este definit în
tabela T.
Semantica expresiei E: cimp este definit în T (apare în structura lui T). Definim TRAN(E)
astfel:

SELECT aj.cimp FROM T aj

Se remarcă faptul că TRAN(E) reprezintă mulţimea valorilor pentru cimp din conţinutul
curent al tabelei T.
2) E ≡ (a1.cimp1, ..., am.cimpm) ∈ (T1 a1)× ...×(Tm am), unde Ti, 1 ≤ i ≤ m sunt tabele,
ai este aliasul lui Ti, 1 ≤ i ≤ m, iar cimpi este definit în tabela Ti,
1 ≤ i ≤ m.
Semantica expresiei E este: cimpi este definit în tabela Ti, 1≤i≤m. Transformarea expresiei E
ca segment al frazei SELECT va fi TRAN(E):

SELECT a1.cimp1, ..., am.cimpm

FROM T1 a1,..., Tm am

3) E ≡ (a α1 .cimp1, ..., a α m .cimpm) ∈ (T1 b1)× ...×(Tq bq), unde Ti, 1 ≤ i ≤ q sunt tabele,
bi este aliasul lui Ti, 1 ≤ i ≤ q, a α j , 1 ≤ j ≤ m sunt alias-uri, astfel încât: a α j ∈{b1,..., bq},
j = 1, m . Evident, bi sunt distincte, i = 1, q . Dar a α j pot să coincidă. Dacă a α j = bi, atunci i
este unic şi cimpj este definit în tabela Ti. Expresia de forma 3) este mai generală decât cea de
forma 2), permiţând în lista de câmpuri să apară mai multe câmpuri definite în aceeaşi tabelă.
Semantica expresiei E este: cimpj este definit în tabela Ti, unde i are proprietatea a α j = bi.
Reprezentarea expresiei E ca segment al unei fraze SELECT, notată prin TRAN(E) va fi:

SELECT a α .cimp1, ..., a α .cimpm


1 m

FROM T1 b1,...,Tq bq
PRACTICĂ Prof.dr.Victor Felea Pag.24
Asist.drd.Ovidiu Gheorghieş
4) E ≡ aj.cimp τ{ (T b) [b.cimp']∧E1}
Variabila cimp este definită într-o tabelă cu aliasul aj, T este o tabelă cu aliasul b, iar cimp'
este un câmp definit în tabela T, E1 este o expresie în limbajul intermediar, care conţine drept
cîmpuri numai cele din T. Operatorul τ este diferit de operatorul ∈, deoarece va avea o altă
semantică. Dacă v α1 ...αh este vectorul curent de înregistrări, pentru care se evaluează această
expresie, atunci să notăm cu v1 valoarea lui aj.cimp pentru acest vector, adică v1 =
aj.cimp( v α1 ...αh ). Să notăm cu E( v α1 ...αh ) valoarea expresiei E pentru vectorul v α1 ...αh . Atunci
definim:
TRUE dacă v1 ∈ σE1 ( T ) [cimp']
E( v α1 ...αh ) = 
FALSE altfel
Expresia σ E1 ( T ) notează operatorul de selecţie σ din algebra relaţională, aplicat lui T cu
expresia de selecţie E1. Expresia σ E1 ( T ) [cimp'] notează proiecţia lui σ E1 ( T ) pe atributul
cimp'. Transformarea expresiei E ca segment al frazei SELECT (de astă dată va fi o condiţie
elementară a componentei WHERE a frazei SELECT) este definită astfel:

TRAN(E) ≡ aj.cimp IN (SELECT b.cimp' FROM T b

WHERE TRAN{E1}).

În continuare definim o expresie similară cu cea de tipul 4), dar mai generală:
5) E ≡ (a1.cimp1,..., am.cimpm) τ {(T1 b1)× ...×(Tm bm) [ b1.cimp1′ ,..., b m .cimp′m ]∧E1},
unde Ti sunt tabele cu aliasul bi, i = 1, m , bi .cimp′i este un atribut definit în tabela Ti, 1 ≤ i ≤
m, iar aj.cimpj este un câmp definit în tabela cu aliasul aj, j = 1, m . E1 este o expresie în Lint
care conţine drept cîmpuri numai cele din Ti, în particular poate fi o condiţie join.
Semantica expresiei E este: vectorul valorilor curente pentru ai.cimpi, i = 1, m aparţine
mulţimii definite de produsul cartezian al tabelelor T1, ..., Tm filtrat de E1 şi proiectat pe
atributele cimp1′ ,..., cimp′m . Ca şi în cazul 4), pentru vectorul de înregistrări curent v α1 ...αh
definim E( v α1 ...αh ), deci valoarea expresiei E pentru acest vector.
Fie vectorul de valori w= (w1, ..., wm), unde wi = ai.cimpi( v α1 ...αh ), i = 1, m , deci wi este
valoarea atributului ai.cimpi pentru v α1 ...αh .
Fie M= T1 × T2 × ... × Tm, produsul cartezian al conţinutului curent al tabelelor Tj, j = 1, m .
Definim:
TRUE dacă w ∈ σE1 ( M ) [b1.cimp1′ ,..., b m .cimp′m ]
E( v α1 ...αh ) = 
FALSE altfel
unde σ E1 ( M ) notează operatorul de selecţie σ, aplicat lui M cu expresia de selecţie E1, iar
σ E1 ( M ) [b1.cimp1′ ,..., b m .cimp′m ] notează proiecţia relaţiei σ E1 ( M ) pe atributele
b1.cimp1′ ,..., b m .cimp′m . Transformarea lui E ca segment al frazei SELECT, care va fi o
condiţie elementară a componentei WHERE (ca şi în cazul 4), se realizează prin:

TRAN(E) ≡ (a1.cimp1, ..., am.cimpm) IN


PRACTICĂ Prof.dr.Victor Felea Pag.25
Asist.drd.Ovidiu Gheorghieş
(SELECT b1.cimp1′ ,..., bm.cimp′m

FROM T1 b1, ..Tm bm WHERE

TRAN{E1}).

Se ştie că această condiţie elementară este echivalentă cu:

EXISTS (SELECT b1.cimp1′ ,..., bm.cimp′m

FROM T1 b1, ..Tm bm

WHERE( b1.cimp1′ = a1.cimp1)AND...

AND ( bm.cimp′m = am.cimpm)AND TRAN{E1})

6) Considerăm tot operatorul τ, dar cu o expresie mai generală decât cea de la punctul
5).
E ≡ (a1.cimp1, ..., am.cimpm) τ {(T1 b1)× ...×(Tq bq) [ bi1 .cimp1′ ,..., bim .cimp′m ]∧E1}
unde Tj sunt tabele, j = 1, q , cu aliasurile bj, respectiv; bi j .cimp′j este un câmp definit în
tabela Ti j , j = 1, m , deci ij∈{1, 2, ...,q}, j = 1, m . Aliasurile b1, ...,bq sunt distincte, iar aj.cimpj
este un atribut definit într-o tabelă de alias aj, j = 1, m . E1 este o expresie in Lint care contine
drept cimpuri numai cele din Tj, in particular poate fi o conditie join.
Definim valoarea expresiei E pentru vectorul curent de înregistrări v α1 ...αh . Fie w = (w1,
..,wm), unde wi = ai.cimpi( v α1 ...αh ), i = 1, m şi M = T1× ...× Tq, produsul cartezian al tabelelor Ti,
i = 1, q .
TRUE dacă w ∈ σ E1 ( M ) [bi1 .cimp1′ ,..., bim .cimp′m ]
E( v α1 ...αh ) = 
FALSE altfel
σ E1 ( M ) notează operatorul de selecţie σ, aplicat lui M cu expresia de selecţie E1. Prin
σE1 (M)[ bi1 .cimp1′ ,..., bim .cimp′m ] se notează proiecţia mulţimii σE1 (M) pe
câmpurile bi1 .cimp1′ ,..., bim .cimp′m . Atât in cazul 5), cât şi în cazul 6) trebuie ca tip(aj.cimpj) =
tip( bi j .cimp′j ), j = 1, m , iar pentru 4) tip(aj.cimp) = tip(b.cimp').
Transformarea lui E ca segment al frazei SELECT, care va fi o condiţie elementară a
componentei WHERE, este următoarea:

TRAN(E) ≡ (a1.cimp1, ..., am.cimpm) IN

(SELECT bi .cimp1′ ,..., bi .cimp′m


1 m
PRACTICĂ Prof.dr.Victor Felea Pag.26
Asist.drd.Ovidiu Gheorghieş
FROM T1 b1, ..., Tq bq WHERE TRAN{E1)

Ca şi în cazul 5), TRAN(E) este echivalentă cu o condiţie elementară exprimată prin EXISTS.
Expresia 6) permite ca mai multe câmpuri de forma bi j .cimp′j să fie definite în aceeaşi
tabelă.
7) E ≡ (∃ aj.cimp) [aj.cimp∈(T aj)∧E1]
T este o tabelă de alias aj, cimp este definit în tabela T, iar E1 este o altă expresie în limbaj
intermediar. Simbolul ∃ este cuantificatorul de existenţă.
Semantica expresiei E este: expresia E este adevărată, dacă există o valoare pentru aj.cimp
între valorile tabelei T proiectate pe aj.cimp, astfel încât să fie satisfăcută expresia E1.
Presupunând că am definit TRAN(E1), atunci transformata expresiei E este următoarea:

TRAN(E) ≡ EXISTS (SELECT aj.cimp

FROM T aj

WHERE TRAN(E1))

Evident TRAN(E) este o condiţie elementară a componentei WHERE a frazei SELECT.


Să definim formal semantica expresiei E pentru vectorul de înregistrări curente v α1 ...αh .
Expresia E1 depinde de aj.cimp şi de v α1 ...αh . Pentru o valoare v cu tip(v)=tip(aj.cimp) şi
vectorul v α1 ...αh , să notăm prin E1(v, v α1 ...αh ) rezultatul evaluării expresiei E1. Definim
E( v α1 ...αh ) prin:
( )
TRUE, dacă ∃v ∈ T[aj.cimp], astfel încât: E1 v, v α1 ...αh = TRUE
E( v α1 ...αh ) = 
FALSE altfel
Expresia T(aj.cimp) notează proiecţia tabelei T pe atributul aj.cimp.
Exemplul 10: Fie date tabelele PERSONAL şi VANZARI cu câmpurile specificate anterior.
Se cere să se afişeze toate persoanele care au cel puţin o vânzare. Expresia E ce precizează
intrarea şi ieşirea comenzii va fi:
E ≡O(A.MARCA, A.NUME, A. PRENUME) I (PERSONAL A) W E1
Expresia E1 trebuie să reprezinte faptul că valoarea lui A.MARCA să apară în VANZARI, în
sensul să existe în VANZARI o înregistrare cu valoarea lui MARCAV egală cu valoarea
curentă pentru A.MARCA.
E1 ≡ (∃ B.MARCAV) [B.MARCAV∈(VANZARI B)∧ E2]
E2 ≡ (B.MARCAV = A.MARCA)
Rezultă că TRAN(E2) ≡ E2.

TRAN(E1) ≡ EXISTS (SELECT B.MARCAV

FROM VANZARI B

WHERE TRAN(E2))
PRACTICĂ Prof.dr.Victor Felea Pag.27
Asist.drd.Ovidiu Gheorghieş
TRAN(E) ≡ SELECT A.MARCA, A.NUME, A.PRENUME

FROM PERSONAL A

WHERE TRAN(E1)

Realizând înlocuirile obţinem comanda:

SELECT A.MARCA, A.NUME, A.PRENUME

FROM PERSONAL A

WHERE EXISTS (SELECT B.MARCAV

FROM VANZARI B

WHERE (B.MARCAV = A.MARCA))

8) E ≡ ∃ (a1.cimp1, ..., am.cimpm)[ a1.cimp1∈ (T1 a1) ∧ ... ∧ am.cimpm∈ (Tm am) ∧ E1]
Semantica lui E este: există un vector de valori pentru a1.cimp1, ..., am.cimpm, astfel încât să
fie satisfăcută expresia E1. Variabila aj.cimpj este definită în tabela tj cu aliasul aj, 1 ≤ j ≤ m.
Evident expresia E1 poate depinde de variabilele aj.cimpj, 1 ≤ j ≤ m. Câmpurile aj.cimpj, 1≤ j ≤
m se mai numesc variabile individuale ale expresiei E. Transformarea lui E în limbajul LSEL
va fi o expresie elementară a componentei WHERE:

TRAN(E) ≡ EXISTS (SELECT a1.cimp1, ..., am.cimpm

FROM Tβ b1,...., Tβ
1 P
bp

WHERE TRAN(E1) )

unde b1, ..., bp sunt aliasurile distincte din şirul a1, ..., am. Pentru fiecare aj există un unic bk, cu
proprietatea aj = bk; în acest caz aj.cimpj este definit în tabela Tβk cu aliasul bk. Este deci
posibil, ca mai multe atribute să fie definite în aceeaşi tabelă. Desigur că, o aceeaşi tabelă
poate să apară de mai multe ori în componenta FROM, dar cu alias-uri diferite.
Să specificăm formal semantica expresiei E pentru vectorul de înregistrări curente v α1 ...αh . În
general, expresia E1 depinde de a1.cimp1, ..., am.cimpm şi de v α1 ...αh . Dacă vj este o valoare de
tip egal cu tipul lui aj.cimpj, atunci E1 se evaluează pentru v1, ..., vm şi v α1 ...αh . Să notăm
rezultatul acestei evaluări cu E1(v1, ..., vm, v α1 ...αh ). Evaluarea expresiei E pentru vectorul
curent v α1 ...αh o vom defini prin:
PRACTICĂ Prof.dr.Victor Felea Pag.28
Asist.drd.Ovidiu Gheorghieş

( )
TRUE, dacă ∃v j ∈ Tj[a j .cimp j ], j = 1, m a.î. E1 v1 ,..., v m , vα1 ...α h = TRUE
E( v α1 ...αh ) = 
FALSE altfel
Notaţia T[aj.cimpj] specifică proiecţia tabelei T pe aj.cimpj, j = 1, m .
9) E ≡ (∀aj.cimp)[aj.cimp∈ (T aj)⇒ E1]
aj.cimp este un atribut definit în tabela T cu aliasul aj, simbolul ∀ este cuantificatorul
universal, iar simbolul "⇒" este implicaţia logică. E1 este o expresie a limbajului Lint.
Semantica lui E este următoarea: E este adevărată dacă pentru orice valoare a câmpului
aj.cimp din tabela T, avem adevărată expresia E1. Formal, această semantică o vom exprima
astfel: fie v α1 ...αh vectorul de înregistrări curente la care se aplică E. Expresia E1 depinde de
aj.cimp şi de vectorul de înregistrări v α1 ...αh .
Să notăm cu E1(v, v α1 ...αh ) valoarea expresiei E1 pentru valoarea v a câmpului aj.cimp şi pentru
vectorul de înregistrări v α1 ...αh . Valoarea lui E pentru v α1 ...αh , notată E(vα1 ...αh ) va fi definită
astfel:
( )
TRUE, dacă pentru ∀v ∈ T[a j .cimp], avem E1 v, v α1 ...αh = TRUE
E( v α1 ...αh ) = 
FALSE altfel
Notaţia T[aj.cimp] specifică proiecţia tabelei T pe câmpul aj.cimp.
Pentru exprimarea expresiei E în limbajul LSEL, va trebui să transformăm formula E cu
cuantificatorul "∀" într-o formulă echivalentă ce utilizează cuantificatorul "∃", deoarece în
LSEL putem exprima numai cuantificatorul "∃", prin expresii elementare de forma: EXISTS
(SELECT ...).
Formula din limbajul Lint de forma E ≡ (∀x)[F ⇒ E1] este echivalentă cu (∀x)[(F ∨E1)]≡  
(∀x)[(F ∨E1)] ≡(∃ x)[F ∧ E1]. Rezultă că transformarea lui E în limbajul LSEL va fi:

TRAN(E) ≡ NOT EXISTS (SELECT aj.cimp


FROM T aj
WHERE NOT TRAN(E1))
Desigur că, se calculează în prealabil TRAN(E1). Simbolul "" notează negaţia logică.
10) E ≡ ∀( a1.cimp1, ..., am.cimp)[ a1.cimp1∈ (T1 a1) ∧ ... ∧ am.cimpm∈ (Tm am) ⇒ E1], unde
Ti sunt tabele cu aliasurile ai, i = 1, m , iar ai.cimpi este definit în tabela Ti.
Semantica lui E: expresia E este adevărată dacă pentru orice valoare a câmpurilor aj.cimpj din
tabela Tj, j = 1, m , avem expresia E1 adevărată. Expresia E1 poate depinde atât de aj.cimpj,
j = 1, m , cât şi de v α1 ...αh . Dacă vj este o valoare de tip egal cu tip(aj.cimpj), j = 1, m , atunci să
notăm prin E1(v1, ..., vm, v α1 ...αh ), evaluarea lui E1 pentru şirul de valori vj, j = 1, m şi vectorul
de înregistrări v α1 ...αh .
Valoarea lui E pentru vectorul v α1 ...αh este astfel definită:
TRUE, dacă pentru ∀v j ∈ Tj[a j .cimp j ], j = 1, m , avem

E( v α1 ...αh ) =  ( )
E1 v1 ,..., v m , v α1 ...αh = TRUE

FALSE altfel
PRACTICĂ Prof.dr.Victor Felea Pag.29
Asist.drd.Ovidiu Gheorghieş
Tj[aj.cimpj] notează proiecţia tabelei Tj pe atributul aj.cimpj, j = 1, m . Este posibil ca mai
multe câmpuri aj.cimpj să fie definite în aceeaşi tabelă. De aceea, ca şi la expresiile 8), să
notăm cu b1, ..., bp aliasurile distincte din şirul a1, ..., am. Avem astfel tabelele Tβ1 ,...., TβP cu
aliasurile b1, ..., bp, respectiv. Pentru fiecare aj (1≤j≤m), există unic bk, (1≤k≤p), astfel incât aj
= bk. Atunci aj.cimpj este definit în tabela Tβk cu aliasul bk. O tabelă poate să apară de mai
multe ori în componenta FROM, dar cu alias-uri diferite. Ţinând cont de transformarea
cuantificatorului "∀" într-o expresie în care se utilizează cuantificatorul "∃" şi negaţia "",
definită la expresiile de tip 9), rezultă că transformata TRAN(E) a expresiei E va fi:

TRAN(E) ≡ NOT EXISTS

(SELECT a1.cimp1, ..., am.cimpm

FROM Tβ 1
b1,...., Tβ P
bp

WHERE NOT TRAN (E1))

Desigur în expresiile de tip 7), 8), 9) şi 10), expresia E1 poate fi o expresie în limbaj
intermediar sau orice expresie logică a limbajului LSEL. În ultimul caz, considerăm TRAN(E1)
= E1. Rezultă că, utilizând expresiile 1) -10), putem construi expresii în Lint şi în acelaşi timp
să obţinem transformarea lor în expresii în LSEL, rezultând în final o frază SELECT
corespunzătoare expresiei Lint.
Exemplul 11. Considerăm tabelele:
a) SECTII cu atributele: CODS (codul secţiei) – cheie unică, DENS – denumirea secţiei,
ETAJ – etajul secţiei respective.
b) PRODUS cu atributele: CODP- codul produsului – cheie unică, DENP – denumire produs,
PRET- preţul unitar al produsului respectiv.
c) FURNIZOR cu CODF- codul furnizorului – cheie unică, NUMEF – nume furnizor,
ADRESAF – adresa furnizorului.
d) FURNIZEAZA cu CODF – codul furnizorului, CODS – codul secţiei în care se face
furnizarea, CODP – codul produsului furnizat, CANT – cantitatea furnizată din produsul
CODP la secţia CODS de către furnizorul CODF.
1) Fie dată interogarea: să se găsească produsele (A.CODP, A.DENP) din PRODUS (aliasul
A) cu proprietatea că A.CODP este furnizat la toate secţiile din tabela SECTII.
E ≡ O(A.CODP, A.DENP) I (PRODUS A) W E1
E1 corespunde la proprietatea A.CODP este furnizat la toate secţiile din tabela SECTII.
E1≡ (∀ B.CODS) [B.CODS ∈ (SECTII B)⇒ E2]
E2 corespunde la faptul că perechea (A.CODP, B.CODS) apare în tabela FURNIZEAZA.
E2 ≡ (A. CODP, B. CODS) τ (FURNIZEAZA C) [C.CODP, C.CODS]
Folosind regulile de transformare definite pentru expresii, obţinem:

TRAN(E2) ≡ (A.CODP, B.CODS) IN


(SELECT C.CODP, C.CODS
FROM FURNIZEAZA C)
PRACTICĂ Prof.dr.Victor Felea Pag.30
Asist.drd.Ovidiu Gheorghieş
TRAN(E1) ≡ NOT EXISTS

(SELECT B. CODS

FROM SECTII B

WHERE NOT TRAN(E2))

TRAN(E) ≡ SELECT A.CODP, A.DENP

FROM PRODUS A

WHERE TRAN(E1)

Ţinând cont de faptul că expresia NOT ((A.CODP, B.CODS) IN (SELECT...)) este


echivalentă cu (A.CODP, B.CODS) NOT IN (SELECT ...), obţinem:

TRAN(E) ≡ SELECT A.CODP, A.DENP


FROM PRODUS A
WHERE NOT EXISTS
(SELECT B.CODS
FROM SECTII B
WHERE (A.CODP,B.CODS) NOT IN
(SELECT C.CODP, C.CODS
FROM FURNIZEAZA C))
2) Considerăm acum interogarea:
Să se găsească produsele din tabela PRODUS cu proprietatea că pentru orice secţie din tabela
SECTII situată la etajul 1, produsul este furnizat de toţi furnizorii din tabela FURNIZORI.
E ≡ O(A.CODP, A.DENP) I (PRODUS A) W E1
E1 ≡ (∀ B.CODS) [(B.CODS)∈(SECTII B)∧(B.BETAJ = 1)⇒E2]
E2 ≡ (∀ C.CODF) [(C.CODF)∈(FURNIZOR C) ⇒E3]
E3 se asociază pentru proprietatea: tripleta A.CODP, B.CODS, C.CODF apare în tabela
FURNIZEAZA.
E3 ≡ (A.CODP, B.CODS, C.CODF) τ (FURNZEAZA D) [D.CODP, D.CODS, D.CODF]
Rezultă expresiile respective din limbajul LSEL:

TRAN(E3) ≡ (a.codp, b.cods, c.codf) IN


PRACTICĂ Prof.dr.Victor Felea Pag.31
Asist.drd.Ovidiu Gheorghieş
(SELECT D.CODP, D.CODS, D.CODF

FROM FURNIZEAZA D)

TRAN(E2) ≡ NOT EXISTS

(SELECT C.CODF

FROM FURNIZOR C

WHERE NOT TRAN(E3))

TRAN(E1) ≡ NOT EXISTS

(SELECT B.CODS

FROM SECTII B

WHERE B.ETAJ = 1 AND NOT TRAN(E2))

TRAN(E) ≡ SELECT A.CODP, A.DENP

FROM PRODUS A

WHERE TRAN(E1)

Realizând înlocuirile se obţine comanda SELECT pentru interogarea dată. Desigur, se poate
înlocui subexpresia NOT((c1, c2) IN (SELECT...)) prin (c1, c2) NOT IN (SELECT..) şi NOT
NOT EXISTS (SELECT ...) prin EXISTS (SELECT ...).
3) Fie dată interogarea: să se găsească produsele din PRODUS cu proprietatea că pentru
orice secţie din SECTII, situată la etajul 1, produsul respectiv este furnizat de toţi furnizorii
din FURNIZORI şi produsul respectiv este furnizat de toţi furnizorii din FURNIZORI numai
la secţiile de la etajul 1.
Se remarcă că interogarea are o primă parte ce coincide cu interogarea precedentă, iar partea a
doua trebuie analizată separat.
E ≡ O(A.CODP, A.DENP) I (PRODUS A) W (E1 ∧ E1′ ).
E1 este expresia din interogarea precedentă. Rămîne să exprimăm expresia E1′ . Aceasta
corespunde la interogarea în limbajul Lnat: dacă produsul respectiv (A.CODP) este furnizat de
toţi furnizorii, atunci acest produs este furnizat numai secţiilor de la etajul 1(pentru orice
secţie s cu ETAJ diferit de 1, A.CODP nu îi este furnizat).
E1′ ≡ (∀E.CODF) [E.CODF ∈(FURNIZOR E) ∧ (A.CODP, E.CODF) τ
PRACTICĂ Prof.dr.Victor Felea Pag.32
Asist.drd.Ovidiu Gheorghieş
FURNIZEAZA F [F.CODP, F.CODF]⇒ E′2 ]
E′2 ≡ (∀G.CODS)[G.CODS∈(SECTII G) ∧ (G.ETAJ ≠ 1) ⇒ (A.CODP, G.CODS) NOT
τ FURNIZEAZA H[H.CODP, H.CODS]]

TRAN( E′2 ) ≡ NOT EXISTS

(SELECT G.CODS
FROM SECTII G
WHERE (G.ETAJ<>1)AND
(A.CODP,G.CODS)IN
(SELECT H.CODP, H.CODS
FROM FURNIZEAZA H))
TRAN( E1′ ) ≡ NOT EXISTS

(SELECT E.CODF

FROM FURNIZOR E

WHERE (A.CODP, E.CODF) IN

(SELECT F.CODP, F.CODF

FROM FURNIZEAZA F)

AND NOT TRAN( E′2 ))

TRAN(E) ≡ SELECT A.CODP, A.DENP

FROM PRODUS A

WHERE TRAN(E1) AND TRAN( E1′ )

Înlocuind TRAN(E1) din exemplul precedent şi TRAN( E1′ ) calculat mai sus, se obţine
TRAN(E) o interogare SELECT ce realizează interogarea dorită (evident nu este singura
comandă SELECT pentru interogarea dată).
(ii).Să considerăm acum cazul când interogarea în limbaj natural conţine expresii de
grupare pentru calcule de forma minim, maxim, sumă, numărare, medie de valori.
PRACTICĂ Prof.dr.Victor Felea Pag.33
Asist.drd.Ovidiu Gheorghieş
O metodă pentru abordarea unor astfel de interogări va fi utilizarea definirii de tabele
temporare în componenta FROM a comenzii SELECT.
În acest caz componenta FROM a comenzii SELECT are forma:
FROM T1 a1, ..., Th ah
Ti este fie numele unei tabele, fie o expresie de forma (SELECT ...), deci o subselecţie ce
defineşte prin comanda SELECT o tabelă numită tabelă temporară.
O astfel de subselecţie conţine componenta expresiile de ieşire, componenta FROM
(obligatorii) şi poate conţine GROUP BY, HAVING cu expresii funcţii de grupare şi
subselecţii în operandul al doilea al unei expresii din HAVING.
Subinterogarea ce defineşte tabela temporară poate avea un număr oarecare de subselecţii şi
nivele de selecţie.
Rezultă că o comandă SELECT ce are în componenta FROM numai tabele temporare va avea
componentele de ieşire şi componenta FROM de tipul următor:

SELECT exp1[AS c1], ..., expq[AS cq]

FROM (SELECT ...) A1,

(SELECT ...) A2,...,

(SELECT ...) Ah

Subselecţiile din FROM definesc tabele temporare, notate cu T1' ,..., Th' , având aliasurile A1, ...
,Ah, respectiv. Aceste aliasuri sunt obligatorii pentru a face referinţă la câmpurile tabelelor
Tj′, j = 1, h . Din punct de vedere al execuţiei acestei comenzi SELECT, se evaluează întâi
aceste tabele temporare. Evident aliasurile A1, A2,... ,Ah sunt distincte.
Am notat expresia generală în limbajul Lint ce corespunde unei interogări fără GROUP BY şi
HAVING prin:
E≡ O(exp1 c1, ..., expq cq) I(T1 a1, ..., Th ah) W E1,
unde E1 este o expresie în limbajul Lint, construită utilizând expresiile de tip 1) – 10) ale lui
Lint.
În cazul în care, interogarea implică funcţii agregat, atunci vom nota expresia în limbajul Lint
prin:
E≡ O(exp1 c1, ..., expq cq) I(T1 a1, ..., Th ah) W E1 G g1, ..., gs H E 2
unde E1 şi E2 sunt expresii în limbaj intermediar, gi, i = 1,s sunt expresiile de grupare.
Expresiile expj, j = 1, q pot conţine funcţii agregat. Expresia E2 are una din următoarele
forme:
1) E2 ≡ expi θ <constantă>, unde expi este una din expresiile de ieşire ale comenzii, θ
este un operator de comparare, iar <constantă> are tipul lui expi. Semantica lui E2: se reţin
clasele de înregistrări calculate de componenta Gg1, ...,gs care satisfac expresia E2.
2) E2 ≡ COUNT(cimp) θ <constantă>, unde cimp este definit în una din tabelele de
intrare, θ este operator de comparare, <constantă> este de tip numeric. Se calculează E2
pentru fiecare clasă de înregistrări definită de componenta Gg1, ...,gs. Se reţin clasele ce
satisfac expresia E2.
PRACTICĂ Prof.dr.Victor Felea Pag.34
Asist.drd.Ovidiu Gheorghieş
3) E2 ≡ expi θ (E"), unde E" este o expresie în limbajul Lint de forma E" ≡ O(exp')I(T1
b1, ..., Tr br) W E3 G g1′ ,..., g′h H E′2 unde exp' este unica expresie de ieşire a expresiei E" şi
are acelaşi tip cu expi, E3 este expresia din componenta WHERE, g′j , j = 1, t sunt câmpurile
după care se face gruparea, iar E′2 este expresia din componenta HAVING. Simbolul θ este
un operator de comparare.
Expresia E′2 poate utiliza, ca operanzi, expresiile de ieşire expj ale expresiei E.
Pentru 1) şi 2) transformarea lui E2 ca secvenţă a frazei SELECT se realizează prin TRAN(E2)
= E2. Pentru 3) avem: TRAN(E2) ≡ expi θ (TRAN(E")).
Expresia E2 din 3) este adevărată pentru clasa de înregistrări curentă, dacă valoarea lui expi
pentru clasa curentă este în relaţia θ cu valoarea lui exp', calculată de fraza SELECT
corespunzătoare lui E".
4) E2 ≡ <constantă> θ (E"), unde E" se defineşte ca la 3).
Expresia E2 este adevărată pentru clasa curentă, dacă valoarea constantei <constantă>
este în relaţia θ cu exp' calculată de fraza SELECT corespunzătoare lui E". Dacă expresia E 2′
din expresia E" face referiri la expresiile expj, 1≤ j ≤ q de ieşire ale expresiei E, atunci
valoarea exp' din E" depinde de clasele definite de expresia E.
Transformarea ca secvenţă a frazei SELECT va fi:
TRAN(E2) ≡ constantă θ (TRAN(E")).
Observaţie: În cazurile 3) şi 4) TRAN(E2) va avea forma sintactică:
expi θ (SELECT exp' FROM ...), respectiv
constanta θ (SELECT exp' FROM ...)
Atât în expresiile de tipul 1) cât şi în cele de tipul 3), drept prim operand, putem avea o
expresie, ce are ca elemente primare expresiile de ieşire: exp1, ..expq.
De asemenea, drept prim operand poate fi o expresie ce conţine funcţii agregat, expresie ce
corespunde unei clase de înregistrări definită de componenta GROUP BY (G g1, ..., gs) şi care
se evaluează pentru fiecare clasă definită de GROUP BY.
Rezultă că, în cadrul componentei H avem o clasă curentă de considerat, spre deosebire de
componenta W, unde avem un vector curent de înregistrări. Clasele sunt calculate de
componenta G în maniera descrisă în paragraful I.1.
Exemplul 12. Considerăm tabelele SECTII, PRODUS, FURNIZOR, FURNIZEAZA,
VANZARI specificate mai sus.
Se cer produsele (din tabela PRODUS) furnizate la cel puţin o secţie s cu proprietatea că
valoarea totală a vânzărilor realizate în s este mai mare decât o treime din valoarea totală de
vânzare.
Pentru realizarea interogării, putem defini două tabele temporare: T1 ce utilizează funcţia
agregat SUM pentru a calcula valoarea totală a vânzărilor pentru o secţie şi T2 ce calculează
valoarea totală a vânzărilor pentru toate secţiile. Tabela T1 va calcula pentru fiecare s,
valoarea de vânzare realizată la secţia s, iar T2 va calcula valoarea tuturor vânzărilor.
Pentru T1 avem expresia E1:
E1 ≡ O(F.CODSV, SUM(F.CANTV * E.PRET) AS VAL1) I (PRODUS E,
VANZARI F) W (E.CODP = F.CODPV) G F.CODSV
Pentru T2 avem expresia E2:
E2 ≡ O(SUM(H.CANTV * I.PRET) AS VAL2) I (VANZARI H, PRODUS I) W
(H.CODPV = I.CODP)
Interogarea dată va avea expresia E:
E≡ O(C.CODP, C.DENP) I((E1) A, (E2) B, PRODUS C) W α
α ≡ (∃D.CODS) [D.CODS ∈ SECTII D ∧ β ∧ γ]
PRACTICĂ Prof.dr.Victor Felea Pag.35
Asist.drd.Ovidiu Gheorghieş
β ≡ (C.CODP, D.CODS) τ FURNIZEAZA [H.CODP, H.CODS]
γ ≡ (D.CODS = A.CODSV)∧(A.VAL1* 3 > B.VAL2)
Transformările ca secvenţe ale frazei SELECT vor fi:

TRAN(β) ≡ (C.CODP, D.CODS) IN

(SELECT H.CODP, H.CODS

FROM FURNIZEAZA H)

TRAN(γ) ≡(D.CODS = A.CODSV) AND (A.VAL1*3>B.VAL2)

TRAN(α) ≡ EXISTS

(SELECT D.CODS

FROM SECTII D

WHERE TRAN(β) AND TRAN(γ))

TRAN(E) ≡ SELECT C.CODP, C.DENP

FROM (TRAN(E1)) A,(TRAN(E2)) B,PRODUS C

WHERE TRAN(α)

TRAN(E1) ≡ SELECT F.CODSV,

SUM(F.CANTV*E.PRET) AS VAL1
FROM PRODUS E, VANZARI F
WHERE (E.CODP =F.CODPV)
GROUP BY F.CODSV
TRAN(E2) ≡ SELECT SUM(H.CANTV*I.PRET) AS VAL2

FROM VANZARI H, PRODUS I

WHERE (H.CODPV = I.CODP)


PRACTICĂ Prof.dr.Victor Felea Pag.36
Asist.drd.Ovidiu Gheorghieş
Realizând înlocuirile, vom avea comanda ce corespunde interogării date:

SELECT C.CODP, C.DENP

FROM

(SELECT F.CODSV,SUM(F.CANTV*E.PRET) AS VAL1

FROM PRODUS E, VANZARI F

WHERE (E.CODP =F.CODPV)

GROUP BY F.CODSV) A,

(SELECT SUM(H.CANTV*I.PRET) AS VAL2

FROM VANZARI H, PRODUS I

WHERE (H.CODPV = I.CODP)) B,

PRODUS C

WHERE EXISTS

(SELECT D.CODS

FROM SECTII D

WHERE (C.CODP, D.CODS) IN

(SELECT H.CODP, H.CODS

FROM FURNIZEAZA H)

AND (D.CODS = A.CODSV)

AND (A.VAL1*3>B.VAL2) )

Exemplul 13.Să se găsească secţiile s (din SECTII) cu proprietatea că valoarea totală a


vânzărilor în acea secţie s este mai mare decât jumătate din valoarea totală furnizată secţiei s.
PRACTICĂ Prof.dr.Victor Felea Pag.37
Asist.drd.Ovidiu Gheorghieş
Vom considera două tabele temporare T1 şi T2, în care T1 va conţine perechi de forma (s1,v1),
unde v1 este valoarea vânzărilor din secţia s1, iar T2 conţine perechile de forma (s2, v2), unde
v2 este valoarea totală a produselor furnizate secţiei s2.
T1 este definită de expresia E1:
E1 ≡ Ο(E.CODSV, SUM(E.CANTV*F.PRET)AS VAL1)I(VANZARI E, PRODUS F) W
(E.CODPV = F.CODP) G E.CODSV

TRAN(E1)≡ SELECT E.CODSV,

SUM(E.CANTV*F.PRET) AS VAL1

FROM VANZARI E, PRODUS F

WHERE (E.CODPV = F.CODP)

GROUP BY E.CODSV

T2 va fi definită de expresia E2:


E2 ≡ Ο(I.CODS, SUM(I.CANT*J.PRET) AS VAL2) I (FURNIZEAZA I, PRODUS J)
W(I.CODP = J.CODP) G I.CODS

TRAN (E2)≡ SELECT I.CODS,

SUM(I.CANT*J.PRET) AS VAL2

FROM FURNIZEAZA I, PRODUS J

WHERE (I.CODP = J.CODP)

GROUP BY I.CODS

Pentru interogarea dată considerăm expresia E din limbajul Lint:


E ≡ O(C.CODS) I ((E1) A, (E2) B, SECTII C) W α
α≡(C.CODS = A.CODSV) ∧ (C.CODS = B.CODS) ∧ (A.VAL1*2>B.VAL2)

TRAN (E)≡ SELECT C.CODS

FROM (TRAN(E1)) A,

(TRAN(E2)) B,

SECTII C
PRACTICĂ Prof.dr.Victor Felea Pag.38
Asist.drd.Ovidiu Gheorghieş

WHERE TRAN(α)

TRAN(α) se obţine din α înlocuind conjuncţia ∧ cu AND.


Realizând substituţiile, obţinem:

SELECT C.CODS

FROM

(SELECT E.CODSV, SUM(E.CANTV*F.PRET) AS VAL1

FROM VANZARI E, PRODUS F

WHERE (E.CODPV = F.CODP)

GROUP BY E.CODSV) A,

(SELECT I.CODS, SUM(I.CANT*J.PRET) AS VAL2

FROM FURNIZEAZA I, PRODUS J

WHERE (I.CODP = J.CODP)

GROUP BY I.CODS) B,

SECTII C

WHERE (C.CODS = A.CODSV) AND

(C.CODS = B.CODS) AND (A.VAL1*2>B.VAL2)

Aceeaşi interogare o putem exprima printr-o frază SELECT în maniera următoare:

SELECT C.CODS
FROM SECTII C, VANZARI A, PRODUS F
WHERE (C.CODS=A.CODSV) AND (A.CODPV=F.CODP)
GROUP BY C.CODS
HAVING SUM(A.CANTV*F.PRET)*2>
PRACTICĂ Prof.dr.Victor Felea Pag.39
Asist.drd.Ovidiu Gheorghieş
(SELECT SUM(I.CANT*J.PRET)
FROM FURNIZEAZA I, PRODUS J
WHERE (I.CODP=J.CODP) AND
(I.CODS=C.CODS)
GROUP BY I.CODS)
Primul operand al componentei HAVING calculează pentru o secţie C.CODS, dublul valorii
totale vândute de produse în secţia C.CODS.
Al doilea operand al componentei HAVING calculează valoarea totală a produselor furnizate
secţiei C.CODS.
Practică - Modul II

OVIDIU GHEORGHIEŞ
ADRIANA GHEORGHIEŞ

Copyright
c 2002-2007 Ovidiu Gheorghieş, Adriana Gheorghieş
40
Cuprins

1 Introducere 43

2 Modele de dezvoltare a programelor 47


2.1 Etapele dezvoltării programelor . . . . . . . . . . . . . . . . . . . 47
2.1.1 Analiza cerinţelor . . . . . . . . . . . . . . . . . . . . . . . 48
2.1.2 Proiectarea arhitecturală . . . . . . . . . . . . . . . . . . 48
2.1.3 Proiectarea detaliată . . . . . . . . . . . . . . . . . . . . . 48
2.1.4 Scrierea codului . . . . . . . . . . . . . . . . . . . . . . . . 48
2.1.5 Integrarea componentelor . . . . . . . . . . . . . . . . . . 48
2.1.6 Validare . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
2.1.7 Verificare . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
2.1.8 Întreţinere . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
2.2 Modelul cascadă . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
2.3 Modelul cascadă cu ı̂ntoarcere . . . . . . . . . . . . . . . . . . . . 50
2.4 Prototipizarea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
2.5 Metode formale . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
2.6 Modelul ı̂n spirală . . . . . . . . . . . . . . . . . . . . . . . . . . 53
2.7 Rational Unified Process . . . . . . . . . . . . . . . . . . . . . . . 54
2.7.1 Pornire . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
2.7.2 Rafinare . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
2.7.3 Construire . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
2.7.4 Tranzitie . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
2.8 Extreme Programming . . . . . . . . . . . . . . . . . . . . . . . . 57

3 Concepte orientare obiect 61


3.1 Obiecte. Clase . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
3.2 Principiile dezvoltării orientată obiect . . . . . . . . . . . . . . . 62
3.2.1 Încapsulare . . . . . . . . . . . . . . . . . . . . . . . . . . 62
3.2.2 Mostenirea . . . . . . . . . . . . . . . . . . . . . . . . . . 63
3.2.3 Polimorfismul . . . . . . . . . . . . . . . . . . . . . . . . . 63

4 Modelare 65
4.1 Introducere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.2 Modele. Limbaje de modelare . . . . . . . . . . . . . . . . . . . . 66

41
5 UML 69
5.1 Scurt istoric . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
5.2 Ce este UML? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
5.3 Organizarea modelelor-Pachete . . . . . . . . . . . . . . . . . . . 70
5.4 Diagrama de clase . . . . . . . . . . . . . . . . . . . . . . . . . . 72
5.4.1 Clase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
5.4.2 Relaţii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
5.5 Diagrama de interacţiuni . . . . . . . . . . . . . . . . . . . . . . . 74
5.5.1 Diagrama de secvenţă . . . . . . . . . . . . . . . . . . . . 76
5.5.2 Diagrama de colaborare . . . . . . . . . . . . . . . . . . . 78

6 Modele de proiectare 79
6.1 GRASP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
6.1.1 Information Expert . . . . . . . . . . . . . . . . . . . . . . 80
6.1.2 Creator . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
6.1.3 Low coupling (cuplaj redus) . . . . . . . . . . . . . . . . . 83
6.1.4 High Cohesion (coeziune mare) . . . . . . . . . . . . . . . 84
6.1.5 Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
6.2 Singleton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
6.3 Composite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
6.4 Iterator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

7 Testare 93
7.1 Metode de testare . . . . . . . . . . . . . . . . . . . . . . . . . . 96
7.1.1 Testarea funcţională (Black-box testing) . . . . . . . . . . 96
7.1.2 Testarea stucturală (White-box testing) . . . . . . . . . . 97
7.1.3 Testarea la integrare . . . . . . . . . . . . . . . . . . . . . 98
7.1.4 Testarea interfeţelor . . . . . . . . . . . . . . . . . . . . . 98
7.1.5 Testarea la stress . . . . . . . . . . . . . . . . . . . . . . . 99
7.1.6 Testarea orientată obiect . . . . . . . . . . . . . . . . . . 99

42
Capitolul 1

Introducere

Ştiinţa calculatoarelor este un domeniu relativ nou. Primele calculatoare au


fost construite la mijlocul anilor 1940 şi de atunci au avut loc dezvoltări spec-
taculoase.
Î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 admite ı̂n 1995 că lucrurile s-au
schimbat ı̂n ultimele două decenii.
Următoarele exemple oferă o imagine asupra gradului de complexitate la
care au ajuns programele:

• Sistemul de rezervare a biletelor pentru compania aeriană KLM conţinea,


ı̂n anul 1992, două milioane de linii de cod ı̂n limbaj de asamblare;

• Sistemul de operare System V versiunea 4.0 (UNIX) a fost obţinut prin


compilarea a 3 700 000 linii de cod;

• Programele scrise pentru naveta spaţială NASA au circa 40 de milioane


de linii de cod obiect;

• Pentru realizarea sistemului de operare IBM OS360 au fost necesari 5000


de ani-om.

Creşterea programelor ı̂n dimensiune şi complexitate a depăsit cu mult pro-


gresele făcute ı̂n domeniul tehnicilor de programare. De aceea, programarea a
devenit şi a rămas mai mult o artă decât o meserie.
Pentru a contracara ceea ce se prefigura ca fiind o criză a programării, a fost
propus ı̂n anul 1968 termenul de “ingineria programării” (software engineering),
ı̂ntr-un mod oarecum provocator. Se dorea ca arta programării să ı̂mprumute
din rigoarea ştiintelor inginereşti pentru a putea livra programe la timp şi ı̂n
mod economic.
Paralela cu ingineria construcţiilor este atractivă. Dacă dorim să construim
o cuşcă pentru un câine, putem să mergem prin grădină, să căutam câteva lemne
şi câteva cuie, să ne ı̂narmăm cu un ciocan şi să ı̂ncepem să lucrăm. Avem şanse
destul de bune să reuşim, mai ales dacă suntem ı̂ndemânatici. Dacă totuşi nu

43
reuşim, putem ı̂ncerca a doua zi din nou cu alte lemne şi alte cuie. Iar dacă
câinele refuză să ı̂şi ocupe locuinţa, putem să ı̂l schimbăm cu un altul.
Lucrurile stau radical diferit atunci când dorim să construim o casă pentru
familia noastră. Atunci va trebui sau să angajăm un arhitect care să ne facă un
proiect, sau să cumpărăm un proiect standard de casă. Va trebui să negociem
cu o firmă de construcţii preţul, durata de realizare, calitatea finisajelor. Nu ne
permitem să riscăm economiile familiei pe o construcţie care se va dărâma la
a doua rafală de vânt. În plus, dacă membrilor familiei nu le place orientarea
ferestrelor şi obiectează vehement la faptul că terenul de sport este de baseball
ı̂n loc să fie de tenis, ne va fi greu să ı̂i ı̂nlocuim cu alţii1 .
Cu atât mai mult, cei care ne ı̂ncredinţează câteva milioane de dolari pentru
a ridica un zgârie nori vor fi foarte atenţi cu cine şi ı̂n ce condiţii vor lucra. Ei vor
dori garanţii precum că proiectul este viabil, vor angaja mai multe firme de arhi-
tectură pentru a-l verifica. Deasemenea, studii geologice, de fizică a pamântului,
de meteorologie vor fi obligatorii. Vor fi folosite cele mai performante materiale
şi se vor angaja cei mai competenţi şi cu experienţă constructori. Eşecul nu mai
este o opţiune pentru contractantul proiectului.
Ştim că inginerii constructori ı̂ntocmesc planuri, construiesc machete, stu-
diază proprietăţilor materialelor folosite şi fac rapoarte privind progresul operaţiunilor.
Construcţii de o complexitate foarte mare au fost realizate ı̂n acest fel ı̂ntr-un
mod raţional şi economic. Inginerii de programe ar trebui să procedeze similar
pentru ca dezvoltarea programelor să nu mai fie un proces impredictibil.
Prima definitie dată ingineriei programării a fost enunţată ı̂n anul 1968 astfel:
Ingineria programării este stabilirea şi utilizarea de principii inginereşti so-
lide pentru a obţine ı̂n mod economic programe care sunt sigure şi funcţionează
eficient pe maşini de calcul concrete.
În IEEE Standard Glossary of Software Engineering Tehnology(1983) ingi-
neria programării este definită după cum urmează:
Ingineria programării reprezintă abordarea sistematică a dezvoltării, funcţionării,
ı̂ntreţinerii, şi retragerii din funcţiune a programelor
Remarcăm că a doua definiţie este mai vagă decât prima, ı̂ntrucât nu face
referire la cost şi la eficienţă. Mai mult, se pare că experienţa ı̂n general nega-
tivă acumulată a făcut să se renunţe la formularea “principii inginereşti solide”,
ı̂ntrucât se pare că acestea nu pot fi identificate fără a fi supuse contestaţiilor.
A doua definiţie adaugă ı̂nsă referiri la perioade importante din viaţa unui pro-
gram, ce urmează creării şi funcţionării, anume ı̂ntreţinerea şi retragerea din
functionare.
Considerăm că ingineria programării are următoarele caracteristici impor-
tante:
• Este aplicabilă ı̂n producerea de programe mari
• Este o ştiinţă inginerească
• Scopul final este ı̂ndeplinirea cerinţelor clientului
Programele mici se pot scrie relativ usor, de către un singur programator,
ı̂ntr-o perioadă relativ scurtă de timp. Un program de 100 de instrucţiuni este
cu siguranţă un program mic. Nu putem da o graniţă ı̂ntre un program mic şi
unul mare, ı̂nsă pe măsură ce dimensiunea programului creşte, apar provocări
1 S-ar putea chiar ca ei să fie aceia care să iniţieze procesul de ı̂nlocuire.

44
noi, calitativ diferite. Întrucât un singur sau câţiva programatori nu pot avea
timpul fizic pentru terminarea programului, este necesară crearea a uneia sau
mai multor echipe de lucru. Este necesară coordonarea şi comunicarea ı̂ntre
echipe. Complexitatea sistemului software şi a organizaţiei care realizează siste-
mul software devine netrivială, putând depăşi capacitatea de ı̂ntelegere a unui
singur individ. Apare ca dezirabilă o abordare riguroasă a acestor probleme, ce
include stilul de lucru, modul de scriere a codului, etc. Ingineria programării
propune crearea unor astfel de abordare.
Ingineria programării are ca scop obţinerea de sisteme funcţionale chiar şi
atunci când teoriile şi instrumentele disponibile nu oferă răspuns la toate pro-
vocările ce apar. Inginerii fac lucrurile să meargă, ţinând seama de restricţiile
organizaţiei ı̂n care lucrează şi de constrângerile financiare.
Problema fundamentală a ingineriei programării este ı̂ndeplinirea cerinţelor
clientului [7]. Aceasta trebuie realizată nu punctual, nu ı̂n acest moment, ci
ı̂ntr-un mod flexibil şi pe termen lung. Ingineria programării se ocupă cu toate
etapele dezvoltării programelor, de la extragerea cerinţelor de la client până la
ı̂ntretinerea ş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 [1]. Aceasta pare să justifice orice disciplină a
muncii care să ı̂mbunătăţească aceste cifre.

• 45% livrate dar nefolosite

• 25% plătite dar nelivrate

• 20% abandonate sau refăcute

• 5% folosite după modificare

• 5% folosite aşa cum au fost livrate

Nerespectarea cerinţelor poate avea efecte serioase. Un sistem de livrare a in-


sulinei pentru diabetici poate provoca moartea pacientului dacă nu funcţionează
corect. Funcţionarea incorectă a unui sistem de control al unui satelit poate pro-
voca 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. Iată câteva
erori celebre.

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

• Sistemul de operare IBM OS360 conţinea aproximativ 1.000 de greşeli la


fiecare nouă versiune care ı̂ncerca să rezolve greşelile din versiunea prece-
dentă;

45
• Un vehicul de explorare a planetei Venus a fost pierdut deoarece programul
primit de pe Pamânt pentru rectificarea orbitei conţinea linia ’DO 3 I =
1.3’; instrucţiunea corectă ı̂n limbajul FORTRAN ar fi trebuit să conţină
“,” (virgulă) ı̂n loc de “.” (punct);

• În 1979 s-a descoperit o eroare ı̂n programele pentru sistemele de răcire ı̂n
centralele nucleare (SUA); nu fusese niciodată nevoie de executia rutinelor
ce conţineau erorile;

• Eroare ı̂n sistemul de avertizare ı̂mpotriva atacului cu rachete balistice;


procedurile de contraatac au fost declanşate ı̂nainte de a se descoperi că
a fost o eroare;
• Racheta Ariane 5 explodează ı̂n iunie 1996 din cauza unei greşeli de pro-
gramare; costurile s-au ridicat la 500 milioane dolari.

On two occasions I have been asked [by members of Parliament!], “Pray, Mr.
Babbage, if you put into the machine wrong figures, will the right answers
come out?” I am not able rightly to apprehend the kind of confusion of
ideas that could provoke such a question.
— CHARLES BABBAGE

46
Capitolul 2

Modele de dezvoltare a
programelor

Când pornim la dezvoltarea unui program avem nevoie de:


• o ı̂nţelegere clară a ceea ce se cere;
• un set de metode şi intrumente de lucru;
• un plan de acţiune.
Planul de acţiune se numeste model de dezvoltare. Dezvoltarea unui anumit
program constă ı̂ntr-un set de paşi ce se fac pentru a-l realiza. Luând ı̂n con-
siderare tipul paşilor ce se efectuează se crează 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)

2.1 Etapele dezvoltării programelor


În timpul dezvoltării programelor s-a constatat că există anumite tipuri de ac-
tivităţi care trebuie făcute la un moment dat:

• analiza cerinţelor

47
• proiectarea arhitecturală
• proiectarea detaliată
• scrierea codului
• integrarea componentelor
• validare
• verificare
• ı̂ntreţinere

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

2.1.2 Proiectarea arhitecturală


Din motive de complexitate, programele mari nu pot fi concepute şi implemen-
tate 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.

2.1.3 Proiectarea detaliată


Se realizează proiectarea fiecărui modul al aplicaţiei, ı̂n cele mai mici detalii.

2.1.4 Scrierea codului


Proiectul detaliat este transpus ı̂ntr-un limbaj de programare. În mod tipic,
aceasta se realizează modular, pe structura rezultată la proiectarea arhitectu-
rală.

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

48
S-a constatat că atunci când se aplică acest model, timpul de testare ex-
plodează, 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.

2.1.6 Validare
În procesul de validare ne asigurăm că programul ı̂ndeplineşte cerinţele utiliza-
torului. Întrebarea la care răspundem este: construim produsul corect? 1 .
Un exemplu de validare este testul de acceptare, ı̂n care produsul este pre-
zentat clientului. Clientul spune dacă este mulţumit cu produsul sau dacă mai
trebuie efectuate modificări.

2.1.7 Verificare
În procesul de verificare ne asigurăm că programul este stabil şi că functionează
corect din punctul de vedere al dezvoltatorilor. Întrebarea la care raspundem
este: construim corect produsul? 2 .

2.1.8 Întreţinere
După ce programul este livrat clientului, mai devreme sau mai târziu sunt des-
coperite defecte sau erori ce trebuie reparate. De asemenea, pot apare schimbări
ı̂n specificaţiile utilizatorilor, care vor diverse imbunătăţiri. Întreţinerea constă
ı̂n gestionarea acestor probleme.

2.2 Modelul cascadă


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

• Testul de acceptare
1 Are we building the right product? (eng.)
2 Are we building the product right? (eng.)

49
Ingineria
cerintelor

P roiectarea
arhitecturala

P roiectarea
detaliata

Implementare

T estarea
unitatilor

T estarea
sistemului

Acceptare

Figura 2.1: Modelul de dezvoltare ı̂n cascadă

Nu se stipulează cum se fac aceşti paşi (metodologie, notaţii), ci doar ordinea


efectuării lor.
Avantaje
O sarcină complexă este ı̂mparţită ı̂n mai multi 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.)

2.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 re-
media o problema gasită. 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 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ă clientul obţine
o viziune practică asupra produsului doar ı̂n momentul terminării procesului de
dezvoltare.

50
Ingineria
cerintelor

P roiectarea
arhitecturala

P roiectarea
detaliata

Implementare

T estarea
unitatilor

T estarea
sistemului

Acceptare

Figura 2.2: Modelul de dezvoltare ı̂n cascadă cu ı̂ntoarcere

51
2.4 Prototipizarea
O problemă generală care apare la dezvoltarea unui program este să ne asi-
gurăm că utilizatorul obţine exact ceaa 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 re-scris 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 aplicatiei care să poată implementa ı̂n primă fază o parte a cerinţelor siste-
mului. Pe masură ce aplicaţia este dezvoltată, noi caracteristici sunt adaugate
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.
• ı̂ntretinerea este redusă, deoarece validarea se face pe parcursul dezvoltării
• se poate facilita instruirea utilizatorilor finali ı̂nainte de terminarea pro-
dusului.
Dezavantaje:
• deoarece prototipul rulează ı̂ntr-un mediu artificial, anumite dezavantaje
ale produsului final pot fi scăpate din vedere de clienţi;
• clientul nu ı̂ntelege de ce produsul necesită timp suplimentar pentru dezvol-
tare, 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 munca.

52
2.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 execu-
tabil;
• 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.

2.6 Modelul ı̂n spirală


Modelul ı̂n spirală ı̂ncearcă să rezolve problemele modelului ı̂n cascadă, pastrând
avantajele acestuia: planificare, faze bine definite, produse intermediare. Dea-
semenea, prototipizarea poate fi folosită 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 progra-
melor este riscul. Riscul nu mai este eliminat prin asertiuni de genul: “in 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 clientu-
lui sunt ignorate;
• o firma concurentă lansează un program rival pe piaţă;
• un dezvoltator/arhitect păraseşte echipa de dezvoltare;

53
1 : pregatirea 2 : gestiunea riscului
[take stock] [dealing with risk]

4 : planif icarea
3 : dezvoltarea
urmatorului stagiu
[development]
[planning]

Figura 2.3: Modelul de dezvoltare ı̂n spirală

• o echipa nu respectă un termen de livrare pentru o anumită componentă

• clientul schimbă cerinţele.

În modelul spirală (2.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, revizu-


irea stării proiectului.

2.7 Rational Unified Process


3
Rational Unified Process (Procesul unificat [de dezvoltare al firmei] Rational)
defineşte următoarele activităţi fundamentale:

• Ingineria functionalităţii. Sunt sintetizate necesităţile funcţionale.

• Cerinţe. Se translatează necesităţile funcţionale ı̂n comportament de sis-


teme automate.

• Analiza şi Proiectare. Se translatează cerinţele ı̂n arhitectura programului.

• Implementare. Se crează programul conform cu arhitectura astfel ı̂ncat


comportamentul acestuia sa fie consistent cu cel dorit.

• Testare. Se asigură că comportamentele cerute sunt corecte şi că toate
comportamentele necesare sunt prezente ı̂n program.
3 Denumirea ı̂n engleză este, probabil din motive de promovare pe piaţă, ambiguă, putând

ı̂nsemna şi (Procesul unificat [şi] raţional [de dezvoltare]).

54
• Administrarea configuratiei şi a schimbarilor. 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 ne-
cesar dezvoltării programului.
• Plasament. Se efectuează activităţile necesare punerii ı̂n funcţiune a pro-
gramului.

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 2.4 cantitatea de cod scrisă la
ı̂nceputul proiectului este mică, ı̂nsă nu zero. Pe masură 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 masură 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 clien-
tului, 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 lu-
crează, 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ţii care durează o saptamână
sau două sunt considerate acceptabile ca ı̂ntindere ı̂n timp.
Iteraţiile pot fi folosite pentru a estima timpul necesar dezvoltării proiec-
tului. Dacă, de exemplu, mai sunt proiectate ı̂ncă 20 de iteraţii, iar până ı̂n
momentul la care se face estimarea timpul de dezvoltare a fost de 2 saptămâni
pe iteraţie, putem să apreciem că proiectul va mai dura aproximativ 40 de
saptămâni. Aceast tip de informaţie poate fi de mare folos administratorilor şi
clienţilor. Iniţial estimările sunt nesigure, ı̂nsă pe masura ce proiectul avansează,
ele devin din ce ı̂n ce mai precise.

2.7.1 Pornire
În faza de pornire, scopul principal al iteraţiilor este să ajute echipa de dezvoltare
să stabileasca care vor fi 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:
• o enumerare a cerinţelor principale, posibil ı̂n formă de cazuri de utilizare;
• o imagine de ansamblu asupra arhitecturii programului;
• o descriere a obiectivelor proiectului;
• un plan preliminar de dezvoltare.

55
Figura 2.4: Activităţile ı̂n cadrul RUP

56
2.7.2 Rafinare
În faza de rafinare se stabileşte o ı̂ntelegere 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.

2.7.3 Construire
Iteraţiile din faza de contruire au ca rol adăugarea se diverse caracteristici pro-
gramului dezvoltat. În această fază se aşteaptă ca cazurile de utilizare oferite
de client să se stabilizeze ı̂n mare masură, deşi ı̂n unele aplicaţii este posibil ca
ele să sufere oarecari modificări.
Cazurile de utilizare sunt adaugate 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

2.7.4 Tranzitie
În cadrul acestei activităti programul este ı̂mbogaţit mai departe cu caracteris-
tici, ı̂nsă accentul se pune pe ı̂mbunătăţirea şi rafinarea celor existente.
Sfârşitul acestei activitati ş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.

2.8 Extreme Programming


Extreme Programing (XP) [3] 4 este o metodologie nouă de dezvoltare, inspi-
rată 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.
4 Programare extremă

57
XP consideră că dezvoltarea programelor nu ı̂nseamna ierahii, responsabi-
lităţ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
ı̂ncurajati să ı̂şi afirme personalitatea, să ofere şi să primească cunoaştere şi să
devină programatori străluciti.
Deasemenea, 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
sedinţ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 adevarat că un
inginer care vrea să construiască un pod peste un râu face intâi măsuratori,
realizează un proiect şi abia apoi trece la executie, toate acestea ı̂ntr-un mod
secvential ş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 frumusete chiar când a terminat de
construit jumătate de pod, putem fi siguri că acel inginer şi-ar schimba modul
de lucru. Din pacate ı̂nsa, nu stim (ı̂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 asig-
nate.

• Ai dreptul să produci treabă de calitate ı̂n orice moment.

• Ai dreptul la liniste, distracţie şi la muncă productivă şi placută.

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 dovedeste
că funcţionează trecând teste repetabile pe care tu le specifici.

• Ai dreptul să te razgândeşti, să ı̂nlocuieşti functionalităţi şi să schimbi


priorităţile.

• Ai dreptul să fii informat de schimbarile ı̂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 opresti la un moment dat şi să rămâi cu un
sistem folositor care să reflecte investiţia până la acea dată.

58
Aceste afirmaţii, deşi par ı̂ntelese de la sine, conţin semnificaţii profunde.
Multe din problemele apărute ı̂n dezvoltarea programelor pornesc de la ı̂ncalcarea
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 cla-


rificarea cerinţelor.

• Codul se scrie cât mai simplu.

• Se scrie cod de test intâi.

• Dacă apare necesitatea rescrierii sau aruncării de cod, aceasta se face fără
milă.

• Modificarile aduse codului sunt integrate continuu (de cateva ori pe zi).

• Se programează ı̂n echipă (programare ı̂n perechi). Echipele se schimbă la


sfârşitul unei iteraţii (1-2 saptămâni).

• Se lucrează 40 de ore pe saptămână, fără lucru suplimentar.

Nu vom discuta aici avantajele aduse de această abordare şi nici criticile care
ı̂i sunt adresate. Le lăsăm ca subiecte de meditaţie pentru cititor.

It took 300 years to build and by the time it was 10% built,
everyone knew it would be a total disaster. But by then the investment
was so big they felt compelled to go on. Since its completion, it has
cost a fortune to maintain and is still in danger of collapsing.
There are at present no plans to replace it, since it was never
really needed in the first place.
— K.E. IVERSON, despre turnul din Pisa

59
60
Capitolul 3

Concepte orientare obiect

3.1 Obiecte. Clase


Un obiect este o entitate care are identitate, stare şi comportament.
Exemple:
• Apartamentul situat la adresa Str. Locuinţei nr. 4, Et. 4, Ap. 4, ı̂n stare
impecabilă, care ţine iarna de cald şi vara de frig.
• Zona de memorie de la adresa 0x00FFA0BC de dimensiune 4 octeti, fiecare
dintre aceştia având biţii setati pe 0; este accesibila doar pentru citire şi
poate fi interpretată ca un ı̂ntreg little-endian.
• Motanul Griuţu’, pipernicit şi slab, care este de fapt pisică, manancă, se
joacă şi doarme.
• Mingea mea galbena de tenis, cu diametrul 10 cm, care sare.
O clasă este o descriere a unei mulţimi de obiecte care au aceleasi caracte-
ristici structurale şi comportmanetale.
Exemple:
• Apartamente care au adresă, cu o stare precizată, care se ţin iarna de cald
şi vara de frig.
• Zone de memorie adresate pe 32 biti, de dimensiune 4 octeti, accesibile
doar pentru citire şi a căror biţi sunt interpretaţi ca ı̂ntregi little-endian.
• Pisici care au nume, ı̂naltime, greutate, sex, care manâncă, se joacă, şi
dorm.
• Mingi, care sunt sferice, au diametru, culoare, ı̂ntrebuintare specifică, şi
care sar.
Putem privi o clasă, ı̂n acelasi timp, ca fiind:
• o descriere generală a unui număr de obiecte
• o colecţie de metode (operaţii) ce pot fi efectuate de instanţele clasei
• un mecanism pentru stabilirea şi reutilizarea conceptelor

61
• o abstractizare a unui obiect

• un tip de date

Spunem că un obiect care aparţine unei clase este instanţă a acelei clase.

3.2 Principiile dezvoltării orientată obiect


Există anumite principii, asupră cărora ı̂n general se convine, referitoare la ce
ı̂nseamna “orientat obiect”.
Se consideră că dezvoltarea unui program este orientată obiect dacă apar
urmatoarele caracteristici:

• ı̂ncapsularea

• moştenirea

• polimorfismul

În principiu, orice limbaj de programare orientat obiect ar trebui să ofere
suport pentru construcţiile de mai sus. Astfel, ar fi posibil să transpunem o
arhitectură orientată obiect ı̂n orice limbaj orientat obiect disponibil.
Toate aceste trei caracteristici promovează reutilizarea codului. Încapsularea
permite ca o clasă să fie folosită fără a şti cum functionează (modularitate, abs-
tractizare). Moştenirea permite reutilizarea codului prin folosirea de caracteris-
tici deja existente şi adaugarea de altele noi. Polimorfismul permite scrierea unei
clase generale care specifică o interfaţă a carei implementare va fi specificată şi
apelată ı̂ntr-un mod transparent pentru clasele care o moştenesc.

3.2.1 Încapsulare
Încapsulare ı̂nseamna punerea ı̂mpreuna a părţilor puternic corelate ale unui
program ı̂n scopul creării de unităţi ce pot fi proiectate, implementate, depanate,
testate şi menţinute una căte una.
Limbajele orientate obiect consideră codul şi datele ca parte a unei entităţi
indivizibile numite obiect. Datele conţinute ı̂ntr-un obiect sunt de regulă mo-
dificate doar prin intermediul codului (metodelor) obiectului respectiv. Fiecare
obiect poate fi privit ca un mic procesor a cărui comportare este definită de
modul cum răspunde la un apel de metodă.
Într-un sistem orientat obiect se lucrează ı̂n mod uzual cu o colectie de
obiecte ce cooperează pentru a obţine un anumit rezultat.
Ascunderea informaţiei: utilizatorii unui obiect nu au nevoie să stie (nu-i
interesează, nu trebuie să stie) implementarea metodelor. Un utilizator poate
modifica starea obiectului doar ı̂n mod indirect, prin apelul de metode. El nu
trebuie să ştie modalitatea ı̂n care modifică metoda starea obiectului ci doar
efectul acestei modificari. Un avantaj al acestei abordări este că ı̂mbunătăţiri
ale codului pot apare fără a schimba interfaţa. Această separare este esenţială
pentru producerea de cod refolosibil şi usor de intreţinut.

62
3.2.2 Mostenirea
Adesea anumite clase sunt percepute ca specilizări ale altor clase.
De exemplu:

• metalele pretioase sunt metale

• masinile de curse sunt masini

• romanele sunt carti

Spunem ca o clasă de obiecte este sub-clasă altei clase de obiecte. Sau, o


clasă este super-clasă a unei altei clase.
O subclasă are caracteristicile super-clasei pe care le extinde ı̂ntr-un anume
fel, de exemplu:
• Metalele pretioase au faţă de celelalte metale valoare monetară

• O zebră este un cal cu dungi

Mai spunem că sub-clasa sau clasa derivată moşteneşte caracteristicile super-
clasei.
Avantaje:

• Definiţii concise ale claselor: pentru subclase este suficient să descriem
cum diferă faţă de super-clase.

• Reutilizarea codului: ı̂n loc să duplice codul, clasele derivate reutilizează
codul existent ı̂n superclasă.

3.2.3 Polimorfismul
Polimorfism: interpretarea semanticii unui apel de metodă se face de catre cel
care primeste apelul, nu de catre apelant (acelasi apel de metoda se poate com-
porta diferit pentru tipuri de obiecte diferite).
Extinderea programelor cu tipuri noi de date se simplifică foarte mult.
Informaţii suplimentare: [6].

CLASS, from Latin “classis”, summons, division of citizens for military


draft, hence army, fleet, also class in general.
— THE AMERICAN HERITAGE DICTIONARY

63
64
Capitolul 4

Modelare

4.1 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 pro-
prietăţile acestuia. Lucrul cu modelele poate facilita o mai bună ı̂nţelegere a
sistemului modelat, datorită dificultăţii intrinseci de ı̂ntelegere a sistemului ı̂n
ı̂ntregul sau. Folosirea tehnicii “divide et impera” permite ı̂nţelegerea părţilor
componente ale unui sistem, iar ansambul sistemului ca o interacţiune ı̂ntre
părţile acestuia. Un model reuşit reţine caracteristicile importante ale obiectului
modelat şi le ignoră pe cele irelevante. De remarcat că noţiunile de importan-
t/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 comu-
nicării ı̂ntre membrii echipei. Este posibil să apară situaţii ı̂n care modelul
construit de proiectant să nu fie ı̂nteles exact de cel ce scrie cod, fie din cauza
lipsei de precizie a modului ı̂n care este prezentat modelul, fie datorită incom-
pletitudinii acestuia; adesea o serie de amănunte subtile ce sunt evidente pentru
designer nu sunt transmise. O rezolvare a problemei comunicarii 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ă continuie 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ă, deter-
minat viziunea sa asupra problemei şi de pregatirea acestuia (un matematician
va utiliza o notatie algebrică, un arhitect o notatie preponderent grafică etc.)
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:
Cuvintele scrise sau limbajul rostit, nu par să joace nici un rol ı̂n meca-
nismele gândirii mele. Entităţile fizice ce par să servească drept elemente de
gândire sunt anumite semne şi imagini mai mult sau mai puţin clare ce pot fi
reproduse şi combinate “voluntar”. (Albert Einstein)

65
Aceasta nu ı̂nseamnă că a comunica altora ideile este facil:
Trebuie să traduc gândurile ı̂ntr-un limbaj ce nu curge la fel de lin. Aşa
că pierd o groază de timp cautănd cuvintele şi frazele corespunzătoare, şi sunt
conştient că atunci cănd mi se cere brusc să vorbesc, de multe ori sunt foarte
neclar datorită faptului că mă exprim pur şi simplu stăngaci şi aceasta nu din
cauza lipsei de claritate a percepţiei. (Francis Galton)
Aşa cum formalismul raţionamentului matematic poate fi agentul de trans-
misie al adevarului matematic [5] (care, odată transmis, este “tradus” ı̂n lim-
bajul intern al receptorului), formalismul limbajului de modelare constă din
stabilirea de elemente cu o semantică asupra căreia se convine şi cu ajutorul
cărora să se poată trasmite idei ı̂n mod cât mai eficient şi neambiguu.
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 pri-
mul rând pentru a facilita proiectarea programelor, ı̂nsa datorită expresivitâţii
sale poate fi folosit şi ı̂n alte domenii (proiectare hardware, modelarea proceselor
de afaceri etc.) [4]
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. El nu este un ı̂nlocuitor al inteligentei, experienţei şi bunului gust
[8].

4.2 Modele. Limbaje de modelare


Deseori, atunci când ne gândim la un obiect ı̂ntr-un context oarecare, ingorăm
caracteristicile care nu interesează. De exemplu, la un microprocesor nu ne
interesează culoarea sau greutatea. Din punct de vedere practic, este proba-
bil ineficient să considerăm aspectul greutaţii microprocesorului de fiecare dată
când utilizam un calculator şi de aceea un mecanism de eliminare a caracteris-
ticilor irelevante functionează probabil ı̂n mintea fiecăruia.
Există ı̂nsa obiecte mult mai complexe decât un microprocesor. O rachetă
cosmică este probabil un artefact ce depăşeşte capacitatea de ı̂ntelegere a unui
singur individ. Motivul pentru care există totuşi microprocesoare ŝi navete
cosmice este simplu: se folosesc modele. Modelul este o simplificare a realitâţii.
Această simplificare reţine caracteristicile relevante ale unui sistem, ı̂n timp ce le
ignoră pe celelalte. Termenul de relevant este bineı̂nţeles relativ, diferite aspecte
fiind relevante ı̂n funcţie de problema la care se foloseste modelul respectiv.
Să considerăm că dorim să analizăm modul de comportare al unui corp sub
acţiunea unei forte externe. Pe baza experienţei ı̂n domeniu, ştim că singu-
rele atribute care influenţează analiza ı̂n acest caz sunt masa corpului şi forţa
netă care acţionează asupra corpului. Un model ı̂ncetăţenit printre fizicieni de
reprezentare a acestei probleme este prezentat ı̂n figura 4.1:
Acest model face câteva simplificari evidente. Forma corpului este desenată
ca un pătrat. Diverse alte caracteristici sau interecţiuni cu mediul sunt ignorate.
Pe de altă parte, viteza şi forţa sunt reprezentate prin vectori. Viteza este notată
cu v, forţa cu F, masa cu m. Toate aceste elemente formează un model. Pentru
un iniţiat, desenul de mai sus este consistent, expresiv şi compact. O descriere
alternativă, sub forma de text, deşi posibil mai exacta, ar fi mai greoaie.
Fie un corp de masă m asupra căruia acţionează o forţa F cu punctul de

66
v
m

Figura 4.1: Modelarea ı̂n fizica

aplicaţie ı̂n centrul său de greutate. Corpul se deplasează cu o viteză v perpen-


diculară pe direcţia forţei.
Limbajul natural pare să fie cel mai la ı̂ndemână limbaj de modelare. Experienţa
arată ı̂nsă că folosirea sa induce adesea neclaritaţi şi inconsistenţe. Apare ast-
fel necesitatea definirii unui limbaj neambiguu de specificat modele. Se convine
asupra unui set de elemente ale limbajului precum şi asupra semanticii acestora.
Evident, descrierea elementelor şi a semanticii se face ı̂n ultimă instanţă ı̂n lim-
baj natural, deci pot apare ambiguităţi. În acest caz ı̂nsă, limbajul natural este
folosit numai ı̂ntr-o mică parte a sistemului şi problemele de semantică pot fi
localizate. Eliminarea ambiguitatilor din modele poate fi facută ı̂mbunătăţind
precizia limbajului de modelare şi nu cautând erori de semantică ı̂n ı̂ntreaga
descriere a modelului.

Documentation is like sex: when it is good, it is very, very good; and


when it is bad, it is better than nothing.
— DICK BRANDON

67
68
Capitolul 5

UML

5.1 Scurt istoric


Începând cu mijlocul anilor 1970 şi continuând ı̂n anii 1980 au aparut diverse
limbaje de modelare, odată cu creşterea experientei de lucru ı̂n cadrul paradig-
mei orientate obiect. Astfel, numârul de limbaje de modelare ajunge de la 10
pâna la mai mult de 50 ı̂n perioada 1989-1994. Limbajele de modelare de suc-
cess din această perioadă sunt Booch (Grady Booch), OOSE - Object-Oriented
Software Engineering (Ivar Jacobson) şi OMT - Object Modeling Technique
(James Rumbaugh). Aceste limbaje aveau propriile punce tari şi slăbiciuni,
după cum creatorii lor puneau accentul pe anumite idei de modelare. Astfel,
utilizatorii găseau tehnicile de modelare ce le erau necesare unui proiect parti-
cular numai ı̂n anumite limbaje, fapt ce a alimentat ”râzboiul metodelor”. La
mijlocul anilor 1990, când este semnalată o nouă generaţie de limbaje de mode-
lare, a ı̂nceput un proces de omogenizare, prin ı̂ncorporarea ı̂n fiecare limbaj a
caracteristicilor gasite ı̂n celelalte limbaje.
Cei trei autori ai limbajelor de modelare principale, Booch, Rumbaugh şi
Jacobson, au ajuns la concluzia ca ar fi mai bine să conducă evoluţia limbajelor
lor pe un acelasi 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 ne-
cesitatea industriei software de a avea o oarecare stabilitate pe piata limbajelor
de modelare. Al treilea motiv a fost convingerea că prin unirea forţelor se pot
aduce ı̂mbunatatiri tehnicilor existente.
Dezvoltarea UML a ı̂nceput ı̂n mod oficial ı̂n octombrie 1994, când Rumba-
ugh s-a alaturat lui Booch ı̂n cadrul companiei Rational Software, ambii lucrând
la unificarea limbajelor Booch şi OMT. Versiunea preliminară 0.8 a Unified
(Metoda Unificată - asa 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 comunita̧t̆ii
proiectanţilor de sistem, a devenit clar ca UML este văzut de catre multe com-
panii 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

69
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 catre OMG ı̂n noiembrie 1997. Actual-
mente UML este dezvoltat de catre OMG Revision Task Force, condus de Cris
Kobryn. Versiunea 1.2 a UML a fost o revizie editoriala; versiunile 1.3 au fost
publicate ı̂ncepând cu sfârşitul anului 1998. În septembrie 2001 a fost publicată
versiunea 1.4. În momentul de faţă se lucrează la versiunea 2.0 a UML.

5.2 Ce este UML?


UML (Unified Modeling Language) este un limbaj de modelare bazat pe notaţii
grafice folosit pentru a specifica, vizualiza, construi şi documenta componentele
unui program [4],[2].
UML este un limbaj cu ajutorul caruia se pot construi (descrie) modele. Un
model surprinde un anumit aspect al unui program. Fiecărui model ı̂i cores-
punde o diagramă. Tipuri 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 (Statechart Diagram)
– Diagrama de activităţi (Activity Diagram)

• Diagrame de implementare:

– Diagrama de componente (Component Diagram)


– Diagrama de plasare (Deployment Diagram)

5.3 Organizarea modelelor-Pachete


Un pachet (package) reprezintă o grupare a elementelor unui model. Pachetele
ı̂nsele pot fi conţinute unul ı̂ntr-altul. Un pachet poate conţine atât alte pachete
subordonate, cât şi alte tipuri de elemente ale modelului. Toate tipurile de
elemente ale unui model UML pot fi organizate ı̂n pachete.
Pachetele reprezintă baza pentru controlul configuraţiei, depozitare şi con-
trolul accesului. Fiecare element poate fi conţinut de un singur pachet. Totuşi
pachetele pot face referiri la alte pachete, iar modelerea acestor situaţii se face
folosind unul dintre stereotipurile  import  şi  access  asociate unei
relaţii de dependenţă.
Notaţia grafică pentru pachet este prezentată ı̂n figura 5.1. Un exemplu de
relaţii care se pot stabili ı̂ntre pachete este dat ı̂n figura 5.2.

70
Figura 5.1: Notaţia grafică pentru pachet

Figura 5.2: Exemplu de relaţii care se pot stabili ı̂ntre pachete

71
Figura 5.3: Exemplu de notaţie grafică pentru clasă

5.4 Diagrama de clase


Este folosită pentru a modela structura (viziunea statica asupra) unui sistem.
O astfel de diagramă conţine de obicei clase, şi relaţii care se stabilesc ı̂ntre
acestea.
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ă vocabula-
rul sistemului. Fiecare dintre aceste elemente are o multime de proprietăţi.
Un exemplu de notaţie grafică pentru clasă este dat ı̂n figura 5.3.

5.4.1 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 - un atribut reprezintă numele unei proprietăţi a clasei.
Operaţii (metode) - o operatie reprezintă implementarea unui serviciu care
poate fi cerut oricărui obiect al clasei.

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 pen-
tru a descrie soluţia problemei. Pentru fiecare element se identifică o mulţime
de responsabilităţi (ce trebuie să facă acesta), dupa care se definesc atributele
şi operaţiile necesare ı̂ndeplinirii acestor responsabilităţi.

5.4.2 Relaţii
O relatie modelează o conexiune semantică sau o interacţiune ı̂ntre elementele
pe care le conectează. În modelarea orientată obiect cele mai importante relaţii

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

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

sunt relaţiile de asociere, dependenţa, generalizare şi realizare. Un caz particular


al relaţiei de asociere ı̂l constituie relaţia de agregare.
Figura 5.4 ilustrează notaţiile grafice pentru diferite tipuri de relaţii.

Relaţia de asociere
Asocierea este o relaţie importantă ce apare foarte des ı̂n diferite contexte de
modelare. Ea se foloseşte pentru a exprima o conexiune semantica ı̂ntre obiecte
individuale aparţinând unor anumite clase.
Asocierile poartă informaţii despre legăturile dintre obiectele unui sistem. Pe
masură ce sistemul evoluează, legăturile dintre obiecte pot fi create sau distruse.
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 ro-
luri, ş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 ale unei clase pot fi relaţionate cu o instanţă 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 5.5).
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

73
indicând direcţia asocierii (figura 5.4 (b)).

Relaţia de agregare
O 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 5.4 (c)).
Î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.

Relaţia de dependenţă
O dependenţă (figura 5.4 (d)) 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ţelor ce apar la implementare. Dependenţele pot să
apară şi la structurarea unui sistem ı̂n pachete, definind arhitectura sistemului.

Relaţia de generalizare
Relaţia de generalizare (figura 5.4 (e)) 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 superclasa, sau clasa 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.

Relaţia de realizare
Relaţia de realizare (figura 5.4 (f)) 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 5.6 prezintă un exemplu de diagramă de clase.

5.5 Diagrama de interacţiuni


Este folosită pentru a modela comportamentul unei mulţimi de obiecte dintr-un
anumit context care interactionează ı̂ntre ele pentru a ı̂ndeplini un anumit scop.
Contextul unei interacţiuni (unde pot găsi interacţiuni) poate fi:
• sistemul/un subsistem (uzual): mulţimea obiectelor din sistem care cola-
borează ı̂ntre ele

74
Figura 5.6: Exemplu de diagramă de clase.

75
• o operaţie: interacţiuni ı̂ntre parametri, variabile locale şi globale

• o clasă: interacţiuni ı̂ntre atributele unei clase (cum colaborează ele),


interacţiuni cu obiecte globale, sau cu parametrii unei operaţii.

Scopul unei diagrame de interacţiuni este acela de a specifica modul ı̂n care
se realizează o operaţie sau un caz de utilizare.
Obiectele care participă la o interacţiune pot fi lucruri concrete sau pro-
totipuri. De obicei, ı̂ntr-o colaborare obiectele reprezintă prototipuri ce joaca
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, na-
vigare, agregare), dar nu şi multiplicitate.
Obiectele care interactionează comunica ı̂ntre ele, comunicarea facându-se
prin schimb de mesaje. Un mesaj specifică o comunicare ı̂ntre obiecte. El
poartă o informatie şi este urmat de o activitate.
Primirea unei instante a unui mesaj poate fi considerată o instanţă a unui
eveniment. Unui mesaj ı̂i este asociată o acţiune care poate avea ca efect schim-
barea stării actuale a obiectului.
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 multime de obiecte şi relaţiile


dintre ele (inclusiv mesajele care se transmit). Există două tipuri de diagrame
de interacţiuni: diagrama de secvenţă şi diagrama de colaborare. Ele specifică
aceeaşi informaţie, ı̂nsă pun accentul pe aspecte diferite.

5.5.1 Diagrama de secvenţă


Diagrama de secvenţă pune accentul pe aspectul temporal (ordonarea ı̂n timp
a mesajelor).
Notaţia grafică este un tabel (figura 5.7) 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ă) şi 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.

76
Figura 5.7: Exemplu de diagramă de secvenţă care ilustrează ce se ı̂ntâmplă
atunci când un student propune un proiect

77
5.5.2 Diagrama de colaborare
Diagrama de colaborare: pune accentul pe organizarea structurală a obiectelor
care participă la interacţiune. Ea conţine obiecte, legături care se stabilesc
ı̂ntre acestea, precum şi mesajele prin care obiectele comunică. Mesajele sunt
prefixate de un număr care indică ordonarea ı̂n timp a acestora şi sunt ataşate
legăturilor dintre obiecte. Unei legături ı̂i pot fi ataşate mai multe mesaje.
Notaţia grafică: este un graf ı̂n care vârfurile reprezintă obiecte, iar arcele
reprezintă legaturile dintre ele.

Die Mathematiker sind eine Art Franzosen:


redet man zu ihnen, so übersetzen sie es in ihre Sprache,
und dann ist es alsobald ganz etwas Anders.
— GOETHE, Maxime şi cugetări (1829)

78
Capitolul 6

Modele de proiectare

Modelele de proiectare sunt solutii la probleme de proiectare care apar des in


practica. Aceste solutii s-au dovedit a fi elegante, generale, reutilizabile, adica
au calitatile pe care un dezvoltator le apreciaza la un program bun. In acest
sens, modelele de proiectare sunt un model de urmat, in sensul ca merita a fi
admirate si imitate.
Desigur, modelele de proiectare nu trebuie folosite doar pentru ca exista.
Este esential ca acestea sa fie aplicate corespunzator, aducand astfel un plus de
valoare programului. Un arhitect incepator va trebui insa in prima faza sa le
studieze in forma lor de baza, pentru a se putea familiariza cu ideile continute in
acestea si pentru a isi dezvolta limbajul. Pe masura ce avanseaza in experienta,
va putea folosi aceste modele de dezvoltare ca termeni cheie in explicarea unei
arhitecturi.
Invatarea mecanica a modelelor de proiectare este inutila. Ceea ce poate
ajuta insa foarte mult este incercarea de a intelege de ce anume un anumit
model de proiectare a fost conceput, care este intentia din spatele sau. Doar
astfel poate fi apoi ulterior identificata necesitatea aplicarii (in mod evident,
corecte) a unui model de proiectare intr-un program.
Am aplicat modelele de proiectare prezentate in acest capitol in diverse
proiecte reale. De fiecare data, am constatat cu incantare ca rezultatul a fost
un cod mai mic, mai clar si mai flexibil. Va incurajam, asadar, sa cititi mai
departe, considerand ca aceste notiuni va vor ajuta sa faceti primii pasi inspre
proiectarea profesionala a programelor pe care le veti dezvolta.
Descrierile prezentate in continuare sunt doar pentru referinta. Detalii pri-
vind modul de implementare a modelelor de proiectare, precum si folosirea prac-
tica a acestora, vor fi prezentate la laborator.

6.1 GRASP
Acronimul GRASP vine de la General Responsibility Assignement Software Pat-
terns (modele generale de asignare a responsabilitatilor in software). Descrise de
Craig Larman ı̂n cartea Applying UML and Patterns. An Introduction to Object
Oriented Analysis and Design, scopul lor este sa ne ajute să alocăm responsabi-
lităţi claselor ı̂n cel mai elegant mod posibil. Modelele sunt numite: Information
Expert (sau Expert), Creator, High Cohesion, Low Couplig şi Controller. In cele

79
ce urmeaza le vom descrie pornind de la problema pe care fiecare isi propune sa
o rezolve.

6.1.1 Information Expert


Problemă: dat un anumit comportament (operaţie), cărei clase trebuie să-i fie
atribuit?
Răspunsul la aceasta ı̂ntrebare este foarte important, deoarece o alocare
bună a operaţiilor conduce la sisteme care sunt:
• uşor de ı̂nţeles
• mai uşor de extins
• refolosibile
• mai robuste
Soluţia pe care o propune Information Expert este sa fie asignată o respon-
sabilitate clasei care are informaţiile necesare pentru ı̂ndeplinirea acelei respon-
sabilităţi. Pentru ı̂nceput este necesară o evidenţiere clară a responsabilităţilor,
urmând ca abia apoi acestea să fie ı̂mpărţite claselor.
Sa consideram exemplul unei aplicatii care implementeaza un POS (Point of
Sale) intr-un supermarket. Mai jos este dat un fragment din diagrama de clase
care modeleaza arhitectura aplicatiei.

Sale
-date
-time

1
Contains

1..*
ProductSpecification
SalesLineItem * Described by 1
-description
-quantity
-price
-itemId

Scenariul de utilizare cere ca utilizatorului sa-i fie prezentată valoarea totală


a cumpărăturilor. Mai intai trebuie sa vedem ce informatii sunt necesare pentru
a determina valoarea totala. Pentru inceput avem nevoie sa stim care sunt
toate instantele clasei SalesLineItem care compun o instanta a clasei Sale si sa
calculam suma subtotalurilor acestora. Cum o instanta a clasei Sale stie care
sunt instantele clasei SalesLineItem pe care le contine, clasa Sale este un bun
candidat pentru a-i fi asignata responsabilitatea calcularii valorii totale deoarece
detine toate informatiile de care are nevoie, fiind astfel un expert in ceea ce
priveste executia acestei sarcini.
Incepem sa asignam responsabilitatile desenand diagrame de interactiuni
(colaborare). Figura urmatoare ilustreaza decizia noastra pana in acest moment:
t := getTotal()
:Sale

80
Continuam procesul de analiza. Ce informatii sunt necesare pentru a de-
termina subtotalul pentru o instanta a clasei SalesLineItem? Avem nevoie sa
cunoastem cantitatea de produs care este vanduta, precum si pretul produsu-
lui. SalesLineItem cunoaste cantitatea si produsul asociat (instanta a clasei
ProductSpecification, deci SalesLineItem poate sa determine subtotalul.
In termeni de diagrama de interactiuni, acest lucru inseamna ca Sale tre-
buie sa trimita mesaje de tipul getSubtotal fiecarei instante SalesLineItem pe
care o contine si sa faca suma rezultatelor. Acest design este ilustrat in figura
urmatoare:

t := getTotal() 1*: st := getSubtotal()


:Sale :SalesLineItem
*

Pentru a-si indeplini responsabilitatea de a raspunde la intrebarea ”care este


subtotalul?”, o instanta SalesLineItem trebuie sa cunoasca pretul produsului.
ProductSpecification detine aceasta informatie, deci trebuie trimis un mesaj
prin care sa-i fie cerut pretul. Figura urmatoare prezinta solutia:

t := getTotal() 1*: st := getSubtotal()


:Sale :SalesLineItem
*

1.1: p:=getPrice()

:ProductSpecification

In concluzie, pentru a indeplini responsabilitatea de a calcula valoarea totala


a unei vanzari, au fost adaugate trei responsabilitati claselor din diagrama de
clase initiala. Ele sunt sintetizate in tabelul de mai jos:
Clasă Responsabilităţi
Sale să cunoască valoarea totală a cumpărăturilor
SalesLineItem să cunoască subtotalul pentru un produs
ProductSpecification să cunoască preţul produsului
Diagrama de clase actualizata este urmatoarea:

81
Sale
-date
-time
+getTotal()

Contains

1..*
ProductSpecification
SalesLineItem Described by 1
* -description
-quantity
-price
+getSubtotal()
-itemId
+getPrice()

Principiul conform caruia a fost asignata fiecare responsabilitate este Ex-


pertul (Information Expert); responsabilitatea i-a fost asignata obiectului care
avea informatiile necesare indeplinirii ei. Indeplinirea unei responsabilitati ne-
cesita deseori informatii care sunt impartite in mai multe clase. Aceasta implica
existenta mai multor ”experti partiali”, care vor colabora in realizarea sarcinii.
Colaborarea se realizeaza apeland metode ale altor obiecte in scopul accesarii
sau partajarii informatiilor pe care acestea le detin.

6.1.2 Creator

Problemă: cine trebie să fie responsabil cu crearea unei instanţe a unei clase?
Soluţia propusa de Creator este sa-i fie asignata clasei B responsabilitatea de
a crea instanţe ale clasei A doar dacă cel puţin una dintre următoarele afirmaţii
este adevărată:

• B agregă obiecte de tip A

• B conţine obiecte de tip A

• B foloseşte obiecte de tip A

• B are datele de iniţializare care trebuie transmise la instanţierea unui


obiect de tip A (B este deci un Expert ı̂n ceea ce priveşte crearea obiectelor
de tip A)

In exemplul unei aplicatii care implementeaza un POS cine ar trebui sa fie


responsabil cu crearea unei unei instanţe a clasei SalesLineItem?.

82
Sale
-date
-time

1
Contains

1..*
ProductSpecification
SalesLineItem * Described by 1
-description
-quantity
-price
-itemId

Deoarece Sale conţine (agregă) instanţe de tip SalesLineItem, clasa Sale


este un bun candidat pentru a i se atribui responsabilitatea creării acestor
instanţe. Diagrama de secventa corespunzatoare instantierii unui obiect de tip
SalesLineItem este urmatoarea:

:Register :Sale

addLineItem(itemType, quantity)
create(itemType, quantity)
:SalesLineItem

6.1.3 Low coupling (cuplaj redus)


Cuplajul este o măsură a gradului de dependenţă a unei clase de alte clase.
Prin dependenţă intelegem situatii in care o clasa
• este conectată cu
• are cunoştinţe despre
• se bazează pe
alta clasa.
O clasă care are cuplaj mic (redus) nu depinde de “multe” alte clase (nu este
dependenta de context). Prin contrast, o clasă care are cuplaj mare depinde de
multe alte clase (este puternic dependenta de context). Nu este de dorit sa
avem clase care au un cuplaj mare, deoarece ele conduc deseori la aparitia unor
probleme cum ar fi:

• schimbări ı̂n clasele relaţionate forţează schimbări locale


• sunt clase greu de ı̂nţeles ı̂n izolare (scoase din context)
• sunt clase greu de refolosit deoarece folosirea lor presupun prezenţa şi a
claselor de care depind

83
Forme comune de cuplaj de la o clasa A la o clasa B sunt:

• A are un atribut de tip B

• O instanţă a clasei A apelează un serviciu oferit de un obiect de tip B

• A are o metodă care referenţiază B (parametru, obiect local, obiect retur-


nat)

• A este subclasă (direct sau indirect) a lui B

• B este o interfaţă, iar A implementează această interfaţă

Cuplajele pot fi usor vizualizate in diagrama de clase sau diagrama de cola-


borare.
1:create()
makePayment()
:Register p:Payment

2:addPayment(p)
:Sale

(a)

makePayment() 1:makePayment()
:Register :Sale

1.1:create()

(b) :Payment

In figura (a) există legături ı̂ntre toate clasele, pe când ı̂n figura (b) este
eliminat cuplajul dintre Register şi Payment.

6.1.4 High Cohesion (coeziune mare)


Coeziunea este o măsură a cât de puternic sunt focalizate responsabilităţile
unei clase. Despre o clasă ale cărei responsabilităţi sunt foarte strâns legate şi
care nu face foarte multe lucruri spunem ca are o coeziune mare. Prin contrast, o
clasa care face multe lucruri care nu sunt relaţionate sau face prea multe lucruri
are o coeziune mică (slabă). Nu este de dorit sa avem clase cu o coeziune mica
deoarece ele conduc la aparitia unei serii de probleme, cum ar fi:

• dificultate de ı̂nţelegere

• greu de refolosit

• intretinere greoaie

• delicate; astfel de clase sunt mereu supuse la schimbări

Coeziunea şi cuplajul sunt principii vechi ı̂n proiectarea software. Ambele
promovează un design modular. Modularitatea este proprietatea unui sistem
care a fost descompus ı̂ntr-o mulţime de module coezive şi slab cuplate.

84
6.1.5 Controller
Problemă: Cine este responsabil cu tratarea unui eveniment generat de un
actor? Aceste evenimente sunt asociate cu operaţii ale sistemului.
Un Controller este un obiect care nu ţine de interfaţa grafică şi care este
responsabil cu recepţionarea sau gestionarea unui eveniment. Pentru fiecare
operaţie a sistemului controlerul defineşte o metoda corespunzătoare.
Soluţia propusă de acest model de proiectare este de a asigna responsa-
bilitatea pentru recepţionarea sau gestionarea unui eveniment unei clase care
reprezintă una dintre următoarele alegeri:
• Reprezintă ı̂ntregul sistem sau subsistem (façade controller)
• Reprezintă un scenariu de utilizare ı̂n care apare evenimentul; deseori
acesta este numit [UseCaseName]Handler, [UseCaseName]Coordinator
sau [UseCaseName]Session (use-case or session controller). Informal,
o sesiune reprezintă o instanţă a unei conversaţii cu un actor. Sesiunile
pot avea orice lungime, ı̂nsă sunt organizate, de obicei, ı̂n termeni de sce-
narii. Se recomandă folosirea unei singure clase Controller pentru toate
evenimentele care apar ı̂n acelaşi scenariu de utilizare.
În mod normal, un controller ar trebui sa delege altor obiecte munca ce tre-
buie făcută. Controller-ul coordonează sau controlează activitatea, ı̂nsă nu face
prea multe lucruri el ı̂nsuşi. O greşeală comună ı̂n proiectarea unui controller
este să i se atribuie prea multe responsabilităţi (mai ales in cazul unui façade
controller). Un UseCaseController este o alegere bună ı̂n cazul ı̂n care există
multe evenimente care apar ı̂n procese diferite. În această situaţie se recomandă
o ı̂mpărţire a responsabilităţilor ı̂n mai multe clase. Soluţia obţinută ı̂n acest
fel este mai uşor de gestionat şi, ı̂n plus, oferă o bază pentru a putea determina
starea scenariului aflat ı̂n desfăşurare.

6.2 Singleton
Uneori intr-o apliactie este nevoie de un obiect global care sa poata fi accesat de
oriunde, insa care trebuie creat o singura data. Vrem ca toate modulele aplicatiei
sa poata folosi obiectul, insa toate trebuie sa foloseasca aceeasi instanta.
De exemplu, am putea dori ca ı̂ntr-o aplicaţie să avem definit un singur
gestionar de securitate care sa se ocupe de drepturile de acces ale utilizatorilor
la diferite parti ale aplicatiei sau un singur gestionar de conexiuni la baza de
date, iar intr-un sistem ar trebui sa avem o singura coda de imprimare pentru
fiecare imprimanta disponibila.
O modalitate de a realiza acest lucru ar fi sa cream un obiect global, iar
apoi sa transmitem o referinta la acest obiect tuturor obiectelor care ar putea
avea nevoie de el. Totusi, este dificil sa ne dam seama cum vrem sa transmitem
referinta si sa stim de la inceput ce parti ale aplicatiei au nevoie sa foloseasca
obiectul. Un alt dezavantaj al acestei abordari este acela ca nu impiedica un alt
obiect sa creeze o alta instanta a obiectului global.
O alta abordare posibila pentru a putea avea valori globale ar fi sa folosim
variabile statice. Aplicatia poate avea cateva obiecte statice in interiorul unei
clase si le poate accesa in mod direct. Exista, insa, si in acest caz dezavantaje.
Obiectele statice se creeaza la momentul incarcarii unei clase, deci nu avem

85
nici o modalitate de a le furniza date de initializare inainte de instantiere. Mai
mult, nu putem controla cine acceseaza obiectul si cum este acesta modificat, o
instanta statica fiind disponibila public oricui.
In aceste situatii modelul de proiectare Singleton devine util. Un singleton
(sau unicat) este o clasă ce poate avea o singură instanţă. Singletonul este
util atunci când se doreşte garantarea faptului că o anumită resursă există ı̂n
aplicaţie ı̂ntr-o singură copie şi că toate componentele pot folosi doar acea copie.
Secvenţa de cod de mai jos arată cum ar putea fi folosit un unicat:

SecurityManager security = SecurityManager.getInstance();


if (!security.hasAccess(currentUser))
return;

Mai jos este dată diagrama de clase pentru un singleton:

Singleton
static uniqueInstance: return uniqueInstance
singletonData
static GetInstance()
Operation()
GetSingletonData()

Pentru a avea control asupra procesului de instantiere constructorul clasei


trebuie sa fie privat. Apare insa o problema: devine imposibil sa creeam chiar
si o singura instanta. De aceea este oferita o metoda statica getInstance().
Aceasta metoda are responsabilitatea de a crea o singura instanta, in cazul in
care nu exista deja, si de a intoarce o referinta la singleton apelantului metodei.
Referinta la singleton este si ea memorata ca un atribut static privat care va fi
folosit in cadrul apelurilor ulterioare ale metodei getInstance().
Un avantaj al unicatului faţă de soluţia folosirii numai de membri şi metode
statice constă ı̂n faptul că resursa este instanţiată numai dacă este nevoie de
ea. În plus, se pot specifica ı̂ntr-un mod mai elegant parametrii de iniţializare
ai resursei.
O secventa de cod care arata o posibila implementare a diagramei de mai
sus, precum si modul de utilizare al unui singleton, sunt prezentate in cele ce
urmeaza:

public class Singleton {


private static Singleton uniqueInstance = null;
private Data singletonData;

private Singleton() {
//initialization code here;
}

public static Singleton GetInstance () {


if( uniqueInstance == null ) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}

86
public void Operation() {
}

public Data GetSingletonData() {


return singletonData;
}
}

public class Client {


public static void main( String arg[] ) {
try {
Singleton instance = Singleton.GetInstance ();
instance.Operation();
} catch( Exception e ) {
e.printStackTrace();
}
}
}

6.3 Composite
Un compozit se foloseşte pentru a reprezenta relaţii de tip parte-intreg ı̂ntre
obiecte, cu posibilitatea de a grupa obiectele ı̂n structuri arborescente. Obiectele
şi grupurile de obiecte sunt tratate uniform.
De exemplu, să considerăm o aplicaţie de editare grafică care lucrează cu cer-
curi, triunghiuri si pătrate. În această aplicaţie este posibil ca anumite obiecte
selectate să fie grupate. Atunci când se acţionează asupra unui astfel de grup,
se acţionează asupra tuturor obiectelor din grup: dacă se translatează grupul,
atunci fiecare obiect ı̂n parte este translatat. Mai mult este posibil să se creeze
grupuri care să conţină atât obiecte atomice, cât şi alte grupuri create anterior.
Astfel, grupurile de obiecte pot fi tratate ca şi cum ar fi obiecte individuale.
Diagrama de clase pentru un compozit este prezentată mai jos.

Component

Client Operation()
Add(c:Component) child
Remove(c:Component)
GetChild(i:int)

Leaf Composite for all c:child


c->Operation()
Operation() Operation()

În cazul nostru, o metodă de tip Operation ar putea fi Move, care realizează
deplasarea obiectului. Compozitul ar implementa metoda Move astfel:

87
void Composite::Move(double dx, double dy)
{
for(Iterator it = GetChildrenIterator(); it.hasNext(); it.next()) {
Component child = it.hasItem();
child.Move(dx, dy);
}
}

Un exemplu de cod Java care implementeaza diagrama de mai sus este pre-
zentat in cele ce urmeaza:

public interface Component {


void operation();
void add( Component component );
boolean remove( Component component );
Component getChild( int index );
}

public class Leaf implements Component {


public void operation() { }
public void add( Component component ) { }

public boolean remove( Component component ) {


return false;
}

public Component getChild( int index ) {


return null;
}
}

public class Composite implements Component {


private Vector children = new Vector();
public void operation() {
int i;
for( i = 0; i < children.size(); ++i ) {
Component component =
(Component)children.elementAt(i);
component.operation();
}
}
public void add( Component component ) {
children.addElement( component );
}
public boolean remove( Component component ) {
return children.removeElement( component );
}
public Component getChild( int index ) {
return (Component) children.elementAt(index);
}

88
}

public class Client {


public static void main( String arg[] ) {
Component composite = new Composite();
Component leaf1 = new Leaf();
Component leaf2 = new Leaf();
composite.add( leaf1 );
composite.add( leaf2 );
composite.operation();
}
}

6.4 Iterator
Un iterator oferă posibilitatea de a accesa ı̂n mod secvenţial obiectele agregate
ı̂ntr-un alt obiect, fără a cunoaşte modul de agregare.
In mod tipic, un iterator it este folosit astfel:

for(it.First();!it.IsDone();it.Next()) {
Item item = it.CurrentItem();
// use item
}

Acest cod are avantajul de a fi simplu, expresiv si ne scuteste de eventuale


probleme care ar putea aparea ulterior.
De exemplu, daca valorile sunt tinute minte intr-un vector, codul de parcur-
gere ar fi urmatorul:

for(int i=0; i<v.lenght; i++) {


Item item = v[i];
// use item
}

Acest cod poate fi scris si utilizat fara probleme. Daca insa la un moment
vom dori, intr-un mod eficient, sa adaugam in structura de date noi inregistrari,
sau sa stergem inregistrari existente, va trebui sa ne gandim la o alta structura
de date, de exemplu la o lista inlantuita. Lista inlantuita va rezolva problemele
de mai sus, insa ne va obliga sa modificam codul de parcurgere la:

for(item=first; item != null; item=item.next) {


// use item
}

La prima vedere aceasta modificare nu pare a fi o prea mare problema, dar


sa ne gandim ca secvente de acest tip se pot gasi in sute de locuri in cadrul
unui program mai mare. Modificarea codului in toate aceste locuri nu numai
ca este foarte scumpa (cineva trebuie platit sa faca asta), dar si foarte riscanta
(se pot introduce erori). De aceea, putem admira solutia simpla si eleganta
din prima secventa de cod, care nu necesita nici o modificare atunci cand tipul
containerului se schimba.

89
Iteratorii sunt utili şi din alt punct de vedere. La un moment dat ı̂ntr-un
program pot fi folosite containere de mai multe tipuri, fiecare folosind modali-
tatea sa specifica de a agrega elemente. Desigur, pentru fiecare tip de container
trebuie implementat un iterator specific, care să implementeze modul de iterare
printre elemente. Ceea ce trebuie remarcat este că, oricare ar fi un iterator
concret, codul de utilizare este identic. Astfel, apare ca utilă ideea de a crea o
interfaţă pentru toţi iteratorii, şi de a se folosi acea (unică) interfaţă pentru a
enumera elementele containerelor.
Astfel, codul următor:
void process(Iterator& it)
{
for(it.First();!it.IsDone();it.Next()) {
Item item = it.CurrentItem();
// use item
}
}
poat fi apelat şi pentru iteratori peste vectori şi pentru iteratori peste liste:
In general, schema unui iterator este urmatoarea:

Client

Iterator
Aggregate
First
CreateIterator() Next()
IsDone()
CurrentItem()

ConcreteAggregate ConcreteIterator
CreateIterator()

return new
ConcreteIterator(this)

O secventa de cod care arata o posibila implementare a diagramei de mai


sus, precum si modul de utilizare al unui iterator, sunt prezentate in cele ce
urmeaza:

public interface Iterator {


Object first() throws IndexOutOfBoundsException;
Object next() throws IndexOutOfBoundsException;
boolean isDone();
Object currentItem() throws IndexOutOfBoundsException;
}

90
public interface Aggregate {
Iterator createIterator();
}

public class ConcreteAggregate implements Aggregate {


public Vector storage = new Vector();

public Iterator createIterator() {


return new ConcreteIterator( this );
}
}
public class ConcreteIterator implements Iterator {
private ConcreteAggregate aggregate;
private int index = 0;

public ConcreteIterator( ConcreteAggregate aggregate ) {


this.aggregate = aggregate;
}

public Object first() throws IndexOutOfBoundsException {


Object object = null;
if( !aggregate.storage.isEmpty() ) {
index = 0;
object = aggregate.storage.firstElement();
} else {
throw new IndexOutOfBoundsException();
}
return object;
}
public Object next() throws IndexOutOfBoundsException {
Object object = null;
if( index + 1 < aggregate.storage.size() ) {
index += 1;
object = aggregate.storage.elementAt(index);
} else {
throw new IndexOutOfBoundsException();
}
return object;
}
public boolean isDone() {
boolean result = false;
if( aggregate.storage.isEmpty() ||
index == aggregate.storage.size() - 1 ) {
result = true;
}
return result;
}
public Object currentItem() throws IndexOutOfBoundsException {
Object object = null;

91
if( index < aggregate.storage.size() ) {
object = aggregate.storage.elementAt( index ); } else {
throw new IndexOutOfBoundsException();
}
return object;
}
}//class

public class Client {


public static void main( String arg[] ) {
Aggregate aggregate = new ConcreteAggregate();
Iterator iterator = aggregate.createIterator();

for(iterator.first(); !iterator.isDone(); iterator.next()) {


Object currentItem = iterator. currentItem();
/** * Process currentItem */
}
}
}

92
Capitolul 7

Testare

Scopul dezvoltării software este acela de a obţine un program conform cu specificaţiile


pe baza cărora a fost scris şi care corespunde aşteptărilor clientului. Testa-
rea este activitatea prin care sunt scoase ı̂n evidenţă defecte ale unui program
ı̂nainte ca programul să fie livrat. Există două procese diferite strâns legate de
activitatea de testare. Acestea sunt validarea şi verificarea.
Validarea este procesul prin care ne asigurăm că programul este corect din
punctul de vedere al clientului. Întrebarea care se pune ı̂n cazul validării este:
“Construim produsul corect?” (Are we building the right product?).
Verificarea este procesul prin care ne asigurăm că programul este corect
din punctul de vedere al programatorului. Întrebarea care se pune aici este
“Construim corect produsul?” (Are we building the product right?).
Validarea şi verificarea au loc la fiecare etapă a procesului de dezvoltare a
unui program. Cea mai mare parte a acestor procese, ı̂nsă, se desfăşoară atunci
când sistemul este operaţional, adică după faza de implementare.
Cu excepţia programelor mici, sistemele nu ar trebui testate de la ı̂nceput
ca un ı̂ntreg nedivizibil. Sistemele mari sunt construite din subsisteme. Subsis-
temele, la rândul lor, sunt formate din module. Modulele conţin proceduri şi
funcţii. Procesul de testare trebuie efectuat ı̂n etape pe măsură ce sistemul este
implementat.
Figura 7.1 prezintă un proces de testare ı̂n cinci etape ı̂n care componentele
sunt testate mai ı̂ntâi individual, apoi sunt integrate ı̂n sistem. Sistemul este
testat pe măsură ce noi componente sunt integrate şi, ı̂n final, sistemul este
testat cu date primite de la client. În cazul ideal, defectele componentelor sunt
descoperite devreme ı̂n procesul de testare, iar problemele legate de interfeţe
ies ı̂n evidenţă la integrare. Totuşi, defectele descoperite ı̂n program trebuie
reparate, astfel apărând noi etape ı̂n procesul de testare. Procesul de testare este
un proces iterativ. Informaţii obţinute ı̂n etapele târzii sunt furnizate etapelor
timpurii.
Etapele procesului de testare sunt:
1. Testarea unităţilor. Componentele individuale sunt testate pentru a se
asigura funcţionarea lor corectă. Fiecare componentă este testată inde-
pendent.
2. Testarea modulelor. Un modul reprezintă o colecţie de componente care
depind unele de altele. Exemple de module sunt clase de obiecte, ti-

93
T estarea
unitatilor
T estarea
modulelor
T estarea
subsistemelor
T estarea
sistemului
T estul
de acceptare

T estarea T estarea T estarea


componentelor la integrare f acuta de utilizator

Figura 7.1: Proces de testare ı̂n cinci etape

puri abstracte de date sau colecţii de proceduri şi funcţii. Un modul


ı̂ncapsulează componente ı̂nrudite care pot fi testate fără a necesita prezenţa
altor module din sistem.

3. Testarea subsistemelor. Această fază presupune testarea unei colecţii


de module care au fost integrate ı̂ntr-un subsistem. Cele mai obişnuite
probleme care apar ı̂n această etapă sunt cele legate de nepotriviri ale
interfeţelor. Procesul de testare al subsistemelor trebuie, deci, să se con-
centreze pe detectarea erorilor de interfaţă ale modulelor exersând ı̂n mod
riguros aceste interfeţe.

4. Testarea sistemului. Subsistemele sunt integrate formând sistemul. Acest


proces vizează găsirea erorilor care apar datorită problemelor de interacţiune
ı̂ntre subsisteme şi ı̂ntre interfeţe ale subsistemelor. Această fază se ocupă
şi de validarea sistemului, precum şi de testarea unor proprietăţi ale sis-
temului.

5. Testul de acceptare. Este etapa finală a procesului de testare ı̂nainte ca


sistemul să fie acceptat ı̂n vederea folosirii lui. În această fază sistemul este
testat cu date furnizate de către client ı̂n loc de datele de test simulate
folosite ı̂n celelalte etape. Testul de acceptare poate scoate ı̂n evidenţă
erori şi omisiuni ı̂n definirea cerinţelor deoarece datele reale pot exersa
sistemul ı̂n mod diferit faţă de datele de test. Testul de acceptare poate
evidenţia şi probleme legate de modul (uşurinţa) de utilizare sau probleme
de performanţă ale sistemului.

Testarea unităţilor este responsabilitatea programatorilor care dezvoltă com-


ponenta. Programatorii ı̂şi construiesc propriile date de test şi testează codul
incremental, pe măsură ce este scris. Ei sunt cei mai ı̂n măsură să facă acest
lucru deoarece cunosc cel mai bine componenta pe care o dezvoltă. Această

94
Localizeaza P roiecteaza modalitatea Corecteaza Retesteaza
eroarea de corectare a erorii eroarea programul

Figura 7.2: Etapele unui proces de debug

Specif icarea Specif icatiile P roiectarea P roiectarea


cerintelor sistemului arhitecturala detaliata

P lanul testului P lanul testului P lanul testului


T estarea unitatilor
de de integrare de integrare
si a modulelor
acceptare a sistemului a subsistemelor

T estul de T estarea T estarea


M entinere
acceptare sistemului subsistemelor

Figura 7.3: Etapele testării ı̂n cadrul procesului de dezvoltare software

activitate scoate deseori ı̂n evidenţă defecte ale programului care trebuie elimi-
nate. Procesul care se ocupă cu localizarea şi corectarea erorilor din program
poartă denumirea de debugging. Figura 7.2 ilustrează un posibil proces de de-
bug. Defectele ı̂n codul sursă trebuie localizate, iar programul trebuie să fie
modificat pentru a satisface cerinţele. Testarea trebuie repetată pentru a avea
siguranţa că modificările făcute sunt corecte. Deci procesul de debug este o
parte atât dezvoltării cât şi a testării unui program. Testarea unităţilor este,
prin urmare, o parte a procesului de implementare. Ca rezultat al etapei de tes-
tare a unităţilor sunt aşteptate componente conforme cu specificaţiile pe baza
cărora au fost dezvoltate.
Etapele următoare presupun integrarea muncii mai multor programatori.
Ele trebuie planificate şi o echipă trebuie să lucreze pe un plan de testare dezvol-
tat pe baza specificaţiilor şi a designului sistemului. Figura 7.3 ilustrează modul
ı̂n care planurile de test sunt legate de activităţile de testare şi dezvoltare.
Testarea ı̂n vederea acceptării mai poartă denumirea de testare alpha (al-
pha testing). În această situaţie sistemele sunt dezvoltate pentru un singur
client. Procesul de testare alpha continuă până când clientul şi dezvoltatorul
cad de acord asupra faptului că sistemul oferit este o implementare acceptabilă
a cerinţelor clientului.
Când sistemul va fi vândut ca un produs pe piaţă procesul de testare poartă
denumirea de testare beta (beta testing). Testarea beta presupune livrarea siste-
mului unui număr de potenţiali clienţi care sunt de acord să folosească sistemul.
Ei vor raporta problemele ı̂ntâlnite celor care dezvoltă sistemul. Acest tip de
utilizare reală a sistemului va evidenţia erori care nu au fost anticipate de cei
care construiesc produsul. În urma acestor reacţii sistemul este modificat şi
livrat fie pentru o nouă fază de testare beta, fie pentru vânzare.

95
Cazurile Datele Rezultatele Rapoarte
de test de test testului de testare

P roiecteaza P regateste Ruleaza programul Compara reazultatele


cazurile de test datele de test cu datele de test pentru cazurile de test

Figura 7.4: Model general de testare

7.1 Metode de testare


Scopul testării este de a scoate ı̂n evidenţă defecte ale unui program ı̂nainte ca
programul să fie livrat. Un test reuşit este acela care face programul să se com-
porte incorect, care evidenţiază un defect. Testarea demonstrează prezenţa, şi
nu absenţa, erorilor ı̂ntr-un program. Un model general de testare este prezentat
ı̂n figura 7.4.
Cazurile de test sunt specificaţii care conţin intrări ale testului, rezultatele
aşteptate din partea sistemului, precum şi o precizare a cerinţei testate. Datele
de test sunt date de intrare concepute pentru testarea programului sau com-
ponentei. Ele pot fi uneori generate automat. Generarea automată a cazurilor
de test este ı̂nsă imposibilă deoarece rezultatele aşteptate nu pot fi prezise ı̂n
această situaţie.
Testarea exhaustivă este, dacă nu imposibilă, cel puţin impractică. De aceea
ı̂n practică testarea se bazează pe alegerea unei submulţimi ale cazurilor posibile
de test.

7.1.1 Testarea funcţională (Black-box testing)


În cazul testării funcţionale testele sunt derivate din specificaţiile programului
sau componentei. Persoana care face testarea (tester) este preocupată doar de
funcţionalitatea programului, nu şi de modul ı̂n care este implementat acesta.
Programul este considerat o cutie neagră a cărui comportament poate fi de-
terminat doar studiind intrările şi iesşirile corespuzătoare. Schema generală a
modelului de testare funcţională este ilustrată ı̂n figura 7.5.
Deoarece un test reuşit este cel care evidenţiază prezenţa unei erori, pro-
blema principală care apare ı̂n cazul testării funcţionale constă ı̂n selectarea
unor date de test care să aparţină cu o probabilitate mare mulţimii Ie . În multe
situaţii selecţia acestei mulţimi de date de test este bazată pe experienţa celui
care face testarea şi care foloseşte cunoştinţe din domeniul problemei. Există
ı̂nsă şi o abordare sistematică ı̂n care datele de intarea se ı̂mpart ı̂n clase de
echivalenţă. Datele din aceeaşi clasă de echivalenţă au caracteristici comune.
În mod normal, programul se va comporta ı̂n mod similar pentru toţi membrii
aceleiaşi clase.
Clasele de echivalenţă se identifică folosind specificaţiile programului. Odată
identificate, datele de test se aleg din fiecare clasă (este indicat să se testeze
graniţele şi un punct de mijloc al clasei de echivalenţă). În cazul ı̂n care datele
de intrare pot avea dimensiuni diferite este bine ca aceste dimensiuni să varieze

96
Date care cauzeaza
un comportament anormal

Date de test Ie

Program

Rezultate care evidentiaza


prezenta unor erori

Rezultate Oe

Figura 7.5: Schema generală a modelului de testare funcţională

Date de test

testeaza deriveaza

Codul componentei Rezultate

Figura 7.6: Schema generală a modelului de testare structurală

la fiecare test.

7.1.2 Testarea stucturală (White-box testing)


În cazul testării structurale testele sunt construite tinând cont de implementa-
rea programului (7.6). Acest tip de testare se aplică la unităţi de program de
dimensiuni mici (funcţie, operaţie asociată unui obiect). Ideea este ca fiecare
instrucţiune din program să fie executată cel puţin o dată. Punctul de plecare
ı̂n cazul testării structurale ı̂l constituie graful execuţiei programului. Acesta se
obţine analizând codul sursă. Drept noduri sunt considerate punctele de deci-
zie (ramificaţie) din program. Un singur nod va reprezenta toate instrucţiunile
care se exucută secvenţial pe o aceeaşi ramură (atribuiri şi instrucţiunile de
intare/ieşire). Muchiile grafului indică fluxul execuţiei programului.
Scopul ı̂n cazul testării structurale este ca fiecare drum independent din
program să fie executat cel puţin o dată. Un drum independent este un drum
care conţine cel puţin o muchie netestată din graful execuţiei. Fiecare punct
de ramificaţie trebuie testat ı̂n ambele situaţii (ı̂n cazul ı̂n care condiţia este
adevărată şi ı̂n cazul ı̂n care condiţia este falsă).

97
T1
T1
A
T1 T2
A
A T2 B
T2 B T3
B T3 C
T3 C T4
D
T4
T5

Figura 7.7: Exemplu de testare la integrare

7.1.3 Testarea la integrare


După ce componentele programului au fost testate individual, ele trebuie inte-
grate pentru a crea un sistem parţial sau complet. Acest proces de integrare
presupune construirea şi testarea sistemului rezultat ı̂n vederea descoperirii pro-
blemelor care apar datorită interacţiunii dintre componente. Testele de integrare
trebuie să fie dezvoltate pe baza specificaţiilor sistemului, iar testarea integrării
componentelor ar trebui să ı̂nceapă ce ı̂ndată ce sunt gata versiuni funcţionale
ale unor componente ale sistemului.
Problema principală care apare ı̂n cazul acestui tip de testare constă ı̂n
localizarea erorilor descoperite ı̂n timpul procesului de testare. De multe ori
interacţiunile ı̂ntre componentele sistemului sunt complexe, iar atunci când este
obţinut un rezultat eronat este greu de descoperit sursa care a cauzat eroarea.
Pentru a face localizarea erorilor mai uşoară se recomandă folosirea integrării
incrementale a componentelor. Iniţial ar trebui integrată o configuraţie mini-
mală şi testat sistemul obţinut. Celelalte componente vor fi adăugate una câte
una, sistemul fiind retestat după fiecare nouă adăugare a unei componente.
Figura 7.7 prezintă un exemplu de testare la integrare. Testele T 1, T 2, T 3
sunt mai ı̂ntâi rulate pe o configuraţie minimală formată din componentele A
şi B. Dacă sunt evidenţiate erori, acestea sunt corectate. Un nou modul, C,
este integrat, şi testele T 1, T 2, T 3 sunt repetate pentru avea siguranţa că nu
au apărut erori neaşteptate. Dacă apar erori, este probabil ca acestea să fie
cauzate de interacţiunea celor trei componente. În acest mod este localizată
sursa problemei, simplificându-se localizarea şi remedierea erorii.

7.1.4 Testarea interfeţelor


Are loc ı̂n procesul de integre a modulelor sau subsistemelor ı̂n sisteme mai
mari. Fiecare modul sau subsistem are o interfaţă care poate fi apelată de
alte module. Obiectivul ı̂n acest caz este de a detecta erori ale interfeţelor sau
presupuneri greşite legate de interfeţe. Problemele apărute se ı̂ncadrează ı̂n una
din următoarele categorii:

• Folosirea greşită a interfeţei. O componentă apelează greşit altă com-


ponentă. Această eroare apare ı̂n cazul interfeţelor parametrizate şi se

98
poate datora trimiterii unor parametri având un alt tip decât cei ceruţi de
interfaţă, sau trimiterea ı̂ntr-o altă ordine a parametrilor, sau trimiterea
unui număr greşit de parametri.
• Neı̂nţelegeri legate de interfaţă. O componentă apelantă ı̂nţelege greşit
specificaţiile interfeţei şi face o presupunere eronată legată de comporta-
mentul componentei apelate. Această presupunere va cauza o eroare ı̂n
funcţionarea componentei apelante.

• Erori de timp. Apar ı̂n cazul sistemelor ı̂n timp real când se foloseşte
memorie partajată sau transmitere de mesaje.

7.1.5 Testarea la stress


Are drep scop scoaterea ı̂n evidenţă a performanţei şi siguranţei sistemului.
Testarea la stress presupune dezvoltarea unei baterii de teste ı̂n care sistemul
este supraı̂ncărcat. Se testează astfel comportamentul sistemului atunci când
acesta cedează. Este important ca ı̂n cazul ı̂n care sistemul cedează acest lucru
să nu cauzeze coruperea datelor.

7.1.6 Testarea orientată obiect


Se desfăşoară pe mai multe nivele:

1. testarea individuală a operaţiilor unui obiect. Acestea sunt funcţii şi pro-
ceduri. Pentru testarea lor se poate folosi testarea funcţională sau testarea
structurală.

2. testarea unei clase de obiecte. Se bazează pe principiul cutiei negre, ı̂nsă


conceptul de clasă de echivalenţă trebuie extins pentru a acoperi secvenţe
de operaţii ı̂nrudite.

3. testarea unui cluster de obiecte. Se bazează pe construirea de scenarii ı̂n


care sunt implicate obiectele.

4. testarea sistemului orientat obiect. Presupune validarea şi verificarea sis-


tenului pe baza specificaţiilor cerinţelor.

If you think all change is for the better, why put it to the test?
— DISCOVERY CHANNEL, promo

99
100
Bibliografie

[1] Douglas Bell. Software Engineering. A Programming Approach. Addison-


Wesley, 2000.

[2] Ivar Jacobson Grady Booch, James Rumbaugh. The Unified Modeling Lan-
guage User Guide. Addison-Wesley, 1999.

[3] Roy Miller Ken Auer. Extreme Programming Applied. Playing to Win.
Addison-Wesley, 2002.

[4] OMG, editor. Unified Modeling Language (UML) Specification: Infrastruc-


ture. OMG, 2005.

[5] Roger Penrose. The Emperor’s New Mind. Penguin Books, 1989.

[6] James Rumbaugh, Michael Blaha, William Lorensen, Frederick Eddy, and
William Premerlani. Object-Oriented Modeling and Design. Addison-Wesley,
1991.

[7] Jan Somerville. Software Engineering. Addison-Wesley, 6 edition, 2001.

[8] Bjarne Stroustrup. The C++ Programming Language. Pearson Education,


1997.

101

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