Sunteți pe pagina 1din 26

Software design

- concepte, exemple -
1. Introducere

Design-ul este ‘‘the process of applying various techniques and principles


for the purpose of defining a device, a process, or a system in sufficient
detail to permit its physical realization.’’ (Taylor, An Interim Report of
Engineering Design, MIT, 1959)

Procesul de design converteşte “ce” din cadrul cerinţelor în “cum” al


design-ului. Rezultatele fazei de design ar trebui să se concretizeze într-un
document ce oferă suficiente detalii care să permită sistemului să fie
implementat fără o viitoare interacţiune cu utilizatorul.

Procesul de design converteşte de asemenea terminologia din spaţiul


problemei al cerinţelor la spaţiul soluţie al implementării. Unii autori
vorbesc de obiectele OOA (Object-Oriented Analysis), care sunt în spaţiul
problemă/domeniu şi de obiectele OOD (Object-Oriented Design), care
sunt în spaţiul soluţie/implementare.
Se vorbeşte despre fenomenon în mediul înconjurător (lumea) şi de
fenomenon în implementare (maşină). Un fenomenon poate fi vizibil sau
ascuns. Cerinţele orientate pe utilizator pot fi exprimate în termeni de
fenomenon, vizibil sau ascuns, din mediu (environment). (Gunter, Gunter,
Jackson, and Zave, ‘‘A Reference Model for Requirements and Specifications’’,
IEEE Software, May/June 2000)

Exemplu. Identificaţi ce fenomenon este în mediu şi ce fenomenon în


implementare în sistemul care gestionează împrumutul de cărţi într-o
bibliotecă. (vezi model_biblioteca)
Soluţie. Cartea propriu-zisă este un fenomenon ascuns al mediului
(environment-hidden). Când bibliotecarul scanează cartea, scanează de fapt un
cod de bare. Codul de bare nu conţine ISBN-ul, dar trebuie să reflecte
eventualele copii multiple ale unei singure cărţi. Acest code de bare este un
fenomenon vizibil al mediului. Implementarea foloseşte probabil un identificator
sau pointer pentru datele referitoare la carte. Acest identificator intern este un
fenomenon ascuns al implementării. Specificarea pentru dezvoltare trebuie să fie
scrisă în termeni de cod de bare.
2. Fazele procesului de design

Data design (designul de date) — această fază produce the structurile de


date.
Architectural design (designul arhitecturii) — această fază produce the
unităţile structurale (clasele).
Interface design (designul interfeţelor) — această fază specifică interfeţele
dintre unităţi.
Procedural design (designul procedural) — această fază specifică algoritmii
fiecărei metode.
Exemplu. Realizaţi designul claselor şi al structurilor de date pentru
sistemul de gestiune a împrumutului de cărţi dintr-o bibliotecă.
Soluţie. Atenţia este concentrată pe împrumutul cărţilor şi mai puţin pe alte
informaţii, cum ar fi catalogarea, administrarea, retragerea cărţilor şi administrarea
clienţilor bibliotecii. Identitatea “carte” se va combina cu identitatea “copie” în
structura de clase/date pentru stocarea informaţiilor despre o copie. Poate conţine
ISBN-ul şi un număr de copie ca unic identificator. Informaţiile despre cititori se vor
păstra într-o structură de date secundară, în care probabil fiecare înregistrare va fi
identificată printr-un ID unic (poate fi CNP-ul). Informaţiile referitoare la împrumut
pot fi puse într-o structură de date separată sau nu.
Diagrama de clasă
2.1 Interfeţele
Interfaţa arată comportarea exterioară a unui modul. Trebuie să fie suficient
de completă pentru ca modulul apelant să ştie ce va face modulul apelat în
orice situaţie. Trebuie să fie completă pentru ca cel care implementează să
ştie exact ce informaţii sunt necesare.
Exemplu. Realizaţi designul interfeţelor pentru functiile imprumutare din
diagrama de clase de mai sus.
Soluţie. Clasele cititor şi copiecarte au o metodă imprumutare. Este de presupus
că apelul oricăreia dintre aceste metode va instanţia clasa imprumut.
method cititor::imprumutare
input parameters – isbn
return type – int
0 dacă nu este disponibilă cartea
1 dacă este disponibilă cartea şi instanţa imprumut se creează cu succes
-1 dacă apare o condiţie de eroare

method copiecarte::imprumutare
input parameter – NrImprumut
return type – int
0 dacă nu e disponibilă copia
1 dacă e disponibilă copia
3. Concepte ale designului

Refinement (rafinare) — dezvoltă designul prin rafinări succesive; se mai


numeşte designul “top-down”.
Modularity (modularitate) — e o abordare care divide software-ul în
componente mai mici, care apoi sunt integrate.

Exemplu. Rafinaţi funcţia imprumutare din sistemul de împrumutare cărţi


de la o bibliotecă, prezentat în exemplele anterioare.

Soluţie. Pe primul nivel de design, începem printr-o funcţie imprumutare care are
doi parametri: titlul cărţii şi numele cititorului.

La următorul nivel, se adaugă noţiunea de entitate împrumut, care (probabil) are


două părţi: caută cartea cu titlul specificat, caută cititorul cu numele dat şi creează
o instanţă de împrumut pe baza ID-urilor cărţii şi cititorului.

Următoarea rafinare expandează fiecare parte de mai sus. gasesteCarte


returnează ISBN dacă e găsită şi e disponibilă cartea, returnează 0 dacă nu e
găsită şi returnează -1 dacă acea carte e dată. găsesteCititor returnează IDcititor
dacă cititorul e găsit, 0 dacă cititorul nu e găsit şi -1 dacă cititorul nu poate
împrumuta cartea. creeazaImprumut returnează 1 dacă e creată cu succes.
3.1 Atribute ale designului

Abstraction (abstractizare) — un obiect e abstract dacă detaliile


nenecesare sunt ascunse.
Cohesion (coeziune) — o procedură e coezivă dacă toate declaraţiile sale
sunt legate de fiecare dintre output-urile sale. O clasă e coezivă dacă toate
atributele din clasă sunt utilizate de fiecare metodă. Cu alte cuvinte,
coeziunea într-un modul se atinge atunci când toate elementele sale sunt
în legură unele cu altele.
Coupling (cuplare) — e o măsură a cât de interconectate sunt modulele
între ele. Două module sunt cuplate dacă modificarea unei variabile într-
unul dintre module necesită schimbări în celălalt modul. E de dorit o
cuplare slabă între module.

Exemplu. Evaluaţi abstractizarea în funcţionalitatea împrumutului din


sistemul de gestionare a împrumuturilor de cărţi dintr-o bibliotecă.
Soluţie. Funcţia imprumutare apare în 3 module: bibliotecă, cititor şi copiecarte.
Cea mai bună abstractizare se atinge atunci când funcţia imprumutare din modulul
biblioteca cunoaşte cât mai puţine detallii cu putinţă despre funcţiile imprumutare
din clasele cititor şi copiecarte.
Aşa cum se arată în figura de mai sus, dacă funcţia imprumutare din modulul
biblioteca doar apelează funcţia imprumutare, atunci există o bună abstractizare.

Dacă funcţia imprumutare din modulul biblioteca ştie despre clasa imprumut, poate
verifica disponibilitatea cărţii, poate crea instanţa imprumut şi poate apela funcţiile
imprumutare pentru a seta valorile instanţei imprumut.
Aşa cum se arată în figura de mai sus, abstractizarea nu e bună. De ce?
4. Măsurarea coeziunii
4.1 “Feliile” unui program (program slices)
Valorile unei variabile într-un program depind de valorile altor variabile.

Există două tipuri de dependenţe:


• data dependencies, în care valoarea lui x afectează valoarea lui y prin
definiţie;
• control dependencies, în care valoarea lui x determină dacă se execută
codul care conţine definiţiile lui y.

Exemplu. Înmulţirea prin însumare repetată: Să se calculeze x*y.


Valoarea de ieşire z are data dependency faţă de x, deoarece x se adună
la z şi are control dependency faţă de y, deoarece controlează de câte ori
de adună x la z.

z = 0;
while x > 0 do
z = z + y;
x = x –1;
end-while
Program slices pot fi obţinute din ambele direcţii. Output slices găsesc
fiecare instrucţiune care afectează valoarea unei ieşiri (output) specificate.
Input slices găsesc fiecare instrucţiune care e afectată de valoarea unui
input specificat.

Sunt uşor de determinat program slices, pornind de la un graf orientat, în


care are o mulţime de vârfuri n, fiecare vârf reprezentând un input, un
output sau o instrucţiune de cod. Arcurile e sunt dependenţele.

James Bieman şi Linda Ott au utilizat definiţii de variabile şi referinţe ca


unităţi de bază, în loc de instrucţiuni de program (program statements).
Aceste definiţii şi referinţe se numesc tokens. De aceea, orice referinţă de
constantă (constant reference), referinţă de variabilă (variable reference) şi
definiţie de variabilă reprezintă un token separat (James Bieman, Linda Ott,
‘‘Measuring Functional Cohesion,’’ IEEE TOSE, 20:8 August 1994)
Exemplu. Desenaţi un graf orientat pentru codul din exemplul precedent:
z = 0; while x > 0 do z = z + y; x = x –1;end-while

Soluţie. Vom folosi linie continuă pentru data dependencies şi linie punctată pentru
control dependencies.

Un input slice poate începe cu variabila de intrare x. Tokens x, 0, x, x, şi 1 din


instrucţiunile while x>0 şi x=x-1 se adaugă la slice. Apoi se adaugă tokens z, z şi y
din instrucţiunea z=z+y. Nici un alt token nu mai poate fi adăugat. De aceea, input
slice e tot mai puţin z=0.

Un input slice pentru variabila y va conţine tokenul iniţial y şi tokens z şi y din


instrucţiunea z=z+y.
4.2 Glue tokens

Bieman şi Ott au definit, de asemenea, anumite metrici de coeziune


utilizând output slices. Definiţiile sunt bazate pe glue tokens, care sunt
tokens (sectiună de cod) care se găsesc în mai mult de un slice şi
superglue tokens, care sunt în toate slices. Adezivitatea (adhesiveness)
unui token e procentul de output slices într-o procedură ce conţine token.

Există trei functional cohesion measures:


• Weak functional cohesion (WFC)—raportul glue tokens la total tokens
• Strong functional cohesion (SFC)—raportul superglue tokens la total
tokens
• Adhesiveness (A)—media adezivităţii tuturor tokens
Exemplu. Calculaţi functional cohesion measures pentru următorul fragment
de cod.

cin >> a >> b;


int x,y,z;
x=0; y=1; z=1;
if (a > b){
x = a*b;
while (10 > a){
y=y+z;
a=a+5;
}
else {
x=x+b;
}

Soluţie. În figura de pe pagina următoare din fiecare token către toate tokens care
sunt afectate imediat de valoarea token-ului.
Graf orientat arătând toate dependenţele
Nu sunt superglue tokens, aşadar strong functional cohesion (SFC) este
zero. Din cei 31 tokens, sunt 12 glue tokens, deci weak functional
cohesion este 12/31 sau 0.387.

Sunt patru slices. Zero tokens au adezivitate 100%. Patru tokens sunt în
trei slices, aşa încât au adezivitate 100%. Opt tokens sunt în două slices,
aşa încât au adezivitate 50%. Tokens rămaşi, 19, sunt doar într-un slice,
aşa încât au adezivitate 25%.

Adezivitatea este media adezivităţii tuturor tokens, deci

(4*0,75+8*0,50+19*0,25)/31=11,25/31=0,363
5. Măsurarea cuplării (coupling)
Dharma (H. Dharma, ‘‘Quantitative Models of Cohesion and Coupling in
Software,’’ Journal of Systems and Software, 29:4, April 1995.) a propus o
metrică cu următoarea listă de situaţii pentru numărare:
di = Numărul de parametri de date de intrare
ci = Numărul de parametri de control de intrare
do = Numărul de parametri de date de ieşire (output data parameters)
co = Numărul de parametri de control de ieşire (output control
parameters)
gd = Numărul de variabile globale folosite ca date
gc = Numărul de variabile globale folsoite ca control
w = Numărul de module apelate (fan-out)
r = Numărul de module apelante a modului fan-in)

Indicatorul de cuplare a modulelor (module coupling indicator) a lui


Dharma se defineşte astfel:
6. Requirements traceability (gradul de urmărire a
cerinţelor)
Requirements traceability încearcă să lege fiecare cerinţă (requirement) de
un element de design care să satisfacă cerinţa. Cerinţele trebuie să
influenţeze designul. Dacă o cerinţă nu are o parte corespondentă a
designului sau o parte a designului nu are o parte corespondentă în cerinţe,
există o potenţială problemă. Evident, anumite părţi de design pot fi atât de
generale, încât nici o parte a cerinţelor nu se reflectă în acea secţiune.
Unele cerinţe, pe de altă parte, s-ar putea să nu aibă părţi specifice în
design care să oglindească aceste cerinţe.

O abordare pentru determinarea gradului de urmărire a cerinţelor


(traceability) este să desenăm o matrice. Pe o axă sunt listate toţi itemii
despre cerinţe, iar pe cealaltă va fi lista cu itemii legaţi de design. Un semn
va fi plasat acolo unde un item de design îndeplineşte o cerinţă.
Exemplu. Desenaţi o matrice pentru următoarea problemă: Tom şi Sue
pregătesc o pensiune într-un oraş. [1] Ei vor avea 3 dormitoare pentru
oaspeţi. [2] Vor un sistem care să gestioneze [2.1] rezervările şi să
monitorizeze [2.2] cheltuielile şi profitul. Când un client potenţial sună pentru
rezervare [3], ei verifică [4] calendarul şi dacă sunt locuri libere [5], vor
introduce [6.1] numele clientului, [6.2] adresa, [6.3] telefonul, [6.4] datele,
[6.4] preţul negociat, [6.6] nr.credit card şi [6.7] nr.camerei. Rezervarea
trebuie [7] garantată prin plata [7.1] primei zile. Rezervările vor fi ţinute fără
garanţie [7.2] o perioadă stabilită. Dacă nu se oferă garanţia până la acea
zi, rezervarea va fi [7.3] anulată.
Aşa cum se arată în tabelele anterioare, există un număr de linii şi coloane
albe. Cerinţa 5 se leagă de locuri libere. Nu există o tratare explicită a
locurilor libere, deşi un loc liber ar trebui să fie absenţa unei rezervări într-o
zi anume. Cerinţa 6.3 se referă la telefonul clientului şi lipseşte. Cerinţa 6.5,
legată de preţul negociat şi lipseşte din informaţiile privind rezervarea.
Cerinţa 7.1 menţionează prima zi de plată, care de asemenea nu este
printre atribute.

Coloana A.1 e o listă a datelor, care e inclusă pentru a a juta căutarea


locurilor libere. B şi B.1 sunt necesare, dar nu sunt specifice pentru vreo
cerinţă. C.8 e un constructor. D.1-D.4 sunt detalii ale tranzacţiei, care sunt
neglijate la cerinţe.
Exemplu. Construiţi un model pentru o bibliotecă.
Soluţie. Obiectele sunt biblioteca, cărţi, copii ale cărţilor şi cititori. Între
biblioteca şi carte, ca şi între bibliotecă şi cititor există o relaţie de agregare.
Între carte şi copiecarte nu e nici agregare, nici moştenire, deoarece obiectul
carte reprezintă abstracţia unei cărţi, în timp de copiecarte este itemul concre
(fizic) care se împrumută.

inapoi

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