Documente Academic
Documente Profesional
Documente Cultură
Cursul 1
• sistemul de rezervare a biletelor pentru compania aeriană KLM conţinea, în anul 1992, două
milioane de linii de cod în limbaj de asamblare;
• sistemul de operare System V versiunea 4.0 (UNIX) a fost obţinut prin compilarea a
3.700.000 linii de cod;
• programele scrise pentru naveta spaţială NASA au circa 40 de milioane de linii de cod;
• pentru realizarea sistemului de operare IBM OS360 au fost necesari 5000 de ani-om.
performante materiale şi se vor angaja cei mai competenţi şi cu experienţă constructori. Eşecul nu
mai este o opţiune pentru contractantul proiectului.
Ştim că inginerii constructori întocmesc planuri, construiesc machete, studiază proprietăţile
materialelor folosite şi fac rapoarte privind progresul operaţiunilor. Construcţii de o complexitate
foarte mare au fost realizate în acest fel într-un mod raţional şi economic. Inginerii de programe ar
trebui să procedeze similar pentru ca dezvoltarea programelor să nu mai fie un proces impredictibil.
Pe măsură ce complexitatea programelor creştea, la sfârşitul anilor ’60 începea să se
prefigureze deja o criză a programării. Un raport prezentat de către o companie, în care erau
analizate câteva proiecte şi stadiile lor de finalizare, a constatat că:
Remarcăm că a doua definiţie este mai vagă decât prima, întrucât nu face referire la cost şi
la eficienţă. Mai mult, se pare că experienţa în general negativă acumulată a făcut să se renunţe la
formularea „principii inginereşti solide”, întrucât se pare că acestea nu pot fi identificate fără a fi
supuse contestaţiilor. A doua definiţie adaugă însă referiri la perioade importante din viaţa unui
program, ce urmează creării şi funcţionării, şi anume întreţinerea şi retragerea din funcţionare.
Considerăm că ingineria programării are următoarele caracteristici importante:
Programele mici se pot scrie relativ uşor, de către un singur programator, într-o perioadă
destul de scurtă de timp. Un program de 100 de instrucţiuni este cu siguranţă un program mic. Nu
putem identifica precis graniţa dintre un program mic şi unul mare, însă pe măsură ce dimensiunea
programului creşte, apar provocări noi, calitativ diferite.
Întrucât un singur sau câţiva programatori nu pot avea timpul fizic pentru terminarea
programului, este necesară crearea uneia sau mai multor echipe de lucru. Este necesară coordonarea
şi comunicarea între echipe. Complexitatea sistemului software şi a organizaţiei care realizează
sistemul software devine importantă, putând depăşi capacitatea de înţelegere a unui singur individ.
2
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Apare ca dezirabilă o abordare riguroasă a acestor probleme, ce include stilul de lucru, modul de
scriere a codului etc.
Nerespectarea cerinţelor poate avea efecte serioase. Un sistem de livrare a insulinei pentru
diabetici poate provoca moartea pacientului dacă nu funcţionează corect. Funcţionarea incorectă a
unui sistem de control al unui satelit poate provoca pagube de milioane de dolari.
Un program este fiabil dacă funcţionează şi continuă să funcţioneze fără întreruperi un
interval de timp. Această noţiune exprimă de fapt rezistenţa la condiţiile de funcţionare. Un sistem
de operare trebuie să fie fiabil pentru că trebuie să funcţioneze o perioadă suficient de lungă de timp
fără să cadă, indiferent de programele care rulează pe el, chiar dacă nu totdeauna la performanţe
optime.
Programul este sigur dacă funcţionează corect, fără operaţii nedorite. Un program pentru un
automat bancar trebuie să fie sigur, pentru a efectua tranzacţiile în mod absolut corect, chiar dacă
funcţionarea sa poate fi întreruptă din când în când. Atunci când funcţionează însă, trebuie să
funcţioneze foarte bine.
Programul este sigur dacă funcţionează corect, fără operaţii nedorite. Un automat bancar
trebuie să fie sigur, pentru a efectua tranzacţiile în mod absolut corect, chiar dacă funcţionarea sa
poate fi întreruptă din când în când. Atunci când funcţionează însă, trebuie să funcţioneze foarte
bine.
Un program are o eroare (engl. „bug”) dacă nu se comportă corect. Se presupune că
dezvoltatorul ştia ce ar fi trebuit programul să facă, iar comportamentul greşit nu este intenţionat.
Iată câteva erori celebre:
• În primii ani în care calculatoarele au fost introduse la staţiile de benzină din SUA,
consumatorii primeau cecuri pe sume enorme. Faptul era privit în general cu umor şi
reclamaţiile erau rezolvate repede;
• Sistemul de operare IBM OS360 conţinea aproximativ 1.000 de greşeli la fiecare nouă
versiune care încerca să rezolve greşelile din versiunea precedentă;
• Un vehicul de explorare a planetei Venus a fost pierdut deoarece programul primit de pe
Pământ pentru rectificarea orbitei conţinea linia 'DO 3 I = 1.3'; instrucţiunea corectă în
limbajul FORTRAN ar fi trebuit să conţină virgulă în loc de punct;
• În 1979 s-a descoperit o eroare în programele pentru sistemele de răcire în centralele
nucleare din SUA; din fericire, nu fusese niciodată nevoie de execuţia rutinelor ce conţineau
erorile;
• Din cauza unei erori în sistemul de avertizare împotriva atacului cu rachete balistice,
procedurile de contraatac au fost declanşate înainte de a se descoperi că a fost o eroare;
• Racheta Arianne 5 a explodat în iunie 1996 din cauza unei greşeli de programare; costurile
s-au ridicat la 500 milioane dolari.
Ingineria programării are ca scop obţinerea de sisteme funcţionale chiar şi atunci când
teoriile şi instrumentele disponibile nu oferă răspuns la toate provocările ce apar. Inginerii fac
lucrurile să meargă, ţinând seama de restricţiile organizaţiei în care lucrează şi de constrângerile
financiare.
Problema fundamentală a ingineriei programării este îndeplinirea cerinţelor clientului.
Aceasta trebuie realizată nu punctual, nu în acest moment, ci într-un mod flexibil şi pe termen lung.
Ingineria programării se ocupă cu toate etapele dezvoltării programelor, de la extragerea cerinţelor
de la client până la întreţinerea şi retragerea din folosinţă a produsului livrat. Pe lângă cerinţele
funcţionale, clientul doreşte (de obicei) ca produsul final să fie realizat cu costuri de producţie cât
mai mici. De asemenea, este de dorit ca aceasta să aibă performanţe cât mai bune (uneori direct
evaluabile), un cost de întreţinere cât mai mic, să fie livrat la timp, şi să fie sigur.
3
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
• posibilitatea de a putea fi întreţinut: un produs cu un lung ciclu de viaţă este supus deseori
modificărilor, de aceea el trebuie foarte bine documentat;
• fiabilitate: produsul trebuie să se comporte după cerinţele utilizatorului şi să nu „cadă” mai
mult decât e prevăzut în specificaţiile sale;
• eficienţă: produsul nu trebuie să folosească în pierdere resursele sistemului ca memoria sau
ciclii procesor;
• interfaţa potrivită pentru utilizator: interfaţa trebuie să ţină seama de capacitatea şi
cunoştinţele utilizatorului.
Optimizarea tuturor acestor atribute e dificilă deoarece unele se exclud pe altele (o mai bună
interfaţă pentru utilizator poate micşora eficienţa produsului). În cazurile în care eficienţa este
critică, acest lucru trebuie specificat explicit încă din faza de preluare a cerinţelor utilizatorului,
precum şi compromisurile pe care ea le implică privind ceilalţi factori.
Trebuie spus că ingineria programării nu rezolvă toate problemele care apar atunci când se
scriu programe. Ingineria programării nu oferă nici teorii. Inginerii fac lucrurile să meargă. Totuşi,
în momentul de faţă, ingineria programării ne poate spune sigur ce să nu facem.
Deşi aceste faze se referă în mod special la ciclul de viaţă al produsului software, ele pot fi
aplicate şi altor stadii de existenţă prin care trece un program de la „naştere” până la „moarte”:
lansare, întreţinere, ieşire din uz.
Această fază defineşte cerinţele sistemului, independent de modul în care acestea vor fi
îndeplinite. Aici se defineşte problema pe care clientul doreşte să o rezolve. Rezultatul acestei faze
este documentul cerinţelor, care trebuie să precizeze clar ce trebuie construit.
Documentul încearcă să redea cerinţele din perspectiva clientului, definind scopurile şi
interacţiunile la un nivel descriptiv înalt, independent de detaliile de implementare, cum ar fi, de
exemplu: formularea problemei, aşteptările clientului sau criteriile pe care trebuie să le
îndeplinească produsul.
Graniţa dintre descrierile de nivel înalt şi cele de nivel scăzut nu este foarte bine trasată.
Uneori, dacă un detaliu tehnic important trebuie specificat, el va apărea în document. Totuşi,
aceasta trebuie să fie excepţia şi nu regula. Aceste excepţii pot fi determinate de necesitatea
menţinerii compatibilităţii cu alte sisteme deja existente, sau a unor anumite opţiuni dorite de client,
de exemplu utilizarea unui anumit standard sau o constrângere asupra dimensiunilor imaginii
aplicaţiei, care poate fi destinată unei categorii speciale de utilizatori sau care va rula pe nişte
sisteme cu o serie de particularităţi (monitoare care nu suportă rezoluţii mari).
4
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Faza de analiză poate fi văzută ca o rafinare a detaliilor. Distincţia dintre detaliile de nivel
înalt şi cele de nivel scăzut sunt puse mai bine în evidenţă de abordările top-down (unde se merge
către detaliile de nivel scăzut) şi bottom-up (care tind către detaliile de nivel înalt).
Documentul cerinţelor poate fi realizat într-o manieră formală, bazată pe logică matematică,
sau poate fi exprimat în limbaj natural. În mod tradiţional, el descrie obiectele din sistem şi acţiunile
care pot fi realizate cu ajutorul obiectelor. Aici noţiunea de „obiect” nu trebuie confundată cu
obiectul din programarea orientată obiect. Descrierea obiectelor şi acţiunilor trebuie să fie generală
şi să nu depindă de o anumită tehnologie. Desigur, într-o abordare POO, descrierile vor lua forma
obiectelor şi metodelor, însă în alte abordări, obiectele pot fi de exemplu servicii care accesează
baze de date.
În general, documentul cerinţelor descrie ontologia proiectului, adică vocabularul de cuvinte
cheie (în special construcţii substantivale şi verbale) care va fi utilizat pentru definirea protocolului
specific aplicaţiei. Descrierile acestea nu implică proiectarea arhitecturii aplicaţiei, ci enumerarea
părţilor componente şi a modului în care acestea se comportă. Mai târziu, în faza de proiectare,
acestea vor fi transformate în primitive informatice, precum liste, stive, arbori, grafuri, algoritmi şi
structuri de date.
Mai concret, documentul trebuie să conţină descrieri pentru următoarele categorii:
• Obiecte: Documentul trebuie să definească mai întâi ontologia sistemului, care este bazată
în mare parte pe construcţii substantivale pentru identificarea pieselor, părţilor componente,
constantelor, numelor şi a relaţiilor dintre acestea;
• Acţiuni: Documentul trebuie să definească de asemenea acţiunile pe care trebuie să le
îndeplinească sistemul şi care sunt sugerate în general de construcţii verbale. Exemple de
acţiuni sunt: metodele, funcţiile sau procedurile;
• Stări: Sunt definite ca mulţimi de setări şi valori care disting sistemul între două ipostaze
spaţio-temporale. Fiecare sistem trece printr-o serie de schimbări de stare. Exemple de stări
sunt: starea iniţială, cea finală sau stările de eroare. Cele mai multe stări depind de domeniul
problemei. Stările sunt asociate cu obiectele sistemului. Un eveniment declanşează o
tranziţie de stare care poate conduce la îndeplinirea unei acţiuni de către sistem;
• Scenarii tipice: Un scenariu este o secvenţă de paşi urmaţi pentru îndeplinirea unui scop.
Când sistemul este terminat şi aplicaţia este disponibilă, clientul trebuie să poată utiliza,
într-o manieră cât mai facilă şi clar specificată, toate scenariile tipice ale aplicaţiei.
Scenariile tipice trebuie să reprezinte majoritatea scenariilor de utilizare ale aplicaţiei.
Ponderea acestora variază de la un sistem la altul, dar 90% se consideră o proporţie
acceptabilă. Bineînţeles că un sistem cu un singur scenariu de utilizare este relativ simplu de
obţinut, pe când unul cu mii de scenarii posibile va fi mult mai dificil de analizat. Deseori
este invocată regula 80/20: 80% din funcţionalitatea sistemului se realizează cu 20% din
efortul de muncă. Executarea restului minoritar de funcţionalitate necesită marea majoritate
a timpului de lucru;
• Scenarii atipice: Un scenariu atipic trebuie să fie îndeplinit de sistem numai în cazuri
speciale. Clientul poate să spere, de exemplu, că o eroare neprevăzută este un eveniment
atipic. Totuşi, sistemul trebuie să gestioneze un număr cât mai mare de categorii de erori,
prin tehnici stabilite, precum tratarea excepţiilor, monitorizarea proceselor etc.;
• Cerinţe incomplete sau nemonotone: O enumerare completă a cerinţelor pentru toate
situaţiile care pot apărea în condiţii de lucru reale nu este posibilă. În logica tradiţională, o
teorie este definită de o mulţime finită de axiome. Teoremele din teoria respectivă sunt
propoziţii adevărate. Dacă se adaugă ulterior noi axiome, teoremele existente rămân valide
iar noile teoreme dezvoltate sunt adăugate teoremelor stabilite. În logica nemonotonă,
adăugarea de noi axiome poate invalida unele teoreme care au fost demonstrate anterior. O
nouă teorie nu mai este o simplă extensie a teoriei vechi, ci o mulţime de teoreme noi,
împreună cu o parte din teoremele vechi. Procesul de stabilire a cerinţelor are o natură
5
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
• Componentele sunt elementele constructive ale produsului. Acestea pot fi create de la zero
sau reutilizate dintr-o bibliotecă de componente. Componentele rafinează şi capturează
semnificaţia detaliilor din documentul cerinţelor;
• Interfeţele ajută la îmbinarea componentelor. O interfaţă reprezintă graniţa dintre două
componente, utilizată pentru comunicarea dintre acestea. Prin intermediul interfeţei,
componentele pot interacţiona;
• Comportamentul, determinat de interfaţă, reprezintă răspunsul unei componente la stimulii
acţiunilor altor componente.
6
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
patch-urile pentru eventualele erori erau imposibile. Astfel, au fost necesare teste cu acoperire de
100%. Codul propriu-zis al BIOS-ului era destul de mic, câteva mii de linii. Însă deoarece BIOS-ul
are o natură asincronă, testul a presupus mai întâi crearea unui mediu asincron care să aducă
sistemul în starea dorită şi apoi trebuia generat un eveniment care să declanşeze un test. Foarte
repede, setul de test a devenit mult mai mare decât BIOS-ul. A apărut astfel problema testării însuşi
a mediului de test! În final, o acoperire de 100% a fost atinsă, dar cu un cost foarte ridicat. O soluţie
mai ieftină a fost înscrierea BIOS-ului într-o combinaţie dintre EPROM (Electronic Programmable
Read Only Memory) şi ROM. Cea mai mare parte a produsului era plasat în ROM, iar patch-urile
erau plasate în EPROM. Aceasta a fost abordarea adoptată de Apple pentru Macintosh.
În general, este suficient ca testele să cuprindă scenariile tipice şi atipice, fără să verifice
întregul sistem, cu absolut toate firele de execuţie. Acesta poate conţine ramificaţii interne, erori sau
întreruperi care conduc la fire de execuţie netestate. Majoritatea sistemelor sunt pline de bug-uri
nedescoperite. De obicei, clientul participă în mod logic la testarea sistemului şi semnalează erori
care vor fi îndepărtate în versiunile ulterioare.
În această fază, sistemul este construit, ori plecând de la zero, ori prin asamblarea unor
componente pre-existente. Pe baza documentelor din fazele anterioare, echipa de dezvoltare ar
trebui să ştie exact ce trebuie să construiască, chiar dacă rămâne loc pentru inovaţii şi flexibilitate.
De exemplu, o componentă poate fi proiectată mai restrâns, special pentru un anumit sistem, sau
mai general, pentru a satisface o direcţie de reutilizare.
Echipa trebuie să gestioneze problemele legate de calitate, performanţă, biblioteci şi debug.
Scopul este producerea sistemului propriu-zis. O problemă importantă este îndepărtarea erorilor
critice. Într-un sistem există trei tipuri de erori:
• Erori critice: Împiedică sistemul să satisfacă în mod complet scenariile de utilizare. Aceste
erori trebuie corectate înainte ca sistemul să fie predat clientului şi chiar înainte ca procesul
de dezvoltare ulterioară a produsului să poată continua;
• Erori necritice: Sunt cunoscute, dar prezenţa lor nu afectează în mod semnificativ calitatea
observată a sistemului. De obicei aceste erori sunt listate în notele de lansare şi au modalităţi
de ocolire bine cunoscute;
• Erori necunoscute: Există întotdeauna o probabilitate mare ca sistemul să conţină un număr
de erori nedescoperite încă. Efectele acestor erori sunt necunoscute. Unele se pot dovedi
critice, altele pot fi rezolvate cu patch-uri sau eliminate în versiuni ulterioare.
Calitatea produsului software este foarte importantă. Multe companii nu au învăţat însă acest
lucru şi produc sisteme cu funcţionalitate extinsă, dar cu o calitate scăzută. Totuşi, e mai simplu să-i
explici clientului de ce lipseşte o anumită funcţie decât să-i explici de ce produsul nu este
performant. Un client satisfăcut de calitatea produsului va rămâne loial firmei şi va aştepta noile
funcţii în versiunile următoare.
În multe metodologii ale ingineriei programării, faza de testare este o fază separată, realizată
de o echipă diferită după ce implementarea s-a terminat. Motivul este faptul că un programator
nu-şi poate descoperi foarte uşor propriile greşeli. O persoană nouă care priveşte codul poate
descoperi greşeli evidente care scapă celui care citeşte şi reciteşte materialul de multe ori. Din
păcate, această practică poate determina o atitudine indiferentă faţă de calitate în echipa de
implementare.
Tehnicile de testare sunt abordate preponderent din perspectiva producătorului sistemului. În
mod ideal, şi clientul trebuie să joace un rol important în această fază.
7
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Testele de regresiune (engl. „regression test”) sunt colecţii de programe care testează una
sau mai multe trăsături ale sistemului. Rezultatele testelor sunt adunate şi dacă există erori, bug-ul
este corectat. Un test de regresiune valid generează rezultate verificate, numite „standardul de aur”.
Validitatea rezultatului unui test ar trebui să fie determinată de documentul cerinţelor. În practică,
echipa de implementare este responsabilă de interpretarea validităţii.
Testele sunt colectate, împreună cu rezultatele standardelor de aur, într-un pachet de test de
regresiune. Pe măsură ce dezvoltarea continuă, sunt adăugate mai multe teste noi, iar testele vechi
pot rămâne valide sau nu. Dacă un test vechi nu mai este valid, rezultatele sale sunt modificate în
standardul de aur, pentru a se potrivi aşteptărilor curente. Pachetul de test este rulat din nou şi
generează noi rezultate. Acestea sunt comparate cu rezultatele standardelor de aur. Dacă sunt
diferite, în sistem a apărut o greşeală. Greşeala este corectată şi dezvoltarea continuă. Acest
mecanism detectează situaţiile când starea curentă de dezvoltare a produsului invalidează o stare
existentă. Astfel, se previne regresiunea sistemului într-o stare de eroare anterioară.
Există patru puncte de interes în testele de regresiune pentru asigurarea calităţii.
Testarea internă tratează implementarea de nivel scăzut. Fiecare funcţie sau componentă
este testată de către echipa de implementare. Aceste teste se mai numesc teste „clear-box” sau
„white-box”, deoarece toate detaliile sunt vizibile pentru test.
Testarea unităţilor testează o unitate ca un întreg. Aici se testează interacţiunea mai multor
funcţii, dar numai în cadrul unei singure unităţi. Testarea este determinată de arhitectură. De multe
ori sunt necesare aşa-numitele „schele”, adică programe special construite pentru stabilirea mediului
de test. Numai când mediul este realizat se poate executa o evaluare corectă. Programul schelă
stabileşte stări şi valori pentru structurile de date şi asigură funcţii externe fictive. De obicei,
programul schelă nu are aceeaşi calitate ca produsul software testat şi adesea este destul de fragil. O
schimbare mică în test poate determina schimbări importante în programul schelă. Aceste teste se
mai numesc teste „black-box” deoarece numai detaliile interfeţei sunt vizibile pentru test.
Testarea internă şi a unităţilor poate fi automatizată cu ajutorul instrumentelor de acoperire
(engl. „coverage tools”), care analizează codul sursă şi generează un test pentru fiecare alternativă a
firelor execuţie. Depinde de programator combinarea acestor teste în cazuri semnificative care să
valideze rezultatelor fiecărui fir de execuţie. De obicei, instrumentul de acoperire este utilizat
într-un mod oarecum diferit: el urmăreşte liniile de cod executate într-un test şi apoi raportează
procentul din cod executat în cadrul testului. Dacă acoperirea este mare şi liniile sursă netestate nu
prezintă mare importanţă pentru calitatea generală a sistemului, atunci nu mai sunt necesare teste
suplimentare.
Testarea aplicaţiei testează aplicaţia ca întreg şi este determinată de scenariile echipei de
analiză. Aplicaţia trebuie să execute cu succes toate scenariile pentru a putea fi pusă la dispoziţia
clientului. Spre deosebire de testarea internă şi a unităţilor, care se face prin program, testarea
aplicaţiei se face de obicei cu scripturi care rulează sistemul cu o serie de parametri şi colectează
rezultatele. În trecut, aceste scripturi erau create manual. În prezent, există instrumente care
automatizează şi acest proces. Majoritatea aplicaţiilor din zilele noastre au interfeţe grafice (GUI).
Testarea interfeţei grafice pentru asigurarea calităţii poate pune anumite probleme. Cele mai multe
interfeţe, dacă nu chiar toate, au bucle de evenimente, care conţin cozi de mesaje de la mouse,
tastatură, ferestre etc. Asociate cu fiecare eveniment sunt coordonatele ecran. Testarea interfeţei
presupune deci memorarea tuturor acestor informaţii şi elaborarea unei modalităţi prin care
mesajele să fie trimise din nou aplicaţiei, la un moment ulterior.
Testarea la stres determină calitatea aplicaţiei în mediul său de execuţie. Ideea este crearea
unui mediu mai solicitant decât cel în care aplicaţia va rula în mod obişnuit. Aceasta este cea mai
dificilă şi complexă categorie de teste. Sistemul este supus unor cerinţe din ce în ce mai numeroase,
până când acesta cade. Apoi produsul este reparat şi testul de stres se repetă până când se atinge un
nivel de stres mai ridicat decât nivelul aşteptat de pe staţia clientului. Deseori apar aici conflicte
între teste. Fiecare test funcţionează corect atunci când este făcut separat. Când două teste sunt
rulate în paralel, unul sau ambele teste pot eşua. Cauza este de obicei managementul incorect al
8
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
accesului la resurse critice. Mai apar şi probleme de memorie, când un test îşi alocă memorie şi apoi
nu o mai dezalocă. Testul pare să funcţioneze corect, însă după ce este rulat de mai multe ori,
memoria disponibilă se reduce iar sistemul cade.
3. Concluzii
În acest curs s-a făcut mai întâi o introducere în problematica domeniului ingineriei
programării, insistându-se pe cauzele care au determinat dezvoltarea sa: creşterea continuă a
complexităţii sistemelor software. Apoi s-au descris cele patru faze fundamentale ale metodologiilor
ingineriei programării: analiza, proiectarea, implementarea şi testarea, necesare pentru realizarea
unor sisteme de calitate.
9
Ingineria programării
Cursurile 2-3
• Analiza cerinţelor: Se stabileşte ce anume vrea clientul ca programul să facă. Scopul este
înregistrarea cerinţelor într-o manieră cât mai clară şi mai fidelă. Claritatea se referă la lipsa
ambiguităţii iar fidelitatea la înregistrarea cât mai exactă (posibil cuvânt cu cuvânt);
• Proiectarea arhitecturală: Din motive de complexitate, programele mari nu pot fi concepute
şi implementate ca o singură bucată. Programul va trebui construit aşadar din module sau
componente. Proiectarea arhitecturală împarte sistemul într-un număr de module mai mici şi
mai simple, care pot fi abordate individual;
• Proiectarea detaliată: Se realizează proiectarea fiecărui modul al aplicaţiei, în cele mai mici
detalii;
• Scrierea codului: Proiectul detaliat este transpus într-un limbaj de programare. De obicei,
aceasta se realizează modular, pe structura rezultată la proiectarea arhitecturală;
• Integrarea componentelor: Modulele programului sunt combinate în produsul final.
Rezultatul este sistemul complet. În modelul numit big-bang componentele sunt dezvoltate
şi testate individual, după care sunt integrate în sistemul final. Având în vedere că
funcţionarea corectă a componentelor individuale a fost testată, integrarea ar trebui să fie o
formalitate. Din păcate, componentele nu pot fi testate exhaustiv, iar când acestea lucrează
împreună pot să apară situaţii pe care o anumită componentă nu le-a întâlnit în procesul de
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
testare sau conflicte între anumite componente (de exemplu, conflicte de partajare a
resurselor). S-a constatat că atunci când se aplică acest model, timpul de testare explodează,
proiectul devenind greu de controlat; aceasta justifică denumirea de „big-bang”. Modelul
incremental propune crearea unui nucleu al aplicaţiei şi integrarea a câte o componentă la un
moment dat, urmată imediat de testarea sistemului obţinut. Astfel, se poate determina mai
uşor unde anume apare o problema în sistem. Acest tip de integrare oferă de obicei rezultate
mai bune decât modelul big-bang;
• Validarea: În procesul de validare ne asigurăm că programul îndeplineşte cerinţele
utilizatorului. Întrebarea la care răspundem este: construim produsul corect? Un exemplu de
validare este testul de acceptare, în care produsul este prezentat clientului. Clientul spune
dacă este mulţumit cu produsul sau dacă mai trebuie efectuate modificări;
• Verificarea: În procesul de verificare ne asigurăm că programul este stabil şi că
funcţionează corect din punctul de vedere al dezvoltatorilor. Întrebarea la care răspundem
este: construim corect produsul?
• Întreţinerea: După ce programul este livrat clientului, mai devreme sau mai târziu sunt
descoperite defecte sau erori ce trebuie reparate. De asemenea, pot apărea schimbări în
specificaţiile utilizatorilor, care vor diverse îmbunătăţiri. Întreţinerea constă în gestionarea
acestor probleme.
Se poate constata uşor că aceste activităţi sunt în strânsă legătură cu cele patru faze ale
ingineriei programării: analiza, proiectarea, implementarea şi testarea.
2. Metodologii generice
În acest paragraf, vor fi prezentate trei categorii importante de metodologii: secvenţială,
ciclică şi hibridă. În metodologia secvenţială (cascadă), cele patru faze urmează una alteia într-o
modalitate serială. În metodologia ciclică (spirală), fazele sunt dispuse în cicluri care îşi generează
incremental contribuţiile la sistemul final. Metodologia hibridă (ecluză) combină progresul constant
al metodologiei secvenţiale cu incrementele iterative ale metodologiei ciclice.
În metodologia secvenţială, cunoscută şi sub numele de metodologia „cascadă”, are loc mai
întâi faza de analiză, apoi cea de proiectare, urmată de cea de implementare, iar în final se
realizează testarea. Echipele care se ocupă de fiecare fază pot fi diferite, iar la fiecare tranziţie de
fază poate fi necesară o decizie managerială.
Avantaje
Metodologia secvenţială este potrivită când complexitatea sistemului este mică iar cerinţele
sunt statice. Ea spune că mai întâi trebuie să ne gândim ce trebuie construit, apoi să stabilim un plan
de lucru şi apoi să realizăm proiectul, ţinând cont de standardele de calitate. De asemenea, se
aliniază metodelor de inginerie hardware. Forţează menţinerea unei discipline de lucru care evită
presiunea scrierii codului înainte de a cunoaşte precis ce produs va trebui de fapt construit.
2
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Dezavantaje
Unul din principalele dezavantaje ale metodologiei secvenţiale este faptul că acordă o foarte
mare importanţă fazei de analiză. Membrii echipei de analiză ar trebui să fie probabil clarvăzători ca
să poată defini toate detaliile aplicaţiei încă de la început. Greşelile nu sunt permise, deoarece nu
există un proces de corectare a erorilor după lansarea cerinţelor finale. Nu există nici feedback de la
echipa de implementare în ceea ce priveşte complexitatea codului corespunzător unei anumite
cerinţe. O cerinţă simplu de formulat poate creşte considerabil complexitatea implementării. În
unele cazuri, este posibil să fie chiar imposibil de implementat cu tehnologia actuală. Dacă echipa
de analiză ar şti că o cerinţă nu poate fi implementată, ei ar putea-o schimba cu o cerinţă diferită
care să satisfacă cele mai multe dintre necesităţi şi care să fie mai uşor de efectuat.
Comunicarea dintre echipe este o problemă: cele patru echipe pot fi diferite iar comunicarea
dintre ele este limitată. Modul principal de comunicare sunt documentele realizate de o echipă şi
trimise următoarei echipe cu foarte puţin feedback. Echipa de analiză nu poate avea toate
informaţiile privitoare la calitate, performanţă şi motivare.
Într-o industrie în continuă mişcare, metodologia secvenţială poate produce sisteme care, la
vremea lansării, să fie deja învechite. Accentul atât de mare pus pe planificare nu poate determina
răspunsuri suficient de rapide la schimbare. Ce se întâmplă dacă clientul îşi schimbă cerinţele după
terminarea fazei de analiză? Acest lucru se întâmplă însă frecvent; după ce clientul vede prototipul
produsului, el îşi poate schimba unele cerinţe.
3
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Avantaje
Dezavantaje
Metodologia ecluză (engl. „watersluice”), propusă de Ronald LeRoi Burback (1998), separă
aspectele cele mai importante ale procesului de dezvoltare a unui produs software de detaliile mai
puţin semnificative şi se concentrează pe rezolvarea primelor. Pe măsură ce procesul continuă,
detaliile din ce în ce mai fine sunt rafinate, până când produsul poate fi lansat. Această metodologie
hibridă preia natura iterativă a metodologiei spirală, la care adaugă progresul sigur al metodologiei
cascadă.
4
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
La început, într-un proces iterativ, fazele de analiză, proiectare, implementare şi testare sunt
împărţite în mai multe sarcini potenţiale, fiecăruia atribuindu-i-se o prioritate care reflectă
beneficiul îndeplinirii sarcinii respective pentru scopul final. La fiecare moment se execută sarcina
cu prioritate maximă. În funcţie de dimensiunea echipelor, mai multe sarcini pot fi realizate în
paralel. Sarcinile rămase, de prioritate minimă, sunt păstrate pentru examinare ulterioară.
Descompunerea problemei este foarte importantă. Cu cât descompunerea şi stabilirea priorităţilor
sunt mai bune, cu atât mai eficientă este metodologia.
Pe măsură ce sarcinile stabilite sunt îndeplinite, noi sarcini pot fi descoperite. Acestea sunt
adăugate sarcinilor rămase nesoluţionate şi se reatribuie priorităţile. Procesul continuă până când
produsul este gata de lansare.
Priorităţile se stabilesc pe baza unei funcţii de prioritate, care depinde atât de domeniul
problemei şi de normele firmei. Ea trebuie să realizeze un compromis între cantitate şi calitate, între
funcţionalitate şi constrângerile privind resursele, între aşteptări şi realitate. Toate funcţiile de
prioritate ar trebuie să aibă ca prim scop lansarea produsului.
Pe lângă rolul evident de a stabili priorităţile şi deci ordinea de execuţie a sarcinilor de lucru,
funcţia mai trebuie să gestioneze sarcinile conflictuale şi nemonotone. Ea trebuie să împartă aceste
sarcini în grupuri consistente, să reglementeze selecţia grupurilor consistente şi apoi să dirijeze
selecţia sarcinilor din cadrul grupurilor. Pe măsură ce sistemul creşte, funcţia de prioritate trebuie să
aleagă sarcini consistente cu partea deja constituită din sistem. O sarcină nemonotonă vine în
contradicţie cu sistemul realizat deja şi trebuie eliminată dacă nu este absolut necesară pentru
succesul sistemului.
Odată ce o componentă este terminată şi acceptată de echipă, schimbările asupra sa sunt
îngheţate. Componenta va fi schimbată numai dacă modificările sunt absolut necesare iar echipa
este dispusă să întârzie lucrul la restul sistemului pentru a le efectua. Schimbările trebuie să fie
puţine la număr, bine justificate şi documentate.
Etapele principale ale metodei sunt: schiţa de principiu, prototipul, versiunile alfa/beta şi
produsul final.
În prima etapă, schiţa de principiu, echipele lucrează simultan la toate fazele problemei.
Echipa de analiză sugerează cerinţele. Echipa de proiectare le discută şi trimite sarcinile critice de
implementare echipei de implementare. Echipa de testare pregăteşte şi dezvoltă mediul de test în
funcţie de cerinţe. Echipa de implementare se concentrează asupra sarcinilor critice, care în general
sunt cele mai dificile. Această abordare contrastează cu practica curentă de realizare mai întâi a
sarcinilor simple. Totuşi, majoritatea produselor care urmează acest model eşuează. Odată ce
componentele critice au fost realizate, sistemul este gata de a face tranziţia către stadiul de prototip.
Unul din scopurile aceste etape este de a se convinge echipele că o soluţie poate fi găsită şi pusă în
practică.
În cea de a doua etapă, de prototip, cerinţele şi documentul cerinţelor sunt îngheţate.
Schimbările în cerinţe sunt încă permise, însă ar trebuie să fie foarte rare şi numai dacă sunt absolut
necesare, deoarece modificările cerinţelor în acest stadiu al proiectului sunt foarte costisitoare. Este
posibilă totuşi ajustarea arhitecturii, pe baza noilor opţiuni datorate tehnologiei. După ce sarcinile
critice au fost terminate, echipa de implementare se poate concentra pe extinderea acestora, pentru
definirea cât mai multor aspecte ale aplicaţiei. Unul din scopurile acestei etape este de a convinge
persoanele din afara echipelor că o soluţie este posibilă.
Acum produsul este gata pentru lansarea versiunilor alfa şi beta. Arhitectura este îngheţată,
iar accentul cade pe implementare şi asigurarea calităţii. Prima versiune lansată se numeşte în
general alfa. Produsul este încă imatur; numai sarcinile critice au fost implementate la calitate
ridicată. Numai un număr mic de clienţi sunt în general dispuşi să accepte o versiune alfa şi să-şi
asume riscurile asociate. O a doua lansare reprezintă versiunea beta. Rolul său este de a convinge
clienţii că aplicaţia va fi un produs adevărat şi de aceea se adresează unui număr mai mare de
clienţi.
5
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Când o parte suficient de mare din sistem a fost construită, poate fi lansat în sfârşit produsul.
În această etapă, implementarea este îngheţată şi accentul cade pe asigurarea calităţii. Scopul este
realizarea unui produs competitiv. În produsul final nu se acceptă erori critice.
Avantaje
Metodologia ecluză recunoaşte faptul că oamenii fac greşeli şi că nici o decizie nu trebuie să
fie absolută. Echipele nu sunt blocate într-o serie de cerinţe sau într-o arhitectură imobilă care se pot
dovedi mai târziu inadecvate sau chiar greşite. Totuşi, pentru respectarea termenelor limită,
metodologia impune date de îngheţare a unor faze. Există timp suficient pentru corectarea greşelilor
decizionale pentru atingerea unui nivel suficient de ridicat de încredere. Se pune mare accent pe
comunicarea între echipe, ceea ce reduce cantitatea de cod inutil la care ar trebui să se renunţe în
mod normal. Metodologia încearcă să mute toate erorile la începutul procesului, unde corectarea,
sau chiar reînceperea de la zero a lucrului, nu sunt foarte costisitoare.
Dezavantaje
3. Metodologii concrete
3.1. Metodologia cascadă
Metodologia cascadă, propusă de Barry Boehm, este una din cele mai cunoscute exemple de
metodologie de ingineria programării. Există numeroase variante ale acestui proces. Într-o variantă
detaliată, metodologia cascadă cuprinde următoarele etape:
6
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
După fiecare etapă există un pas de validare. Procesul „curge” de la etapă la etapă, ca apa
într-o cascadă. În descrierea originară a lui Boehm, există o întoarcere, un pas înapoi interactiv între
fiecare două etape. Astfel, metoda cascadă este de fapt o combinaţie de metodologie secvenţială cu
elemente ciclice. Totuşi, în practica inginerească, termenul „cascadă” este utilizat ca un nume
generic pentru orice metodologie secvenţială.
Acesta este modelul după care de obicei sistemele sunt dezvoltate în practică. De asemenea,
reordonarea fazelor s-a dovedit a fi sub-optimală. Există o mare atracţie pentru acest model datorită
experienţei, tradiţiei în aplicarea sa şi succesului pe care l-a implicat. O sarcină complexă este
împărţită în mai mulţi paşi mici, ce sunt mai uşor de administrat. Fiecare pas are ca rezultat un
produs bine definit (documente de specificaţie, model, etc.)
Modelul cascadă cu feedback propune remedierea problemelor descoperite în pasul
precedent. Problemele la pasul i care sunt descoperite la pasul i + 3 rămân neremediabile. Un model
realist ar trebui să ofere posibilitatea ca de la un anumit nivel să se poată reveni la oricare dintre
nivelele anterioare.
Dezavantajul principal al modelului în cascadă apare deoarece clientul obţine o viziune
practică asupra produsului doar în momentul terminării procesului de dezvoltare. De asemenea,
modelul nu are suficientă putere descriptivă, în sensul că nu integrează activităţi ca managementul
resurselor sau managementul configuraţiei. Aceasta face dificilă coordonarea proiectului.
După cum am menţionat la prezentarea metodologiei generice secvenţiale, şi modelul
cascadă impune îngheţarea specificaţiilor foarte devreme în procesul de dezvoltare pentru a evita
iteraţiile frecvente (reîntoarcerile în fazele anterioare atunci când în faza curentă s-au detectat erori:
în timpul analizei se descoperă erori de specificaţii, în timpul implementării se descoperă erori de
specificaţii/proiectare etc., astfel încât procesul poate implica multiple secvenţe de iteraţii ale
activităţilor de dezvoltare). Îngheţarea prematură a cerinţelor conduce la obţinerea unui produs prost
structurat şi care nu execută ceea ce doreşte utilizatorul. Conduce de asemenea la obţinerea unei
7
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Metodologia spirală, propusă tot de Boehm, este un alt exemplu bine cunoscut de
metodologie a ingineriei programării. Acest model încearcă să rezolve problemele modelului în
cascadă, păstrând avantajele acestuia: planificare, faze bine definite, produse intermediare. El
defineşte următorii paşi în dezvoltarea unui produs:
• studiul de fezabilitate;
• analiza cerinţelor;
• proiectarea arhitecturii software;
• implementarea.
• în timpul unui proces îndelungat de dezvoltare, cerinţele noi ale clientului sunt ignorate;
• clientul schimbă cerinţele;
• o firmă concurentă lansează un program rival pe piaţă;
• un dezvoltator/arhitect părăseşte echipa de dezvoltare;
• o echipă nu respectă un termen de livrare pentru o anumită componentă.
În modelul spirală se consideră că fiecare pas din dezvoltare conţine o serie de activităţi
comune:
8
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Ciclul 3 – Proiectarea:
12. Simulare, modele, benchmark-uri
13. Proiectarea produsului software, validare şi verificare
14. Integrare şi plan de test
15. Obiective, alternative, constrângeri
16. Analiza riscului şi prototipul operaţional
Procesul începe în centrul spiralei. Fiecare ciclu terminat reprezintă o etapă. Pe măsură ce
spirala este parcursă, produsul se maturizează. Cu fiecare ciclu, sistemul se apropie de soluţia finală.
Deşi este considerată ca un exemplu generic pentru metodologia ciclică, metoda are şi elemente
secvenţiale, puse în evidenţă de evoluţia constantă de la o etapă la alta.
9
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Această metodologie extinde spirala Boehm prin adăugarea unui pas de stabilire a priorităţii
la începutul fiecărui ciclu din spirală şi prin introducerea unor scopuri intermediare, numite puncte
ancoră. Procesul WinWin identifică un punct de decizie. Pentru fiecare punct de decizie, se
stabilesc obiectivele, constrângerile şi alternativele.
Punctele ancoră stabilesc trei scopuri intermediare. Primul punct ancoră, numit obiectivul
ciclului de viaţă, precizează cazurile sigure de funcţionare pentru întregul sistem, arătând că există
cel puţin o arhitectură fezabilă (adică posibilă din punct de vedere practic) care satisface scopurile
sistemului. Primul scop intermediar este stabilit când sunt terminate obiectivele de nivel înalt ale
sistemului, arhitectura, modelul ciclului de viaţă şi prototipul sistemului. Această primă ancoră
spune de ce, ce, când, cine, unde, cum şi estimează costul produsului. După executarea acestor
operaţii, este disponibilă analiza de nivel înalt a sistemului.
Al doilea punct ancoră defineşte arhitectura ciclului de viaţă, iar al treilea – capacitatea
operaţională iniţială, incluzând mediul software necesar, hardware-ul, documentaţia pentru client şi
instruirea acestuia.
Aceste puncte ancoră corespund etapelor majore din ciclul de viaţă al unui produs:
dezvoltarea iniţială, lansarea, funcţionarea, întreţinerea şi ieşirea din funcţiune.
3.4. Prototipizarea
• de aruncat (throw-away);
• evoluţionar.
În cazul realizării unui prototip de aruncat, scopul este exclusiv obţinerea unei specificaţii.
De aceea nu se acordă nici o importanţă stilului de programare şi de lucru, punându-se accent pe
viteza de dezvoltare. Odată stabilite cerinţele, codul prototipului este „aruncat”, sistemul final fiind
rescris de la început, chiar în alt limbaj de programare.
În cazul realizării unui prototip evoluţionar, scopul este de a crea un schelet al aplicaţiei care
să poată implementa în primă fază o parte a cerinţelor sistemului. Pe măsură ce aplicaţia este
dezvoltată, noi caracteristici sunt adăugate scheletului existent. În contrast cu prototipul de aruncat,
aici se investeşte un efort considerabil într-un design modular şi extensibil, precum şi în adoptarea
unui stil elegant de programare.
Această metodă are următoarele avantaje:
• deoarece prototipul rulează într-un mediu artificial, anumite dezavantaje ale produsului final
pot fi scăpate din vedere de clienţi;
• clientul nu înţelege de ce produsul necesită timp suplimentar pentru dezvoltare, având în
vedere că prototipul a fost realizat atât de repede;
• deoarece au în fiecare moment şansa de a face acest lucru, clienţii schimbă foarte des
specificaţiile;
• poate fi nepopulară printre dezvoltatori, deoarece implică renunţarea la propria muncă.
Dezavantaje:
11
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Datorită acestor caracteristici, metodele formale sunt potrivite în special pentru sisteme cu
cerinţe critice.
Există mai multe limbaje pentru specificarea formală a funcţionalităţii programelor: Z, CSP
(Communicating Sequential Processes), VDM (Vienna Development Method), Larch, FDM
(Formal Development Methodology) etc.
În continuare, vom exemplifica câteva funcţii simple de lucru cu o bază de date cu datele de
naştere ale unor persoane, descrisă în limbajul Z (Spivey, 1989):
Extreme Programming (Kent Beck, 1996) este o metodologie care propune rezolvări
originale pentru problemele care apar în dezvoltarea de programe. Fiind o tehnologie nouă (şi
extremă) are atât adepţi cât şi critici. XP consideră că dezvoltarea programelor nu înseamnă ierarhii,
responsabilităţi şi termene limită, aşa cum se află acestea pe masa administratorului, ci înseamnă
colaborarea oamenilor din care este formată echipa. Aceştia sunt încurajaţi să îşi afirme
personalitatea, să ofere şi să primească cunoştinţe şi să devină programatori străluciţi.
De asemenea, XP consideră că dezvoltarea de programe înseamnă în primul rând scrierea de
programe. Această sintagmă banală se pare că este uitată de multe companii care se ascund în
spatele proceselor de dezvoltare stufoase, a şedinţelor şi a rapoartelor de activitate. XP ne aminteşte
cu respect ca fişierele PowerPoint nu se pot compila.
De altfel, inspirarea proceselor de dezvoltare a programelor din ingineria construcţiilor se
pare că nu este cea mai fericită alegere. Este adevărat că un inginer care vrea să construiască un pod
peste un râu face mai întâi măsurători, realizează un proiect şi abia apoi trece la execuţie, toate
acestea într-un mod secvenţial şi previzibil. Dar dezvoltarea de programe nu seamănă cu aşa ceva,
oricât am vrea să credem asta. Dacă inginerului constructor respectiv i s-ar schimba cerinţele de
rezistenţă şi i s-ar muta malurile chiar când a terminat de construit jumătate de pod, putem fi siguri
că acel inginer şi-ar schimba modul de lucru. Din păcate însă, nu ştim (încă) cum.
Iniţiatorii XP definesc următoarele două carte, ca bază filosofică pentru această
metodologie.
12
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
• Ai dreptul să ştii ceea ce se cere, prin cerinţe clare, cu declaraţii clare de prioritate;
• Ai dreptul să spui cât îţi va lua să implementezi fiecare cerinţă, şi să îţi revizuieşti estimările
în funcţie de experienţă;
• Ai dreptul să îţi accepţi responsabilităţile, în loc ca acestea să-ţi fie asignate;
• Ai dreptul să faci treabă de calitate în orice moment;
• Ai dreptul la linişte, distracţie şi la muncă productivă şi plăcută.
Aceste afirmaţii, deşi par de la sine înţelese, conţin semnificaţii profunde. Multe din
problemele apărute în dezvoltarea programelor pornesc de la încălcarea acestor principii.
Enumerăm pe scurt câteva dintre caracteristicile XP:
4. Concluzii
Au fost prezentate aici cele mai importante metodologii de dezvoltare a programelor. Mai
întâi au fost descrise metodologiile generice: secvenţială, ciclică şi hibridă, cu avantajele şi
dezavantajele fiecăreia. Apoi s-au amintit câteva metode concrete de dezvoltate: modelul cascadă,
modelul spirală, WinWin, prototipizarea, metodologia Booch, metodele formale şi aşa-numita
„programare extremă”.
13
Ingineria programării
Cursul 4
1. Funcţiile managementului
1.1. Planificarea
1.2. Organizarea
1.3. Selecţia de personal
1.4. Conducerea
1.5. Controlul
2. Managementul software
2.1. Planificarea proiectului
2.2. Controlul proiectului
3. Managementul configuraţiei
4. Managementul echipei
4.1. Managementul forţei de muncă
4.2. Organizarea echipei
4.2.1. Organizarea ierarhică
4.2.2. Organizarea matrice
4.2.3. Echipa programatorului şef
4.2.4. Principii generale de organizare a unei echipe
5. Concluzii
1. Funcţiile managementului
Din punct de vedere organizaţional, managementul reprezintă îndeplinirea unor obiective
prin intermediul oamenilor şi al altor tipuri de resurse. O examinare mai atentă a principiilor de
management confirmă faptul că acesta se referă de asemenea la procesul de stabilire şi atingere a
scopurilor prin cinci funcţii de bază (planificare, organizare, selecţie de personal, conducere şi
control), folosind resurse umane, financiare şi materiale. Aceste cinci funcţii vor fi detaliate în
continuare.
1.1. Planificarea
Această funcţie managerială priveşte anticiparea situaţiilor viitoare şi determinarea celei mai
potrivite modalităţi de acţiune pentru îndeplinirea obiectivelor organizaţionale. Deseori considerată
„prima” funcţie a managementului, planificarea pune bazele tuturor celorlalte funcţii. Planificarea e
un proces continuu care implică stabilirea acţiunilor necesare pentru a răspunde la întrebări precum:
ce trebuie făcut, de către cine, unde, când şi cum. De asemenea, în această fază managerul trebuie să
ia în calcul şi factorii care pot ajuta sau împiedica atingerea scopului, precum şi posibilele
alternative disponibile pentru atingerea scopului.
Prin planificare se instituie o serie de acţiuni care angajează indivizii, departamentele şi
întreaga organizaţie timp de zile, luni sau chiar ani. De aceea, planificarea trebuie făcută cu atenţie,
luându-se în calcul următoarele aspecte:
În funcţie de amploare sau sfera de acţiune, se pot evidenţia trei categorii de planificare:
1.2. Organizarea
Organizarea este funcţia managementului care îmbină resursele umane şi materiale prin
proiectarea unei structuri formale a sarcinilor şi autorităţii. Organizarea stabileşte deci relaţiile
dintre activitate şi autoritate. În acest context, managerul trebuie să îndeplinească patru scopuri
principale:
2
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
cine răspunde pentru fiecare sarcină. Structurarea orizontală defineşte relaţiile de lucru între
departamente şi stabileşte numărul de subordonaţi ai fiecărui manager.
1.4. Conducerea
3
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
1.5. Controlul
2. Managementul software
Multe proiecte de dezvoltare software au probleme, mai devreme sau mai târziu. Programul
nu este livrat la timp, bugetul este depăşit, clienţii sunt nemulţumiţi. De multe ori, cauzele sunt de
natură tehnică. Însă în la fel de multe situaţii, problemele îşi au originea în organizarea şi
managementul proiectului. Când un produs este livrat prea târziu, motivele prezentate sunt în
general următoarele:
4
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
deseori termenul planificare informaţională. Scopul său este precizarea unor condiţii sau
constrângeri pentru fiecare proiect concret.
De asemenea, nici din punct de vedere tehnic programul nu este pornit de la zero. El trebuie
să fie interfaţat cu programe existente, să extindă funcţionalitatea altor programe, să folosească
biblioteci de funcţii etc. De fapt, însuşi termenul de dezvoltare software poate fi considerat
nepotrivit. Nu doar programul este dezvoltat, ci un întreg sistem. Software-ul este o componentă
principală, dar nu este unică.
Să considerăm un sistem pentru automatizarea unei biblioteci. El va conţine diverse
componente soft, precum baze de date despre cărţi şi persoane sau interfeţe pentru prelucrarea
interactivă a cererilor utilizatorilor. Pe lângă acestea, trebuie găsite modalităţi de rezolvare a unor
probleme de genul:
5
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
6
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Planul proiectului trebuie să furnizeze o imagine clară a ceea ce trebuie făcut, atât pentru
client cât şi pentru echipa de realizare. Dacă obiectivele nu sunt clare, ele nu vor fi niciodată
îndeplinite.
După ce planul proiectului a fost realizat şi aprobat, poate începe executarea propriu-zisă. Pe
parcursul executării, trebuie controlate următoarele dimensiuni:
• timpul;
• informaţiile;
• organizarea;
• calitatea;
• banii.
Cursul de evoluţie al proiectului (aspectul timp) este greu de măsurat. Afirmaţiile de genul
„90% din cod a fost deja scris” trebuie cântărite de două ori înainte de a fi crezute. De obicei, se
prezintă o situaţie mai bună a proiectului decât este în realitate. Timpul necesar construirii unui
sistem este în mod evident legat de dimensiunea sa şi deci de forţa de muncă utilizată. Cu cât un
sistem este mai mare, cu atât mai mult timp trebuie pentru dezvoltarea sa, deşi se poate încerca
scurtarea intervalului prin alocarea suplimentară de personal. Una din problemele care apar aici este
găsirea unui compromis cât mai bun între timpul de dezvoltare şi resursele umane folosite. Totuşi,
cu cât sunt implicate mai multe persoane, cu atât va creşte timpul pentru coordonare şi comunicare.
Peste un anumit punct, mărirea personalului va avea ca efect creşterea timpului de dezvoltare. În
acest sens, legea lui Brooks afirmă că adăugarea de personal la un proiect întârziat îl va întârzia şi
mai mult.
Din punctul de vedere al informaţiilor, accentul principal cade pe realizarea documentaţiei:
documentele utilizatorului, documentele tehnice şi documentaţia proiectului însuşi, care include
starea curentă de lucruri, modificările convenite, deciziile luate.
În cadrul echipei, fiecare persoană trebuie să-şi cunoască foarte clar rolul. Dacă rolurile nu
sunt definite sau sunt neclare, fiecare individ îşi va stabili propriile scopuri, care pot intra în
contradicţie cu scopurile proiectului. Project manager-ul trebuie să ţină seama de aceste aspecte
organizaţionale la alcătuirea echipei şi stabilirea atribuţiilor.
Calitatea este foarte importantă, deoarece clienţii nu se mulţumesc cu soluţii pur tehnice, ci
doresc sisteme adecvate nevoilor lor reale. De multe ori, cerinţele de calitate pot fi conflictuale. Pe
parcursul executării proiectului, acestea trebuie mereu evaluate, pentru ca să poată fi luate măsuri
din timp pentru remedierea problemelor. Calitatea nu trebuie să fie o trăsătură adăugată, ci o
caracteristică intrinsecă a sistemului.
Controlarea resurselor financiare înseamnă în mare parte controlarea costurilor de personal.
Deşi costurile asociate hardware-ului şi diferitelor instrumente nu poate fi neglijat, acestea pot fi de
obicei estimate destul de precis în fazele incipiente ale proiectului. Estimarea costului se reduce
astfel la estimarea forţei de muncă necesare, care depinde de mulţi factori. Un factor important este
dimensiunea software-ului (determinată de exemplu de mărimea codului). Există însă şi alţi factori
care influenţează costurile sau productivitatea. O echipă bine echilibrată de persoane experimentate
vor avea o productivitate mult mai mare decât o echipă nou formată de persoane fără experienţă.
7
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
3. Managementul configuraţiei
Pe parcursul ciclului de viaţă al unui proiect de dezvoltare software sunt create şi actualizate
un mare număr de elemente, precum module de cod sursă, cerinţe de modificare etc. Pentru
gestionarea acestora trebuie să existe un set de proceduri bine definite, care poartă numele de
managementul configuraţiei.
De cele mai multe ori, un sistem .software nu este monolitic. Sistemele există în diferite
versiuni şi configuraţii. Versiunile diferite apar atunci când sunt făcute modificări după ce sistemul
a fost livrat clientului. Din timp în timp, clientul este confruntat cu o nouă lansare. Diferite versiuni
pot exista şi pentru componentele din sistem. Dacă o modificare a fost aprobată, un programator
poate implementa schimbarea prin rescrierea unei componente. În acelaşi timp, un altul poate utiliza
în continuare versiunea anterioară a aceleiaşi componente.
La orice moment trebuie să existe o singură versiune oficială setului de documente legate de
proiect. Aceasta se numeşte linie de bază (engl. „baseline”), definită ca standard IEEE:
„o specificaţie sau un produs care a fost analizat şi acceptat în mod formal, care serveşte în
continuare ca bază pentru dezvoltarea viitoare şi care poate fi modificat numai prin proceduri
formale de control al modificărilor”.
Linia de bază este deci o bază de date partajată care conţine toate entităţile aprobate,
denumite entităţi de configurare (engl. „configuration items”). O entitate de configurare este
definită de asemenea ca standard IEEE astfel: „o colecţie de elemente hardware sau software tratate
ca o unitate în scopul gestionării configuraţiei”.
Exemple de entităţi de configuraţie sunt:
8
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Am menţionat că entităţile din baza de date partajată pot fi utilizate fără restricţii de toţi
membrii echipei. Dacă o entitate trebuie modificată, persoana responsabilă de schimbare primeşte o
copie a entităţii şi apoi entitatea este blocată temporar, astfel încât nimeni să nu poată o modifica în
acelaşi timp. Persoana desemnată acţionează asupra copiei. După ce modificarea este testată, este
trimisă înapoi la CCC. După ce comisia o aprobă, entitatea este inclusă în baza de date, schimbarea
e documentată şi apoi entitatea e deblocată. Şirul de modificări documentate formează istoricul
reviziei entităţii.
Când o entitate e modificată, este păstrată şi versiunea veche. Aceasta poate fi încă utilizată
de alte persoane până când acestea se adaptează schimbării. Având versiuni diferite pentru aceeaşi
entitate, trebuie să existe o modalitate de a le distinge. De obicei, se preferă o schemă numerică, în
care fiecare versiune următoare este identificată de următorul număr. Pentru o componentă X vom
avea deci versiunile X.0, X.1, X.2 ş.a.m.d.
Schema GNU de numerotare denotă versiunea unui produs software printr-un triplet de
întregi: versiunea majoră, versiunea minoră şi patch-ul.
Între două versiuni majore, în produs au loc schimbări majore ale funcţionalităţii şi în acest
caz nu este garantată compatibilitatea înapoi.
Dimpotrivă, între două versiuni minore ale aceleiaşi versiuni majore trebuie să existe
compatibilitate. De exemplu, formatele de fişiere suportate de o versiune ulterioară trebuie să fie
suportate şi de versiunea minoră anterioară. Rolul unei versiuni minore este introducerea unor
funcţii noi. Funcţiile vechi nu vor fi îndepărtate; totuşi documentaţia programului îl poate avertiza
pe utilizator că la anumite funcţii se va renunţa în viitor.
Patch-ul nu poate realiza decât schimbări în implementarea unor funcţii. De obicei, rolul său
este corectarea unor erori ale programului.
Aceste reguli nu se aplică însă şi pentru programele în versiuni alfa sau beta (0.Y). Înainte de
versiunea 1.0, deoarece programul este în curs de formare, sunt permise modificări importante între
versiuni. Totuşi, versiunea 1.0 trebuie să reprezinte o platformă stabilă, cu cât mai puţine bug-uri,
care să poată fi utilizată cu încredere de clienţi. Nu e neapărat necesar ca aceasta să conţină cât mai
multe funcţionalităţi, ci ca funcţiile implementate să fie cât mai sigure. Noi funcţionalităţi pot fi
adăugate în versiunile următoare.
La nivelul codului sursă, managementul configuraţiei este sprijinit de instrumente puternice,
care blochează şi deblochează elementele, asigură numerotarea automată a reviziilor şi furnizează
utilizatorului ultima versiune disponibilă.
Un astfel de instrument este CVS (Concurrent Versions System), care permite crearea
istoricului schimbărilor din cod. Când sursele sunt modificate uneori apar erori, care pot fi detectate
abia după un interval mare de timp de la modificare. În aceste situaţii este utilă identificarea
versiunilor vechi şi implicit a schimbărilor care au produs eroarea. CVS nu salvează toate fişierele
sursă din diferitele versiuni, ci numai modificările aduse fişierelor. De asemenea, când mai mulţi
programatori lucrează la acelaşi proiect, trebuie evitată suprascrierea de către o persoană a
modificărilor alteia. O rezolvare a acestei probleme este blocarea unui fişier astfel încât acesta să nu
poată fi modificat decât de un singur dezvoltator la un anumit moment de timp. Abordarea CVS se
bazează pe izolarea programatorilor, astfel încât fiecare lucrează în propriul său director, iar la
sfârşit, când toţi au terminat, programele sunt integrate.
4. Managementul echipei
În multe organizaţii care dezvoltă proiecte software, programatorii, analiştii şi alţi
profesionişti lucrează împreună într-o echipă. Stabilirea structurii adecvate a unei echipei depinde
9
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
de mulţi factori, precum numărul de persoane, experienţa şi gradul de implicare în proiect, tipul
proiectului, diferenţele individuale şi stilul.
Orice tip de proiect implică un număr de sarcini de lucru. O responsabilitate fundamentală a
project manager-ului este coordonarea şi distribuirea sarcinilor către toţi membrii echipei. Pentru un
proiect mic, echipa va consta în câteva persoane. Pe măsură ce creşte dimensiunea proiectului, şi
dimensiunea echipei va creşte. Echipele mari sunt dificil de coordonat iar volumul comunicării
dintre membri tinde să crească exponenţial cu mărimea echipei. De aceea, echipele mari sunt
împărţite în sub-echipe astfel încât majoritatea comunicării şi coordonării să se desfăşoare la nivelul
acestora.
O echipă este formată din indivizi, fiecare cu scopurile sale personale. Este sarcina project
manager-ului să formeze din aceştia o echipă, în care scopurile individuale sunt subscrise scopului
proiectului ca întreg. Identificarea încă de la început a scopurilor proiectului este foarte importantă,
iar aceste scopuri trebuie aduse la cunoştinţa membrilor echipei. În caz contrar, fiecare persoană îşi
va stabili propriile scopuri, ceea ce poate cauza probleme serioase. De exemplu, un programator
pune accent pe viteza programului, altul doreşte să folosească cât mai puţină memorie, iar altul
consideră că cel mai important este să scrie cât mai multe linii de cod.
După ce au fost stabilite scopurile, trebuie monitorizate şi evaluate performanţele membrilor
echipei. Acest lucru este dificil, deoarece mare parte din ceea ce se face este „invizibilă” iar
progresul e greu de măsurat. De aceea, în mod ideal, se defineşte productivitatea drept suma
funcţionalităţilor realizate în unitatea de timp. Din punct de vedere practic, productivitatea se
defineşte ca numărul de linii de cod realizate pe lună-om. Toată lumea este de acord că nu este o
măsură optimă, dar până în prezent nu s-a găsit una mai bună. Una din marile pericole ale utilizării
acestei metode este faptul că programatorii tind să scrie cât mai mult cod cu putinţă, care are efecte
negative. De asemenea, definiţia aceasta a productivităţii nu încurajează reutilizarea componentelor,
care ar conduce la scrierea unui cod mai mic.
• Structura simplă: Într-o structură simplă există unul sau numai câţiva manageri şi un nucleu
de persoane care lucrează la producţia proiectului propriu-zis. Această configuraţie este
întâlnită de obicei în firmele noi, cu personal redus. Specializarea este limitată, la fel şi
instruirea şi formalizarea. Mecanismul de coordonare corespunzător este supervizarea
directă;
• Birocraţia automată: Când conţinutul sarcinilor este complet specificat, este posibilă
executarea acestora pe bază de instrucţiuni precise. Exemple tipice ale acestui tip de
configuraţie sunt producţia de masă şi liniile de asamblare. Instruirea este redusă, în schimb
se pune mare accent pe specializare şi formalizare. Coordonarea se obţine prin
standardizarea proceselor de lucru;
• Forma divizionalizată: În acest tip de configuraţie fiecărei divizii (fiecărui proiect) i se
acordă o mare autonomie în ceea ce priveşte modul de atingere a scopurilor. Detaliile sunt
stabilite de divizii. Coordonarea se obţine prin standardizarea producţiei. Controlul se
exercită regulat prin măsurarea performanţelor diviziilor. Acest mecanism de coordonare
este posibil numai atunci când este specificat precis rezultatul final;
• Birocraţia profesională: Dacă nu este posibilă specificarea rezultatului sau conţinutul
sarcinilor, coordonarea poate fi realizată prin standardizarea calificării. Profesioniştii
talentaţi se bucură de o autonomie considerabilă privind modul de îndeplinire a atribuţiilor;
10
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
• Adhocraţia: În proiecte mari şi/sau inovatoare, lucrul este divizat între mai mulţi specialişti.
În acest caz, poate fi greu sau chiar imposibil de stabilit cu precizie ce trebuie să facă fiecare
specialist sau modul în care trebuie să-şi îndeplinească sarcinile. Succesul proiectului
depinde de capacitatea grupului de a atinge un scop nespecificat într-un mod nespecificat.
Coordonarea se obţine prin ajustare reciprocă.
Trebuie spus că majoritatea organizaţiilor reale nu pot fi încadrate într-un singur tip de
configuraţie. Diferite departamente ale firmei pot fi organizate diferit. De asemenea, tipurile
prezentate reprezintă idei abstracte. În realitate, organizaţiile pot tinde spre unul din aceste tipuri,
păstrând în acelaşi timp şi caracteristici ale celorlalte.
Teoria lui Reddin (1970) a stilurilor de management pune accent pe factorii interni. Autorul
distinge două dimensiuni ale managementului forţei de muncă:
• gradul de dirijare a relaţiei: se referă la atenţia acordată individului şi relaţiilor lui cu alţi
indivizi din organizaţie;
• gradul de dirijare a sarcinii: priveşte atenţia acordată rezultatelor care trebuie obţinute şi
modului în care acestea trebuie obţinute.
Gradele de dirijare pot fi scăzute sau ridicate, ceea ce conduce la patru combinaţii de bază,
prezentate în tabelul 1. Desigur, aceste combinaţii corespund unor orientări extreme. Pentru fiecare
dimensiune, există în realitate o întreagă gamă de nuanţe.
Stilul cel mai potrivit pentru o anumită situaţie depinde de tipul lucrării:
• Stilul de separare: Este cel mai potrivit pentru munca de rutină. Eficienţa este tema centrală.
Project manager-ul se comportă ca un birocrat care aplică reguli şi proceduri. Acest stil
corespunde configuraţiei birocraţiei automate;
• Stilul de relaţionare: Este cel mai eficient în situaţiile în care oamenii trebuie motivaţi,
coordonaţi şi instruiţi. Sarcinile sunt clar atribuite indivizilor. Munca nu are un caracter de
rutină, ci este inovatoare şi specializată. Stilul este asemănător configuraţiei de adhocraţie;
• Stilul de angajament: Este cel mai eficient când se lucrează sub tensiune. Project manager-ul
trebuie să ştie cum să se atingă scopurile fără să trezească resentimente. Stilul e asemănător
configuraţiei de birocraţie profesională;
• Stilul de integrare: Se potriveşte situaţiilor când rezultatul este nesigur. Munca are o natură
exploratorie şi sarcinile au un puternic caracter de interdependenţă. Rolul project manager-
ului este să stimuleze şi să motiveze. Şi în acest caz, configuraţia de adhocraţie este cea mai
apropiată.
11
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
familiar, coordonarea poate fi realizată prin standardizarea producţiei. Pentru o aplicaţie complexă
şi novatoare, acest mecanism ar fi ineficient.
Indivizii cooperează în cadrul unei echipe pentru a obţine un rezultat optim. Într-o echipă se
pot distinge diverse roluri: există manageri, testeri, designeri, programatori ş.a.m.d. În funcţie de
dimensiunea proiectului, o persoană poate îndeplini mai multe roluri, sau diferite persoane pot avea
acelaşi rol. Este foarte important ca atribuţiile rolurilor să fie bine precizate şi delimitate. Este de
asemenea important ca anumite roluri să fie separate; de exemplu, se recomandă separarea echipei
de test de echipa de implementare.
Echipele mari sunt greu de gestionat şi deseori sunt împărţite în subechipe. Prin definirea
clară a responsabilităţilor subechipelor, comunicarea dintre membrii echipei se limitează la
comunicarea în cadrul aceleiaşi subechipe, ceea ce măreşte productivitatea.
Organizarea ierarhică reflectă deseori structura globală a sistemului care trebuie dezvoltat.
Dacă sistemul are trei sub-sisteme majore, pot exista trei sub-echipe. De asemenea, pot exista
unităţi funcţionale asociate cu responsabilităţi specifice proiectului, cum ar fi testarea.
O problemă a organizării ierarhice este distanţa dintre nivelele superioare şi inferioare ale
piramidei. Munca „reală” se face de obicei pe nivelele inferioare, unde sunt prelucrate cunoştinţele
concrete despre aplicaţie. Pe măsură ce ne ridicăm în ierarhie, cunoaşterea devine din ce în ce mai
puţin specifică; de aceea managementul de pe nivelele superioare tinde să aplice coordonarea prin
standardizare. Totuşi, chiar dacă deciziile importante se iau la aceste nivele, în multe cazuri sunt
luate în considerare semnalele venite de pe nivelele de jos, care sunt de obicei însumate pe nivele
intermediare.
De multe ori, pe măsură ce informaţia urcă în ierarhie, lucrurile tind să pară din ce în ce mai
bune. Următorul scenariu nu este imposibil:
Aceste distorsiuni sunt totuşi dificil de împiedicat. Ele sunt cauzate de faptul că linia
ierarhică pe care se raportează progresul este aceeaşi cu cea utilizată pentru evaluarea
performanţelor. Oamenii doresc evaluări pozitive şi de aceea tind să-şi modifice şi rapoartele în
consecinţă. Dacă datele privind progresul proiectului sunt colectate şi prelucrate de persoane
neimplicate în evaluarea membrilor echipei, cresc mult şansele ca şi informaţiile să fie suficient de
corecte.
Un alt aspect problematic al acestui tip de organizare este faptul că individul este judecat,
atât din punct de vedere social cât şi financiar, după nivelul pe care îl ocupă în ierarhie. Este de
înţeles aspiraţia de a ajunge pe nivele din ce în ce mai înalte, deşi nu este foarte clar dacă acest lucru
este de dorit. Principiul lui Peter din legile lui Murphy spune: într-o organizaţie ierarhică, fiecare
angajat urcă până la nivelul său de incompetenţă. Totuşi, un programator bun nu e neapărat şi un
bun manager. Programarea necesită competenţe diferite de cele ale managementului. Ideală ar fi
stimularea oamenilor pentru menţinerea lor pe nivelele la care lucrează cel mai bine.
Acest tip de organizare este deseori întâlnită în situaţia când la un proiect software lucrează
oameni din diferite departamente şi care pot avea simultan mai mulţi şefi. Unitatea de bază este un
grup mic, specializat. Pot exista mai multe unităţi cu aceeaşi specializare. Unităţile sunt organizate
în conformitate cu specializarea lor; de exemplu: grafică computerizată, baze de date, interfeţe
utilizator, controlul calităţii etc. Proiectele implică unităţi cu diferite specializări. Indivizii sunt
organizaţi pe două axe: una reprezentând grupurile specializate iar cealaltă – proiectele la care
participă:
programare
grafică baze de date testare
în timp real
proiectul A X X
proiectul B X X X
proiectul C X X X
Acest tip de organizare a fost propus de Harlan Mills (1970). Nucleul unei astfel de echipe
constă în trei persoane. Programatorul şef este conducătorul echipei şi răspunde de proiectarea şi
implementarea porţiunilor cheie ale sistemului. El este ajutat de un asistent. Dacă e nevoie, acesta
poate lua temporar locul programatorului şef. Un bibliotecar se ocupă de administrare şi
documentare. Pe lângă aceste trei persoane, mai poate exista un mic grup de experţi. Evident,
programatorul şef joacă un rol central şi trebuie să fie foarte competent, mai ales în domeniul
tehnic, dar şi managerial. Se pune problema dacă există suficienţi astfel de programatori şefi.
13
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Noţiunea iniţială de programator şef pare elitistă. Pentru un proiect de dimensiuni foarte
mari este mai plauzibilă ideea unui grup restrâns responsabil colectiv. Rolurile nu sunt structurate în
funcţie de stadiile ciclului de viaţă a produsului. Nu există analişti, designeri, programatori.
Testarea însă poate fi alocată unei anumite persoane. În grup pot exista diferite nivele de experienţă.
Persoanele cele mai experimentate au rolul de programator şef şi asistent. Începătorii învaţă din
mers şi la început pot îndeplini rolul bibliotecarului.
Indiferent de modul în care este organizată o echipă, cel mai important este faptul că trebuie
să fie o echipă. Testele privind productivitatea în proiectele de dezvoltare software au arătat că
factorii privind capacităţile echipei au o importanţă mult mai mare decât orice altceva. Principiile de
organizare a unei echipe în general se aplică şi în cazul proiectelor software:
• Folosirea unui număr mic de oameni capabili: Productivitatea maximă este obţinută de un
grup relativ mic de oameni. Grupurile mari necesită mai multă comunicare, care are efecte
negative asupra productivităţii şi duce la erori mai multe;
• Sarcinile trebuie puse în acord cu motivaţiile şi capacităţile oamenilor disponibili: Cu alte
cuvinte, trebuie să ne asigurăm ca principiul lui Peter nu se aplică şi în cazul echipei noastre.
În cele mai multe organizaţii, programatorilor foarte buni li se oferă funcţii manageriale.
Este mult mai bine să li se ofere posibilităţi de a avansa în carieră în arii mai tehnice ale
dezvoltării şi întreţinerii software-ului;
• Pe termen lung, organizaţia are mai mult de câştigat dacă îi ajută pe angajaţi să dea ceea
ce au mai bun: Altfel spus, nici una din situaţiile următoare nu trebuie să aibă loc:
o Principiul lui Peter inversat: angajaţii urcă în ierarhie până la nivelul la care devin
indispensabili. De exemplu, un programator poate deveni singurul expert într-un
anumit domeniu. El nu va avea şansa să lucreze în alt domeniu. S-ar putea ca
persoana respectivă să părăsească firma pentru a lucra la altceva mai interesant;
o Principiul lui Paul: angajaţii urcă în ierarhie până la nivelul la care experienţa lor
devine învechită în cinci ani. Având în vedere viteza cu care noutăţile intră pe piaţa
dezvoltării software, angajaţii trebuie să aibă posibilitatea să fie la curent cu acestea;
• Este bine să fie selectaţi oameni care să formeze o echipă bine echilibrată şi armonioasă:
Nu este suficient să existe în echipă doar câţiva experţi de vârf. Trebuie să existe şi persoane
care să îndeplinească sarcinile mai simple, de rutină, pe care experţii s-ar putea simţi chiar
insultaţi să le rezolve;
• Cine nu se potriveşte cu echipa trebuie îndepărtat: Dacă echipa nu funcţionează ca o unitate
coerentă, de multe ori suntem înclinaţi să mai aşteptăm puţin, să vedem cum evoluează
lucrurile şi să sperăm ca totul va merge bine. Acest lucru este dăunător pe termen lung.
5. Concluzii
În acest curs au fost prezentate mai întâi noţiuni legate de managementul unui proiect
software, precum planificarea şi controlul proiectului. Apoi au fost tratate problemele legate de
gestionarea creării şi modificării elementelor caracteristice unui proiect de dezvoltare software. Au
fost explicate mecanismele care ţin de managementul unei echipe, privind în deosebi coordonarea şi
stilurile de management, alături de modalităţi de organizare a echipei şi sfaturi practice pentru
organizarea optimă a unei echipe de dezvoltare a unui proiect software.
14
Ingineria programării
Cursul 5
1. Introducere
2. Cum nu trebuie estimat costul
3. Modele algoritmice clasice
4. Modele algoritmice moderne
4.1. Modelul Walston-Felix
4.2. Modelul COCOMO
4.3. Analiza punctelor funcţionale
4.4. Modelul Putnam-Norden
5. Distribuirea forţei de muncă în timp
6. Concluzii
1. Introducere
La construirea unei case, la decorarea unei băi sau a unei grădini, dorim să cunoaştem costul
precis al operaţiilor ce urmează a fi efectuate înainte de începerea acestora. Un grădinar este capabil
să facă o aproximare a costurilor bazându-se, de exemplu, pe tipul pământului, mărimea dorită a
terasei sau a zonei cu iarbă, prezenţa sau absenţa unui iaz şi pe alte informaţii similare. Apoi această
estimare poate fi făcută mai precisă printr-un dialog ulterior înainte de a se începe lucrările. Cine
doreşte o precizie similară în ceea ce priveşte estimarea costurilor pentru un proiect de dezvoltare
software ar putea avea o surpriză.
Estimarea costurilor unei aplicaţii software reprezintă un domeniu foarte puţin dezvoltat, în
care toţi se bazează doar pe aproximări. Din fericire există şi excepţii de la această situaţie. Acum
există un număr de algoritmi care ne permit să estimăm costul total şi timpul de dezvoltare pentru
un proiect software pe baza unui număr limitat de generatori relevanţi de costuri.
În cele mai multe modele de estimare, este presupusă o relaţie simplă între cost şi efort.
Efortul poate fi măsurat de exemplu în luni-om (adică numărul estimativ de luni necesar unui singur
om să realizeze lucrarea), şi fiecărei luni-om i se asociază o sumă fixă de bani, corespunzător
salariului angajaţilor. Costul total estimat se obţine înmulţind numărul estimat de luni-om cu
factorul constant considerat.
Noţiunea de cost total reprezintă de obicei costul efortului iniţial de dezvoltare software,
costul analizei cerinţelor, proiectării, implementării şi testării, fără a fi luate în considerare costurile
de întreţinere. Prin timp de dezvoltare se înţelege timpul dintre specificarea cerinţelor şi momentul
în care produsul software va fi predat clientului. Noţiunea de cost, care se va folosi în continuare, nu
include şi posibilele costuri hardware. Ea include numai costurile legate de personalul angajat în
dezvoltarea produsului software.
Cercetările în domeniul estimării costului sunt departe de a fi cristalizate. Diferite modele
folosesc diferite sisteme de măsură şi generatori de costuri, încât este foarte dificilă compararea lor.
Să presupunem că un model foloseşte o ecuaţie de forma:
Acesta ecuaţie arată o relaţie între efortul necesar şi mărimea produsului (KLOC = Kilo-
Lines Of Code, kilo-linii de cod). Unitatea de măsură a efortului poate fi numărul de luni-om
necesare. Apar aici mai multe întrebări. Ce este o linie de cod? Numărăm codul maşină sau codul
sursă într-un limbaj de nivel înalt? Numărăm de asemenea şi comentariile, liniile libere care măresc
vizibilitatea sau nu? Luăm în considerare şi zilele libere, vacanţele, concediile medicale în noţiunea
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
de luni-om sau se referă numai la o măsurare netă? Diferite interpretări ale acestor întrebări pot
conduce la rezultate complet diferite. Uneori nici nu este cunoscut ce definiţii au fost folosite în
aplicarea modelului.
Pentru a determina ecuaţiile unui model algoritmic de estimare a costului, putem urma
diferite abordări. În primul rând ne putem baza ecuaţia pe rezultatul experimentelor. Într-un
asemenea experiment, în general variem un parametru, în timp ce ceilalţi parametri rămân constanţi.
În acest fel încercăm să determinăm influenţa pe care o are parametrul care variază. Un exemplu
tipic este cel ce testează dacă comentariile ajută la înţelegerea unui program. Vom pune un număr
de întrebări despre acelaşi program unor programatori împărţiţi în două grupuri. Primul grup va
primi programul fără comentarii, iar al doilea grup va primi textul aceluiaşi program, dar comentat.
Folosind rezultatele celor două grupuri, putem testa ipoteza realistă că o înţelegere mai bună şi mai
rapidă a programului are efecte pozitive asupra întreţinerii programului.
O altă modalitate de a descoperi modele algoritmice de estimare a costului se bazează pe o
analiză a datelor unui proiect real, dar şi pe un suport teoretic. O organizaţie poate strânge date
despre un număr de sisteme software care au fost dezvoltate. Aceste date pot privi timpul petrecut la
diferite faze bine determinate, calificarea personalului implicat, momentele de timp în care au
apărut erori, atât în timpul testării cât şi după instalare, complexitatea, robusteţea şi alţi factori
importanţi ai proiectului, mărimea diferitelor entităţi implicate şi o analiză statistică a acestor date.
Un exemplu pentru o asemenea relaţie este cea dată mai sus, ce exprimă o legătură între E şi KLOC.
Aplicabilitatea şi corectitudinea acestor ecuaţii este, în mod evident, dependentă de corectitudinea
datelor pe care se bazează. De asemenea, trebuie cunoscută ipoteza ce stă la baza ecuaţiei.
Rezultatele obţinute în acest fel reprezintă o medie, cea mai bună aproximare bazată pe
datele disponibile, de aceea rezultatele obţinute trebuie aplicate cu atenţie. De exemplu, programul
ce urmează a fi dezvoltat în cadrul unui nou proiect nu se poate compara cu produse anterioare
datorită inovaţiilor implicate. Estimarea costurilor pentru un proiect legat de o navetă spaţială nu
poate fi făcută printr-o simplă extrapolare a proiectelor anterioare.
Trebuie reţinut că aplicarea oarbă a unor formule din modele existente nu va rezolva
problema estimării costului. Fiecare model necesită o adaptare la mediul în care va fi folosită.
Aceasta conduce la necesitatea colectării continue a datelor din propriul proiect şi aplicarea unor
metode statistice pentru a calibra parametrii modelului.
Neconcordanţele dintre diferitele modele mai pot apărea deoarece:
• Majoritatea modelelor oferă o relaţie între numărul lunilor-om necesar şi mărime (în linii de
cod). După cum am remarcat mai devreme, există mai multe definiţii diferite pentru aceste
noţiuni;
• Efortul nu înseamnă întotdeauna acelaşi lucru. Uneori se consideră activităţile pornind de la
conceperea produsului, după ce au fost fixate cerinţele. Alteori se includ eforturile de
întreţinere.
• Scrierea de mai puţin cod: Mărimea sistemului este una din cauzele principale a efortului şi
a costului. Prin metode care încearcă sa reducă mărimea, cum ar fi reutilizarea programului
şi utilizarea de limbaje de nivel înalt, se pot obţine reduceri semnificative;
• Stimularea oamenilor să lucreze la capacitatea maximă: Capacităţile de lucru individual şi
în echipă au un mare impact asupra productivităţii. Angajarea celor mai buni oameni este de
obicei o afacere profitabilă. O mai bună stimulare, condiţii mai bune de lucru, cursurile de
perfecţionare asigură oportunităţi de creştere a productivităţii;
2
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Au fost făcute numeroase studii în care scopul principal era estimarea efortului necesar
pentru o sarcină limitată de programare. Primele experimente au fost realizate de Halstead (1977).
La baza modelului său stă faptul că numărarea liniilor de cod poate constitui o problemă, chiar dacă
avem o definiţie exactă a termenului „linie de cod”. Unele linii sunt mult mai complicate decât
altele. Conform teoriei lui Halstead, este mai bine să se pornească de la un număr de unităţi
sintactice, după cum sunt recunoscute de compilator. Halstead face distincţia între operatori şi
operanzi. Operatorii denotă o operaţie; exemple de operatori sunt operatorii standard (+, -, *, etc.),
dar şi semnul de punctuaţie punct şi virgulă (;) care arată structura unei instrucţiuni şi construcţii ca
if-then-else şi while-do. Operanzii înseamnă date: variabile şi constante. Calcularea numărului de
operatori şi operanzi dintr-un program va oferi o mai bună măsură a mărimii decât simplă calculare
a numărului de linii.
Cele patru entităţi de bază pentru un program, în modelul Halstead sunt:
N = N1 + N 2
Astfel se obţine o rafinare a măsurării numărului de linii simple de cod, LOC. Atât LOC cât
şi N oferă o bună corelaţie cu efortul de programare. De aceea este important să se găsească
modalităţi de estimare a entităţilor precum LOC şi N în primele etape. Valoarea lui N depinde de n1
şi n2. Valoarea lui n1 este, de cele mai multe ori, un factor constant pentru multe programe scrise
într-un anumit limbaj de programare de nivel înalt. Această constantă depinde de limbajul de
programare ales. De exemplu, pentru un limbaj dat, numărul maxim de operatori care poate fi
utilizat în orice program este fix: toţi sunt prezentaţi în sintaxa limbajului. Majoritatea programelor
vor folosi un mare procent din acestea, cel puţin o dată. O ipoteză consideră că n2 este determinat în
principal de numărul de variabile (VARS) care apar în program. Bazându-se pe aceste presupuneri, a
fost enunţată următoarea relaţie empirică:
Astfel, fiecare program va conţine aproximativ 100 de linii de cod, plus 5 linii suplimentare
pentru fiecare variabilă ce apare în program. Primele experimente arată că în acest fel se pot obţine
aproximări destul de corecte ale mărimii şi efortului necesar. Valoarea estimată a lui VARS poate fi
obţinută relativ devreme dacă este folosită o metodă de proiectare top-down în combinaţie cu un
limbaj de nivel înalt.
Generalizarea acestor rezultate la programe mai mari nu este indicată. În programele mai
complexe, factori precum interfaţa dintre componente şi comunicarea necesară între persoanele
implicate joacă un rol ce nu poate fi neglijat.
3
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
• Dacă avem 12 luni pentru a finaliza o lucrare, ea va necesita 12 luni. Acest motiv poate fi
privit ca o variantă a legii Parkinson: munca ocupă tot timpul disponibil;
• Dacă ştim că concurenţa a făcut o ofertă de 100000 de euro, noi vom face o ofertă de 90000
de euro. Acesta este cunoscut sub denumirea de preţ de câştig;
• Dorim să ne promovăm produsul la un anumit târg de tehnică de calcul şi din acest motiv
programul trebuie scris şi testat în următoarele 9 luni, deşi realizăm că timpul este limitat.
Această situaţie este cunoscută sub denumirea de metoda bugetului de estimare a costului.
• Proiectul poate fi dezvoltat într-un an, dar şeful nu ar accepta acest termen. Ştim că termenul
de 10 luni este acceptabil şi atunci îl programăm pentru 10 luni.
Aceste estimări pot avea efecte dezastruoase, după cum s-a demonstrat frecvent în scurta
istorie a acestui domeniu. Argumentele politice intervin întotdeauna dacă estimările sunt realizate
de cei implicaţi direct în proiect, cum ar fi managerul proiectului sau o persoană ce lucrează direct
cu acesta. În curând estimările vor influenţa sau vor fi influenţate de către dorinţele acestor
persoane.
Pe de altă parte, simpla comparare a caracteristicilor unui proiect cu un proiect precedent nu
garantează o estimare corectă a costului său. Dacă o echipă lucrează în mod repetat la proiecte
asemănătoare, timpul de lucru necesar va scădea, datorită acumulării experienţei. În 1968, unei
echipe de programatori i s-a cerut să dezvolte un compilator FORTRAN pentru trei maşini diferite.
Efortul necesar pentru aceste trei proiecte este descris în tabelul de mai jos:
Pentru estimarea costului se poate apela la serviciul unui expert, care apelează la propria sa
experienţă. Factori greu de cuantificat, precum caracteristicile de personalitate sau caracteristici
neobişnuite ale proiectului, pot fi astfel luaţi în considerare. În acest caz, calitatea estimării nu poate
depăşi calitatea expertului.
Pentru o estimare mai precisă se pot solicita mai mulţi experţi. Totuşi, dacă un grup de
persoane trebuie să găsească împreună o soluţie, vom observa că unii membri ai grupului au un
impact mai mare asupra rezultatului decât ceilalţi. Unii nu îşi vor exprima opinia sau vor fi
impresionaţi de opiniile celorlalţi. Aceasta poate avea un impact negativ asupra rezultatului final.
Pentru a anticipa acest efect negativ, putem folosi metoda Delphi. În metoda Delphi, fiecare expert
îşi expune opinia în scris. Un moderator colectează estimările obţinute astfel şi le redistribuie
celorlalţi experţi. În acest proces nu sunt asociate numele experţilor cu estimările lor. Fiecare expert
4
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
va preda o nouă estimare bazată pe informaţiile primite de la moderator. Acest proces continuă până
când se ajunge la un consens.
O altă metodă care doreşte obţinerea unei estimări mai bune este aceea de a avea un expert
care să realizeze mai multe estimări: o estimare optimistă a, o estimare realistă m şi o estimare
pesimistă b. Folosind o distribuţie beta, efortul aşteptat va fi:
a + 4m + b
E=
6
Această estimare va fi probabil mai bună decât dacă s-ar fi considerat numai media
aritmetică a lui a şi b.
n
E = a0 + ∑ ai xi
i =1
Coeficienţii ai sunt constante, iar xi reprezintă factorii care au impact asupra efortului
necesar. Un număr mare de factori poate influenţa productivitatea şi implicit efortul necesar.
Analizând cu atenţie datele proiectelor precedente şi diferite combinaţii de factori, putem încerca să
obţinem un model cu un număr mic de factori. Nelson, de exemplu, sugerează un model care ia în
considerare 14 factori:
5
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Putem face mai multe observaţii asupra acestui model. În dezvoltarea programelor pentru
aplicaţiile de apărare, în care programele vor fi încorporate în maşini diferite de maşina gazdă (un
exemplu ar putea fi programul de control al zborului pentru rachete), factori precum x12 şi x14 vor
avea cu siguranţă un impact mare asupra costului. Aceasta nu se va mai verifica însă într-un caz
complet diferit. Observăm din nou că datele proiectelor care stau la baza modelului au un impact
semnificativ asupra factorilor care influenţează costul. Există o creştere a efortului datorită folosirii
limbajului de asamblare în locul unui limbaj de nivel înalt (x6).
În general modelele liniare nu funcţionează foarte bine. Deşi există un număr mare de
factori care influenţează productivitatea, este puţin probabil ca ei să intervină independent şi liniar.
Trebuie atrasă atenţia asupra preciziei acestui tip de formulă. În formula lui Nelson,
constantele sunt date cu precizia a 2 zecimale. Aplicând această formulă va rezulta o estimare a
efortului de genul 97,32 de luni-om. Va trebui totuşi să avem grijă la capcana exprimată în sloganul:
există trei tipuri de minciuni – minciuni mici, minciuni mari şi statistici. Formula lui Nelson este
rezultatul analizei statistice a datelor unui proiect real şi trebuie interpretată ca atare, adică în
termeni probabilistici. Dacă avem o estimare E, atunci efortul real R va verifica formula:
P((1 − α ) E ≤ R ≤ (1 + α ) E ) ≥ β ,
Complexitate
Tipul modulului Mică ↔ Mare
1 2 3 4 5
1. Management de date 11 13 15 18 22
2. Management de memorie 25 26 27 29 32
3. Algoritm 6 8 14 27 51
4. Interfaţă utilizator 13 16 19 23 29
5. Control 20 25 30 35 40
Fiind dată o matrice de costuri C, un modul de tip I, complexitate j şi mărime Sk, va rezulta
un cost al modulului M k = S k ⋅ Cij .
Acest tip de model are de asemenea probleme. În afară de dificultatea estimării costului de
integrare a modulelor, utilizatorul trebuie să estimeze subiectiv clasa de complexitate din care face
parte fiecare modul, ceea ce determină un grad mare de nesiguranţă. Alţi factori care vor avea
impact asupra productivităţii, cum ar fi experienţa în programare şi caracteristicile hardware, nu
sunt luaţi în considerare. Extinderea matricei pentru a include şi aceşti factori ar creşte gradul de
subiectivitate al metodei.
6
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
E = (a + b ⋅ KLOC c ) ⋅ f ( x1 ,..., xn ) ,
unde KLOC reprezintă mărimea programului în kilo-linii de cod, iar E reprezintă efortul în luni-om.
a, b şi c sunt constante iar f ( x1 ,..., xn ) este o funcţie care depinde de valorile factorilor x1 ,..., xn .
În general, formula de bază este:
E = a + b ⋅ KLOC c
Autor Formula
Halstead E = 0.7 ⋅ KLOC 1.50
Boehm E = 2.4 ⋅ KLOC 1.05
Walston-Felix E = 5.2 ⋅ KLOC 0.91
Când c < 1, se remarcă apariţia unui fenomen foarte bine cunoscut din teoria economică.
Pentru o producţie de masă, se presupune că este mai ieftin să se producă mari cantităţi din acelaşi
produs. Costurile fixe vor fi împărţite astfel unui număr mai mare de unităţi, ceea ce conduce la
scăderea costului pe unitate. În cazul programelor, liniile de cod sunt produsul. Dacă presupunem că
producând multe linii de cod va scade costul pe linie de cod. Motivul este costul instrumentelor
scumpe precum generatoare de program, medii de dezvoltare şi instrumente de testare, care poate fi
distribuit unui număr mai mare de linii de cod.
În cazul opus (c > 1), observăm că după un anumit punct, producerea de unităţi suplimentare
implică costuri suplimentare. Putem afirma că programele foarte mari vor fi mult mai scumpe.
Suma necesară va fi mai mare deoarece creşte necesitatea de comunicare şi de control managerial,
deoarece problemele şi interfeţele devin mai complexe. Deci fiecare linie de cod suplimentară
necesită mai mult efort.
Nu există nici un argument convingător pentru nici un tip de relaţie, deşi ultima (c > 1) pare
mai plauzibilă. În mod sigur, pentru proiecte foarte mari, efortul creşte mai mult decât liniar cu
mărimea. Alegerea unui model sau a altuia depinde în principal de gradul de complexitate a
interfaţării modulelor proiectului.
Este evident că valoarea exponentului c influenţează foarte mult valoarea calculată E, în
special pentru valori mari ale KLOC. Tabelul următor prezintă valorile lui E, calculate prin
7
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
metodele prezentate anterior şi pentru câteva valori ale KLOC. Se remarcă marea diferenţă dintre
modele. Pentru programe mici, prin metoda Halstead rezultă costuri estimate mici. Pentru proiecte
cu aproximativ un milion de linii de cod, acelaşi model generează estimări ale costului cu un ordin
de mărime mai mari decât prin aplicarea metodei Walson-Felix.
Ecuaţia ce stă la baza modelului Walston-Felix este: E = 5.2 ⋅ KLOC 0.91 . Acest model a fost
creat prin analiza a 60 de proiecte de la IBM. Aceste proiecte erau complet diferite ca mărime, iar
programele au fost scrise în mai multe limbaje de programare. Totuşi nu reprezintă o surpriză faptul
că dacă aplicăm acest model chiar unei submulţimi a celor 60 de proiecte, nu vom avea rezultate
satisfăcătoare.
Încercând să explice aceste rezultate dintr-o plajă mare de valori, Walston şi Felix au
identificat 29 de variabile care influenţează în mod sigur productivitatea. Pentru fiecare din aceste
variabile au fost considerate trei nivele: mare, mediu şi mic. Pentru un număr de 51 de proiecte,
Walston şi Felix au determinat nivelul fiecărei variabile din cele 29, împreună cu productivitatea
obţinută (exprimată ca numărul liniilor de cod pe lună-om). Aceste rezultate sunt prezentate în
tabelul următor pentru câteva din cele mai importante variabile. De exemplu, productivitatea medie
este de 500 de linii de cod pe lună-om pentru proiecte cu o interfaţă utilizator de complexitate
scăzută. Pentru o interfaţă de complexitate înaltă sau medie, productivitatea este de 295 şi respectiv
124 de linii de cod pe lună. Ultima coloană reprezintă variaţia productivităţii, PC (engl.
„productivity change”), valoarea absolută a diferenţei dintre valorile minime şi maxime.
8
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
29
I = ∑ Wi X i ,
i =1
În relaţia de mai sus, PCi reprezintă variaţia productivităţii factorului i. Pentru primul factor
din tabelul de mai sus, complexitatea interfeţei cu utilizatorul, rezultă următoarele: PC1 = 376 , deci
W1 = 1.29 . Variabilele X i pot lua valorile –1, 0 şi 1, unde factorul corespunzător este de nivel
scăzut, mediu sau înalt. Indexul productivităţii obţinut poate fi tradus într-o productivitate aşteptată
(linii de cod scrise pe lună-om).
Trebuie menţionat că numărul factorilor luaţi în considerare în acest model este destul de
ridicat (29 de factori din 51 de proiecte). De asemenea, nu este clar cum aceşti factori se
influenţează unul pe celălalt. Un alt dezavantaj ar fi că numărul de alternative pentru fiecare factor
este de numai 3, şi nu oferă destule opţiuni pentru situaţiile practice.
COCOMO (COnstuctive COst MOdel) este unul din cei mai bine documentaţi algoritmi de
estimare a costului (Boehm, 1981). În forma sa cea mai simplă, numită „Basic COCOMO”, formula
care exprimă legătura dintre efort şi mărimea programului este:
E = b ⋅ KLOC c ,
• Organice: În proiectele de tip organic o echipă relativ mică dezvoltă programul într-un
mediu cunoscut. Persoanele implicate au în general experienţă în proiecte similare realizate
9
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
în organizaţia lor. Astfel, ei pot să lucreze de la început, nefiind necesare investiţii iniţiale.
Proiectele de acest tip sunt de multe ori programe relativ mici;
• Integrate: Proiectele din acest tip implică sisteme unde mediul impune constrângeri severe.
Produsul va fi integrat într-un mediu care este foarte strict. Exemplu de asemenea proiecte
sunt programe de control al traficului aerian sau aplicaţiile militare;
• Semidetaşate: Aceasta este o formă intermediară. Echipa poate fi formată din persoane
experimentate şi neexperimentate, proiectul poate fi destul de mare, dar nu foarte mare.
Pentru clase diferite, parametrii metodei Basic COCOMO iau următoarele valori:
Clasa de proiect b c
organică 2.4 1.05
semidetaşată 3.0 1.12
integrată 3.6 1.20
Tabelul următor prezintă estimări ale efortului pentru fiecare din cele trei moduri, pentru
diferite valori ale KLOC (deşi un proiect organic de un milion de linii de cod nu este realist). Se
observă influenţa foarte mare a constantei c asupra estimărilor obţinute. Estimările efortului sunt
exprimate tot în luni-om.
Analiza punctelor funcţionale este o metodă de estimare a costurilor care încearcă să evite
problemele determinate de estimarea dimensiunii codului. APF (Albrecht, 1983) se bazează pe
numărarea diferitelor structuri de date utilizate; se presupune că acest număr este un bun indicator
pentru dimensiunea proiectului. Metoda este potrivită mai ales pentru aplicaţiile comerciale, în care
structura datelor are o foarte mare importanţă. APF este mai puţin indicată pentru proiectele în care
algoritmii joacă rolul dominant (de exemplu compilatoarele sau aplicaţiile în timp real).
Unul din scopurile principale ale APF este evaluarea posibilităţilor sistemului din punctul de
vedere al utilizatorilor. De aceea, analiza se bazează pe modalităţile în care diverşi utilizatori
interacţionează cu aplicaţiile. Astfel, sistemul îndeplineşte cinci funcţii fundamentale:
Fişierele interne logice (engl. „Internal Logical Files”, FIL): Permit utilizatorilor să
folosească datele pe care trebuie să le întreţină. De exemplu, un pilot poate introduce datele de
10
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
navigare la un terminal din carlingă înainte de plecare. Datele sunt stocate într-un fişier şi pot fi
modificate în timpul misiunii. Pilotul este deci responsabil pentru întreţinerea acestor date.
Fişierele externe de interfaţă (engl. „External Interface Files”, FEI): În acest caz,
utilizatorul nu este responsabil pentru întreţinerea datelor; acestea sunt localizate în alt sistem care
le întreţine. Utilizatorul sistemului analizat solicită datele doar pentru informare. De exemplu, un
pilot se poate informa asupra poziţiei cu ajutorul sateliţilor GPS sau al sistemelor de la sol. El nu are
responsabilitatea actualizării acestor date, însă le poate accesa în timpul zborului.
Intrările externe (engl. „External Input”, IE): Permit utilizatorului să întreţină fişierele
interne logice prin operaţii de adăugare, modificare şi ştergere.
Ieşirile externe (engl. „External Output”, EE): Permit utilizatorului să producă date de ieşire.
De exemplu, pilotul poate să afişeze separat viteza la sol şi viteza reală în aer, informaţii derivate
din datele interne (pe care le poate întreţine) şi cele externe (pe care le poate accesa).
Cererile externe (engl. „External Inquiries”, CE): Pentru ca utilizatorul să poată selecta şi
afişa datele din fişiere, el trebuie să introducă informaţii de selecţie pentru a găsi datele în
conformitate cu anumite criterii. În această situaţie datele din fişiere nu sunt modificate, ci doar
căutate şi furnizate. De exemplu, dacă pilotul afişează date cu privire la relieful solului, date stocate
anterior, rezultatul este regăsirea directă a informaţiilor.
Prin încercări repetate, s-au stabilit ponderi pentru fiecare din aceste entităţi. Numărul de
puncte funcţionale neajustate este:
În funcţie de complexitatea tipurilor de date, se disting o serie de valori pentru aceste puncte
funcţionale, prezentate în tabelul următor.
Nivel de complexitate
Tip
Simplu Mediu Complex
FIL 7 10 15
FEI 5 7 10
IE 3 4 6
EE 4 5 7
CE 3 4 6
PF = PFN ⋅ FCT .
11
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
diferite. Ele pot fi folosite pentru a stabili o rată de productivitate (PF / h) care facilitează estimările
privind durata proiectului ca întreg.
Norden a studiat distribuţia forţei de muncă în timp într-un număr de proiecte de dezvoltare
software în anii ’60. A descoperit că această distribuţie avea deseori o formă caracteristică. Această
formă caracteristică este bine aproximată de distribuţia Rayleigh. Bazându-se pe această
descoperire, Putnam a dezvoltat un model de estimare a costului în care forţa de muncă necesară la
un moment de timp t este dată de:
2
FM (t ) = 2 ⋅ K ⋅ a ⋅ t ⋅ e − at ,
unde a este un factor de accelerare care determină panta iniţială a curbei, iar K reprezintă forţa de
muncă totală necesară, incluzând faza de întreţinere. K este egal cu volumul zonei delimitate de
curba Rayleigh, reprezentată în figura 1.
Integrarea ecuaţiei pentru FM(t) determină efortul cumulat I:
2
I (t ) = K (1 − e − at )
Dacă considerăm momentul de timp T în care curba Rayleigh ajunge în punctul de maxim,
1
atunci a = 2 . Acest moment T va fi apropiat de momentul de timp în care proiectul va fi predat
2T
clientului. Aria delimitată de curba Rayleigh între punctele 0 şi T va fi o bună aproximare a
efortului iniţial de dezvoltare. Pentru acesta obţinem:
E = I (T ) = 0.3945K
Acest rezultat este foarte apropiat de o regulă euristică foarte des utilizată: 40% din efortul
total este cheltuit pentru dezvoltarea efectivă, în timp ce 60% este cheltuit pentru întreţinere.
Specificarea cerinţelor nu sunt incluse în model. Estimările conform acestui model nu se pot aplica
decât începând cu proiectarea şi implementarea.
12
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
D = k ⋅ E 1/ 3 ⋅ t 4 / 3 ,
unde D este dimensiunea proiectului, E este efortul total în ani-om, t este timpul scurs până la
lansare în ani iar k este un factor tehnologic bazat pe 14 componente, precum:
Puterea supraunitară a timpului are implicaţii puternice asupra alocării resurselor în proiecte
mari. Prelungiri relativ mici ale datei de livrare pot determina reducerea substanţială a efortului
(Pressman, 1997).
Pentru estimarea efortului, Putnam a introdus ecuaţia acumulării forţei de muncă:
A = E / t3 ,
unde A este numită accelerarea forţei de muncă iar E şi t au semnificaţiile de mai sus.
Accelerarea forţei de muncă este 12,3 pentru proiecte software noi, cu multe interfeţe şi
interacţiuni cu alte sisteme, 15 pentru sisteme de sine stătătoare şi 27 pentru reimplementări ale
sistemelor existente.
Pe baza celor două ecuaţii, putem elimina timpul şi determina efortul:
E = ( D / k ) 9 / 7 ⋅ A4 / 7 .
Acest rezultat este interesant deoarece arată că efortul este proporţional cu dimensiunea la
puterea 9 / 7 ≈ 1.286 , valoare similară cu factorul lui Boehm, între 1,05 şi 1,20.
Evident, scurtarea timpului de dezvoltare implică un număr mai mare de persoane necesare
pentru proiect. Referindu-ne la modelul curbei Rayleigh, scurtarea timpului de dezvoltare conduce
la mărirea valorii a, factorul de accelerare care determină panta iniţială a curbei. Vârful curbei
Rayleigh se deplasează spre stânga şi în acelaşi timp în sus. Astfel obţinem o creştere a puterii
necesare la începutul proiectului şi o forţă de muncă maximă mai mare.
Există şi dezavantaje ale acestei deplasări. Mai multe studii au arătat că productivitatea
individuală scade odată cu creşterea echipei. Conform lui Brooks, există două cauze ale acestui
fenomen:
• Dacă o echipă se măreşte, va creşte timpul acordat comunicării cu ceilalţi membri ai echipei
(pentru consultare, sincronizarea sarcinilor etc.);
13
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
• Dacă este adăugată forţă de muncă suplimentară unei echipe în timpul dezvoltării unui
proiect, mai întâi scade productivitatea. Noii membri ai echipei nu sunt productivi de la
început. În acelaşi timp ei necesită ajutor, deci timp, de la ceilalţi membri ai echipei în
timpul procesului de învăţare. Luate împreună, acestea conduc la scăderea productivităţii
totale.
Combinând aceste două informaţii, ajungem la fenomenul cunoscut sub denumirea de legea
lui Brooks: adăugarea de personal la un proiect întârziat îl va întârzia şi mai mult.
Analizând o mare bază de date ale proiectelor, Conte a descoperit următoarea relaţie între
productivitatea medie L (măsurată în linii de cod pe lună-om) şi mărimea medie a unei echipe P:
L = 777 ⋅ P −0.5
Lγ = L − l ( P − 1)γ ,
unde γ ∈ (0,1] este o măsură a numărului de legături de comunicaţie. Presupunem că există cel puţin
o persoană care să comunice cu mai mult de o persoană, deci γ > 0. Pentru o echipă de mărime P,
aceasta conduce la o productivitate totală:
Ltot = P ⋅ Lγ = P ⋅ ( L − l ⋅ ( P − 1)γ ) .
Pentru o mulţime dată de valori pentru L, l şi γ, pentru valori crescătoare ale lui P, această
funcţie creşte de la 0 la o valoare maximă şi apoi scade din nou. Deci există o mărime optimă a
echipei Popt , care conduce la o productivitate maximă a echipei. Productivitatea echipei pentru
diferite valori ale lui P este data în tabelul de mai jos. Aici, presupunem că productivitatea
individuală este de 500 LOC / lună-om (L = 500), iar scăderea de productivitate este de 10% pe
canal de comunicaţie (l = 50). Cu interacţiune completă între membrii echipei (γ = 1), rezultă o
echipă optimă de 5.5 persoane:
14
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
6. Concluzii
În acest curs au fost prezentate diferite modele pentru estimarea efortului necesar pentru
dezvoltarea unui proiect software, a forţei de muncă necesare şi a timpului efectiv de dezvoltare.
S-au descris şi factorii care nu trebuie să contribuie la estimarea costului proiectului. În final, s-a
prezentat o modalitate de estimare a distribuţiei forţei de muncă în timp pentru găsirea optimului
numărului de persoane implicate în proiect.
15
Ingineria programării
Cursurile 6-7
Între timp, Dedal, sătul de Creta şi de lunga sa absenţă de acasă, simţea un dor nebun de ţara sa, dar era
blocat de mare. Apoi el spuse: „Regele îmi poate închide calea pe uscat şi pe mare, dar, desigur, cerul este
liber şi noi pe acolo vom merge. Poate că Minos deţine restul, dar aerul nu îi aparţine”. Spunând aceste
cuvinte, Dedal şi-a îndreptat atenţia spre ştiinţe încă neexplorate şi a sfidat legile naturii. A aşezat un rând de
pene, începând cu cele mai mici, apoi crescându-le dimensiunea, astfel încât marginea părea că se înclină în
sus. În acelaşi fel naiul cu care cântă ciobanii este construit din trestie, fiecare puţin mai lungă decât cealaltă.
Apoi, a fixat penele la mijloc cu aţă, iar la bază cu ceară; după ce le-a aranjat în acest fel, le-a îndoit puţin,
dându-le o formă uşor curbă, aşa cum arată aripile păsărilor.
Publius Ovidius Naso: Metamorfoze, VIII, 183-194
1. Introducere
Dedal merită un loc în mitologia ingineriei programării. Pe vremea regelui Minos,
software-ul nu exista şi totuşi existau problemele şi noţiunile pe care le regăsim în ingineria
programării de astăzi. Un exemplu îl constituie construirea sistemelor complexe. Dedal deţine cu
siguranţă un record în acest domeniu. A reuşit să administreze un proiect care poate fi comparat cu
proiectele de dezvoltare software din ziua de astăzi: construirea labirintului din Knossos.
După o vreme, Dedal a dorit să părăsească insula Creta, aşa cum Ovidius povesteşte mai sus.
Totuşi regele Minos nu a vrut să îi dea drumul. Ştim cum se termină povestea: Dedal zboară din
Creta împreună cu fiul său, Icar. În ciuda avertismentelor tatălui său, Icar zboară din ce în ce mai
sus, se aproprie prea mult de soare şi ceara din aripi se topeşte. Icar cade în mare şi se îneacă iar
Dedal reuşeşte în schimb să ajungă cu bine în Sicilia.
Construcţia lui Dedal este interesantă din punctul de vedere al reutilizării resurselor. Faptul
că acest lucru interesează mai mult partea de hardware decât pe cea de software nu prezintă
importanţă aici. Ceea ce interesează în discuţia de faţă este aplicarea anumitor principii în
construcţie:
În urma aplicării corecte şi ferme a acestor principii, a fost realizat cu succes un proiect
ambiţios (zborul lui Dedal până în Italia). Încercarea de a cuceri cerul cu o tehnologie insuficientă
s-a terminat cu un dezastru (prăbuşirea lui Icar în mare).
Să facem acum un salt în istorie, la sfârşitul anilor ’70. Criza software era deja acută de
mulţi ani. Cererea de noi aplicaţii depăşea cu mult posibilităţile forţei de muncă din domeniu.
Diferenţa dintre cerere şi ofertă era în continuare în creştere. Reutilizarea soft-ului este una din căile
explorate pentru a obţine o creştere semnificativă în productivitatea software.
De ce să se scrie cod pentru nişte calcule deja cunoscute? Reutilizarea componentelor
software de calitate nu duce oare la creşterea drastică a siguranţei şi productivităţii? Totuşi, nu este
chiar atât de simplu. Utilizarea componentelor software existente necesită standardizarea
denumirilor şi interfeţelor. Ideea de a lipi componente între ele nu este direct transferabilă în
domeniul programării.
Punctul de vedere modern nu restricţionează noţiunea de reutilizare a soft-ului la reutilizarea
componentelor. Şi informaţiile care privesc proiectarea pot fi reutilizate, aşa cum pot fi reutilizate şi
alte cunoştinţe culese în timpul construcţiei unui proiect software.
Foarte apropiată de reutilizarea software este şi flexibilitatea programelor, care trebuie să fie
în continuă adaptare la modificările circumstanţelor. În implementarea următoarei versiuni a unui
sistem, am prefera, evident, să folosim cât mai mult din versiunea curentă. Aceasta este de
asemenea considerată a fi o formă de reutilizare a soft-ului.
Într-o tehnologie bazată pe compunere, reutilizarea se face parţial prin compunerea unui nou
sistem din componentele existente. Elementele cu care se construieşte (engl. „building blocks”) sunt
fragmente pasive ce sunt copiate dintr-o bază de componente existentă. Într-o tehnologie bazată pe
generare, este mult mai dificilă identificarea componentelor reutilizate. Mai precis, cunoştinţele
reutilizate se regăsesc într-un program care generează un alt program. Într-o tehnologie bazată pe
generare, tiparele reutilizabile reprezintă un element activ folosit pentru a genera sistemul ţintă.
Exemple mai vechi ale acestor două tehnologii sunt bibliotecile de subrutine şi, respectiv,
generatoarele de aplicaţii. Cele mai multe sisteme reutilizate deţin aspecte din ambele perspective.
Dacă avem de realizat un program care trebuie să facă apel la funcţii trigonometrice, în mod
normal nu ne gândim să ne construim propriile rutine de calcul al acestor funcţii. În general, acestea
sunt deja implementate în majoritatea limbajelor de programare. Ne-am pune problema
2
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
reimplementării lor doar în cazuri particulare, când performanţele programului depind în mod direct
de precizia sau viteza calculelor.
Punând întrebarea de ce este aşa de simplă reutilizarea funcţiilor matematice, ajungem să
găsim un număr mare de piedici ce sunt puse în faţa reutilizării componentelor software în alte
domenii. Pentru reutilizarea cu uşurinţă a unor funcţii, trebuie să avem:
Reutilizarea rutinelor funcţionează cel mai bine într-un domeniu de aplicaţii bine
documentat, cu noţiuni clare şi pentru care datele de intrare sunt într-un format standardizat. Istoria
modernă a reutilizării software începe cu McIlroy, care a prevăzut un viitor strălucit pentru o
tehnologie a unei componente software la Conferinţa NATO de Ingineria programării din 1968. Din
punctul său de vedere, ar trebui să fie posibilă asamblarea sistemelor şi componentelor mari
dintr-un număr de blocuri gata de utilizat, aşa cum sistemele hardware sunt asamblate folosind
componente standard.
Pentru ca reutilizarea componentelor software să fie realizabilă, trebuie mai întâi să
rezolvăm următoarele probleme:
Componentele hardware sunt de obicei clasificate într-o ierarhie multinivel. Având în vedere
că s-au standardizat deja convenţiile de nume, se poate trece în revistă ierarhia. La cel mai de jos
nivel sunt date descrieri alternative ale componentelor, ca de exemplu: descrierea limbajului,
schema logică şi informaţii de sincronizare, care descriu diferite aspecte ale componentelor.
S-au făcut unele eforturi pentru a clasifica componentele software după principii ierarhice.
În taxonomia lui Booch (1987), o componentă este descrisă mai întâi de nivelul de abstractizare pe
care îl implementează. O parte din taxonomia sa este prezentată în tabelul următor. În al doilea
rând, componentele sunt descrise de comportamentul lor în spaţiu şi timp.
şiruri de caractere
monolitice stive
cozi
structuri
liste
polilitice arbori
grafuri
utilităţi
instrumente
filtre
3
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
• Concurenţă: obiectele îşi pot păstra sau nu semantica proprie atunci când există mai multe
fire de control;
• Spaţiu: obiectele sunt sau nu sunt statice ca dimensiune;
• Manager de memorie: obiectele pot sau nu administra singure memoria;
• Iterator: componentele pot sau nu pot pune la dispoziţie operaţii care să permită acces la
obiectele cuprinse de componentă.
Tabelul următor descrie o altă taxonomie a unor bine cunoscute structuri de date. Pentru a
crea această taxonomie au fost utilizate relaţiile structurale dintre elementele unor structuri de date
compuse. De exemplu, există o relaţie 1-1 între elementele unei structuri lineare, ca lista sau coada.
0-0 mulţimi
stive
1-1 cozi
structuri
liste
1-n arbori
n-m grafuri
4
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
2.2. Şabloane
5
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Reutilizarea proiectării se dovedeşte profitabilă într-un mediu în care este dezvoltat mereu şi
mereu acelaşi tip de program. În multe medii de afaceri aplicaţiile sunt considerate în mod greşit
unice. Drept urmare, ele sunt proiectate şi implementate pornind de la zero de fiecare dată.
Lanergan şi Grasso (1984) declarau că în programele comerciale scrise în COBOL există numai
câteva operaţii de bază diferite, cum ar fi sortarea, actualizarea şi raportarea.
Reutilizarea arhitecturii, adică modalitatea în care diverse părţi a unui sistem se îmbină, este
oarecum diferită de reutilizarea componentelor software sau a şabloanelor. O bibliotecă de
arhitecturi software nu este chiar aşa de utilă. Pentru fiecare problemă în parte am putea mai
degrabă să căutăm o arhitectură specifică. O arhitectură nepotrivită nu poate fi niciodată baza unui
sistem bun. Situaţia se modifică oarecum dacă o problemă apare în mod repetat în diferite variante.
Dacă există o arhitectură standard utilă pentru un anumit tip de problemă, aceasta poate fi aplicată
tuturor variantelor viitoare.
Un prim domeniu din ştiinţa calculatoarelor în care se poate reutiliza arhitectura software
este construcţia compilatoarelor. Marea majoritate a compilatoarelor sunt construite cu aceleaşi
componente: parser, analizor lexical, analizor sintactic, tabelă de simboluri, generator de cod. Există
câteva tipuri bine definite de parsere, cum ar fi parserele LL(1) sau LALR(1). Există o teorie amplă
despre cum funcţionează compilatoarele. În acest mod a evoluat o arhitectură de compilatoare
standard general acceptată. Evident, nu s-a demonstrat niciodată că aceasta este singura cale, sau
cea mai bună cale pentru a construi compilatoare. Dar este o metodă fără cusur şi bine cunoscută de
a ataca probleme într-un domeniu dificil.
Un alt exemplu este domeniul mediilor de dezvoltare software, în care majoritatea
întrebuinţează un depozit de informaţii centrale înconjurată de un număr mic de straturi ce conţine
instrumente ce devin din ce în ce mai specifice către straturile exterioare.
Reutilizarea arhitecturii se regăseşte rareori şi în alte domenii. Principalul motiv este că un
corp similar de cunoştinţe partajate şi cristalizate pur şi simplu nu există. Putem totuşi observa cum
câteva tipuri de arhitecturi standard au fost implementate în alte discipline de inginerie. De
exemplu, Spector (1986) descrie procedurile utilizate în construirea podurilor de autostradă din
punctul de vedere al ingineriei programării. Standish (1984) observă că în fiecare disciplină de
inginerie trebuie depusă o muncă enormă şi câştigată multă experienţă înainte de a realiza o astfel
de standardizare.
Evident, ambele tipuri de transformări pot fi făcute manual, ceea ce deseori se şi întâmplă.
Putem lua totuşi în considerare posibilitatea asistenţei computerizate în realizarea acestor
transformări, adică, mai precis, un sistem automat care să facă transformări.
Cel mai simplu punct de pornire ne este pus la dispoziţie de transformările lingvistice ale
clasei. O construcţie de forma:
6
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
este foarte clară. Din păcate, această construcţie nu există în majoritatea limbajelor de nivel înalt
folosite în prezent. Prin urmare, la un moment dat, construcţia IfExists va trebui înlocuită cu o
secvenţă de cod echivalentă semantic.
Un sistem de transformare pentru un domeniu de aplicaţie larg va necesita în general
îndrumare umană pentru realizarea rafinării. De exemplu, dacă o descriere de nivel înalt aminteşte
de mulţimi de obiecte, aceasta se poate rafina printr-o reprezentare folosind liste sau arbori binari.
În general, programatorul este cel care va decide ce structuri de date se potrivesc cel mai bine
aplicaţiei, dacă sistemului de transformare îi lipsesc cunoştinţele pentru a realiza automat acest
lucru.
Cea mai dificilă sarcină a unui utilizator de sistem de transformare este identificarea unor
nivele intermediare consistente, bine definite care trebuie folosite în procesul de transformare. Dacă
reuşim să descompunem acest proces în nişte paşi bine separaţi din punct de vedere conceptual,
atunci transformările devin realizabile.
O întrebare interesantă este dacă putem formaliza toate construcţiile şi construi un
compilator inteligent care să translateze o proiectare în cod executabil fără intervenţie umană. Acest
lucru este într-adevăr posibil dacă restricţionăm domeniul de aplicaţie la unul foarte îngust. Pentru a
putea rescrie din toate punctele de vedere o proiectare, trebuie să se introducă în sistem, într-un fel
sau altul, o cantitate suficientă de cunoştinţe în domeniul aplicaţiei. Astfel de sisteme sunt
generatoarele de aplicaţii.
7
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Relaţia dintre diferitele module ale unui sistem pot fi exprimate formal într-un Limbaj de
Interconectare a Modulelor (engl. MIL, „module interconnection language”). MIL reprezintă un
instrument important atunci când se proiectează şi se întreţin sisteme mari, constituite din diverse
module. O descriere MIL este o descriere formală a structurii formale a unui sistem software, care
poate fi utilizată pentru a verifica automat integritatea sistemului şi pentru a verifica dacă diversele
module sunt conforme cu interfeţele aprobate.
Vom prezenta în continuare un mic fragment dintr-un cod MIL pentru a ilustra aspectul
general a unui astfel de limbaj. Exemplul priveşte structura unui compilator cu un singur pas.
Notaţia folosită este INTERCOL (Tichy, 1979), în care descrierea constă dintr-o secvenţă de
module şi de familii de sisteme urmate de un set de compoziţii. Exemplul conţine o singură familie
de sisteme. Un membru al acestei familii reprezintă o versiune a unui sistem. De notat că familia de
module scan are implementări multiple, câte una pentru fiecare limbaj suportat. Compoziţia dată la
sfârşitul descrierii selectează un număr de blocuri definite anterior.
system compile
provide compile
module compiler
provide procedure compiler
require scanner, parser, postfix_generator, symtbl_funct
end compiler
module scan
provide package scanner is
type token: ALPHA
function nextchar_funct return: CHARACTER
procedure backup_proc
end scanner
require symtbl_proc, errormsg_proc
implementation SCAN02 .. PL/I
implementation SCAN03 .. PASCAL
end scan
...
8
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
module errormsg
provide procedure errormsg_proc
implementation ERRMSG
end errormsg
COMPOSITION
compile_A = [compile, SCAN02, PARSE.NIP, SYMBL.PL/I,
POSTFIXGEN.NIP, SYSLBL, VRFY, ERRORMSG]
end compile
• resursele: tot ce are nume într-un limbaj de programare (constantă, tip, variabilă, procedură)
şi tot ce poate fi făcut disponibil de către un modul pentru un alt modul;
• modulele: care fac resursele disponibile sau care le utilizează;
• sistemele: grupuri de module care îndeplinesc împreună o sarcină bine definită. Pentru
lumea exterioară, un sistem poate fi văzut ca un singur modul.
Legătura dintre module poate fi modelată ca un graf: nodurile grafului reprezintă module, în
timp ce arcele (orientate) reprezintă dependenţele dintre module. Funcţie de gradul de sofisticare a
MIL, acest graf poate fi un arbore, un graf orientat aciclic sau un graf orientat fără nici o restricţie.
Toate MIL arată aceleaşi limitări: ele se implică numai în sintaxa interfeţelor. Nu se poate
aprecia dacă resursele prelucrate au sens sau nu. Goguen (1986) a încearcat să facă un pas mai
departe pentru a asigura unui MIL o semantică limitată. Sistemul lui se numeşte Limbaj de
Interconectare a Bibliotecilor (engl. LIL, „library interconnection language”). LIL conţine
următoarele entităţi:
• pachete cu axiome asociate: aceste axiome spun ceva legat de operaţia pachetului. Axiomele
pot fi formale (într-o logică primară) şi informale (limbaje naturale);
• teorii: seamănă cu pachetele, dar nu conţin deloc cod. Acestea arată numai că ar trebui
furnizate anumite tipuri şi funcţii şi pun la dispoziţie axiomele care descriu comportarea
acestor funcţii;
• vizualizările: arată cum este îndeplinită o anumită teorie.
Un exemplu ar putea clarifica această descriere. Fie SORT un pachet generic pentru sortarea
unei secvenţe. Încă nu am specificat tipul elementelor din secvenţă. Teoria ORDEREDSET ne
spune ce este aceea o ordonare, folosind următoarele axiome:
9
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
• E ≤ E (reflexivitate);
• E1 ≤ E 2 , E2 ≤ E3 ⇒ E1 ≤ E3 (tranzitivitate);
• E1 ≤ E2 , E2 ≤ E1 ⇒ E1 = E2 (antisimetrie);
• E1 ≤ E2 sau E2 ≤ E1 (ordine completă).
Mulţimea numerelor naturale cu relaţia de ordine „ ≥ ” satisface aceste axiome. Dar există
multe alte ordonări posibile (cum ar fi: toate numerele pare care sunt mai mari decât toate numele
impare şi atât numerele pare cât şi cele impare să fie în ordinea firească). Dacă dorim să sortăm o
secvenţă în ordine descrescătoare, alegem o vizualizare care are ca elemente ale ORDEREDSET
numerele naturale şi relaţia „ ≥ ” ca relaţie de ordine.
Construcţia de mai sus a fost numită de către Goguen compoziţie orizontală. Vizualizarea
descrie o interfaţă între două componente aflate la acelaşi nivel de abstractizare. O componentă
caută o teorie, cealaltă satisface axiomele, pe când vizualizarea ne transmite cum se potrivesc ele.
Este de asemenea posibilă şi o compoziţie verticală. În pachetul SORT nu a fost încă definită
modalitatea în care trebuie implementată o secvenţă. De aceea, SORT trebuie să fie conectat la o
componentă cu un nivel mai coborât de abstractizare care implementează o secvenţă.
Este evident că axiomele care nu sunt exprimate într-o notaţie formală nu pot fi verificate
automat. De aceea, Goguen propune un adevărat sistem de management, care ar trebui să poată
verifica axiomele (de exemplu: aprobarea de către un demonstrator de teoreme, de către
programator, executarea cu succes a câtorva teste). În caz că apare o eroare, o analiză a căii critice
ar putea indica cea mai slabă legătură în lanţul demonstraţiei.
Putem constata că MIL se potriveşte bine cu sistemele de transformare şi alte forme de
reutilizare în care proiectarea joacă un rol esenţial. Atunci când se caută o componentă potrivită
într-o bibliotecă de module, semantica acestora este mult mai importantă decât sintaxa interfeţei.
LIL reprezintă un prim pas în această direcţie, deoarece legătura semantică este una din cele mai
importante probleme în demararea reutilizării.
Programarea orientată obiect a fost una din cele mai răsunătoare sintagme ale anilor '80.
Care este potenţialul limbajelor orientate obiect în ceea ce priveşte reutilizarea software? Este
evident că bibliotecile de module devin mult mai utilizabile dacă conţin module orientate obiect.
Dacă modulul căutat nu este găsit, nu este necesară adaptarea unui modul aproape potrivit prin
cârpirea codului, acesta este întotdeauna un demers periculos. Caracteristicile dorite pot fi moştenite
dintr-o clasă convenabilă deja prezentă în bibliotecă iar caracteristicile în plus pot fi adăugate unei
subclase nou definite.
Moştenirea şi polimorfismul unui limbaj orientat obiect asigură prin ele însele oportunităţi
pentru construirea părţilor reutilizabile. Acestea promovează apariţia unor clase abstracte care pot
servi ca punct de plecare pentru derivarea mai multor componente specifice.
10
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
O întrebare centrală în toate tehnologiile de reutilizare discutate mai sus este felul în care se
poate exploata o mulţime dată de blocuri reutilizabile. Aceasta are o mare importanţă în diversele
proiecte din domeniul bibliotecilor de componente, unde principalul scop este de a prevedea
modalităţi de extragere a componentei utilizabile pentru sarcina în lucru. În mod alternativ, putem
privi reutilizarea software drept identificarea blocurilor de care avem nevoie pentru a le putea folosi
în diverse aplicaţii.
Atunci când se încearcă identificarea unor componente refolosibile, principala problemă este
de a se decide de ce componente avem nevoie. O componentă refolosibilă trebuie valorificată, nu
din motivul evident că ne scuteşte de a implementa funcţionalitatea noi înşine, ci pentru că oferă o
parte din cunoştinţele domeniului corect, adică exact funcţionalitatea de care avem nevoie, câştigată
după multă experienţă şi după mai multe încercări de a găsi abstractizările corecte. Componentele ar
trebui să reflecte noţiunile primitive ale domeniului aplicaţiei. Pentru a putea identifica o mulţime
adecvată de primitive pentru un domeniu dat, este necesară o experienţă considerabilă în
implementarea unui software pentru acel domeniu. Pe măsură ce este câştigată această experienţă,
evoluează încet şi mulţimea adecvată de primitive.
Un domeniu este caracterizat printr-o colecţie de noţiuni comune care au coerenţă, în timp
ce în afara domeniului, aceleaşi noţiuni nu există sau nu prezintă aceeaşi coerenţă. Domeniile pot fi
mai largi sau mai restrânse, de exemplu:
• La început, nu există încă un set clar de noţiuni şi tot programul este scris de la început.
Experienţa se câştigă încet, pe măsură ce se învaţă din greşelile anterioare;
• În etapa a doua, probleme similare sunt depistate şi rezolvate în moduri similare. Sunt
recunoscute primele primitive semantice. După ce se încearcă şi se dă greş, se decide care
sunt primitivele utile şi care sunt cele inutile;
• În a treia etapă, domeniul este gata de reutilizare. S-au implementat un număr rezonabil de
programe, s-a stabilit o mulţime de concepte, s-au găsit soluţii standard pentru o serie de
probleme standard;
• În sfârşit, domeniul a fost explorat în totalitate. Implementarea programelor pentru domeniu
poate fi automatizată. Nu se mai programează efectiv, ci se foloseşte o interfaţă standard
formată din primitivele semantice ale domeniului.
Majoritatea reutilizării apare în ultima etapă, deşi nu mai este recunoscută ca atare. Acum
mult timp în urmă, calculatoarele erau programate în limbaj de asamblare. În limbaje de nivel înalt
„scriem ce dorim” şi compilatorul transformă acesta într-un program „adevărat”. Această acţiune nu
mai este privită ca reutilizare. Un fenomen asemănător apare la tranziţia dintr-un limbaj de a treia
generaţie într-un limbaj de a patra generaţie.
Din punctul de vedere al reutilizării, clasificarea de mai sus este una normală, naturală
pentru evoluţia unui domeniu. Diversele etape sunt categorizate la nivele calitative diferite:
11
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
În cadrul unui domeniu dat este folosit un limbaj informal, în care acelaşi lucru poate fi
exprimat în mai multe moduri, folosind concepte care nu sunt bine definite. Totuşi, limbajul
informal este uşor de înţeles deoarece conceptele se referă la un univers de expunere comun atât
vorbitorului cât şi ascultătorului.
Într-un limbaj formal, conceptele nu se referă la experienţa sau cunoştinţele de zi cu zi.
Acestea au înţeles abia într-un sistem formal. Un astfel de sistem formal este o maşină virtuală iar
limbajul său este de asemenea un limbaj formal.
A formaliza un domeniu înseamnă a construi un limbaj formal (pe domeniu) care mimează
un limbaj informal existent. Trebuie apoi să se aleagă dintre diversele primitivele semantice care
există informal. Uneori este convenabil să se adauge noi primitive, care se potrivesc bine
domeniului formalizat. De exemplu, să considerăm domeniul setărilor de tehnoredactare. O parte a
formatării documentelor se referă la asamblarea cuvintelor în linii şi a liniilor în paragrafe. Secvenţa
de cuvinte care compune un paragraf trebuie împărţită în linii astfel încât rezultatul să fie
satisfăcător din punct de vedere tipografic.
Knuth (1981) descrie această problemă folosind termenii „cutii”, „lipici” şi „penalizări”.
Cuvintele sunt cuprinse în cutii cu o anume lăţime. Spaţiul alb dintre cuvinte este considerat lipici,
care se poate mări sau micşora. Este preferat un spaţiu convenţional între cuvinte adiacente, iar
acestei asocieri îi corespunde o penalizare nulă. Apropierea sau depărtarea cuvintelor atrage o
penalizare pozitivă. Cu cât este mărit sau micşorat mai mult lipiciul, cu atât este mai mare
penalizarea. Prin urmare, penalizarea asociată cu formatarea unui întreg paragraf este suma
pedepselor asociate spaţiilor inter-cuvânt din paragraful formatat. Problema poate fi reformulată
astfel: împarte paragraful în linii astfel încât penalizarea totală să fie minimă. Trebuie menţionat că
penalizările pot fi asociate şi altor proprietăţi nedorite din punct de vedere tipografic, aşa cum este
despărţirea cuvintelor în silabe la sfârşitul unui rând. Noţiunile „cutii”, „lipici” şi „penalizări” oferă
o formalizare elegantă a anumitor aspecte ale tehnoredactării. De asemenea, oferă o soluţie eficientă
a problemei de mai sus, folosind o tehnică a programării dinamice.
În practică, formalizarea nu este o activitate cu un singur pas. Este mai degrabă un proces
iterativ. Versiunea formalizată nu descrie tocmai exact limbajul informal, ci precizează o posibilă
interpretare. Dacă îi studiem semantica, acesta poate avea câteva aspecte nedorite. În continuare
este atins un compromis acceptabil între cei ce folosesc limbajul (în domeniile de nivel înalt) şi cei
care îl implementează (în domeniile de nivel scăzut). Odată ce limbajul domeniului formal este
hotărât, el afectează şi limbajul informal al domeniului. Persoanele care lucrează în cadrul
domeniului încep să folosească primitivele limbajului formal.
Este acum clar că nu este indicată trecerea directă de la etapa întâi (fără reutilizare) la etapa
a treia (reutilizare structurată). Formalizarea are un efect de solidificare asupra primitivelor
semantice ale domeniului. Noţiunile privind primitivele se modifică, deoarece acestea nu mai sunt
considerate ca venind din universul de discurs intuitiv, ci din formalismul care stă la baza acestuia.
Răspunsul la întrebarea crucială dacă am formalizat primitivele semantice corecte devine mai greu
de găsit. Etapa a doua (reutilizarea ad hoc) este foarte importantă. În această etapă avem o vedere de
interior a domeniului de aplicaţie şi descoperim primitivele semantice utile. Atât în ceea ce priveşte
lucrul cu primitivele, cât şi implementarea lor, se dovedeşte că această experienţă este de o
importanţă vitală pentru formalizarea domeniul în mod corect.
12
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
„Reutilizarea este o investiţie pe termen lung.” (Tracz, 1990) Reutilizarea nu este gratuită.
Într-un mediu tradiţional de dezvoltare, produsele sunt realizate în funcţie de situaţie. Este probabil
ca situaţii similare să necesite produse şi componente puţin diferite. Pentru ca o componentă
software să devină refolosibilă, trebuie să fie generalizată pornind de la situaţia prezentă, testată şi
documentată intens, încorporată într-o bibliotecă şi o schemă de clasificare şi păstrată ca o entitate
separată. Aceste operaţii necesită investiţii iniţiale, care încep să fie recuperabile abia după o
anumită perioadă de timp.
Recuperări imediate ale investiţiei pot fi obţinute dacă programul refolosit este mic la
început şi are o bibliotecă iniţială ai cărei membri sunt extraşi din produsele existente. Dezvoltarea
programului poate fi apoi justificată pe baza experienţelor pozitive anterioare. Dar chiar şi atunci
trebuie alocate fonduri care nu sunt specifice proiectului.
Consecinţele economice ale reutilizării software depăşesc economiile costurilor în producţie
13
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Producţia de software trebuie să fie organizată astfel încât să fie promovată reutilizarea.
Modelul tradiţional cascadă tinde să împiedice reutilizarea. În modelul cascadă, accentul cade pe
măsurarea şi controlarea progresului proiectului. Calitatea produsului în raport cu gradul de
reutilizare este greu de măsurat. Nu există un stimulent real pentru a căuta să se realizeze gradul de
reutilizare, având în vedere că principalul (şi uneori singurul) scop este de a termina proiectul curent
la timp şi încadrat în buget. Nu există nici o motivare în a face următorul proiect să arate bine. În
consecinţă, reutilizarea software tinde să capete o prioritate mică. Reutilizarea software trebuie să
fie încorporată în procesul de dezvoltare. Articolele reutilizabile trebuie căutate în mod activ.
Chiar şi bibliotecile de articole reutilizabile trebuie administrate. Astfel, trebuie creată o
infrastructură organizaţională care face accesibilă biblioteca (documentare, scheme de clasificare),
evaluează candidaţii pentru includerea în bibliotecă, menţine şi actualizează biblioteca etc. Un rol
organizaţional separat, bibliotecarul, poate fi creat tocmai pentru acest scop. Sarcinile sale seamănă
cu cele ale unui administrator de baze de date.
Un alt tip de reutilizare este reutilizarea persoanelor. Proiectanţii experţi valorează
greutatea lor în aur. Orice programator mediu este capabil să scrie un program mare, complicat.
Pentru a obţine o soluţie radical nouă, mai bună, mai mică, mai elegantă pentru aceeaşi problemă,
este nevoie de o persoană care are idei sclipitoare din când în când.
O problemă majoră în domeniul nostru este că administratorii sunt mai apreciaţi decât
programatorii sau proiectanţii. Dacă eşti bun cu adevărat, mai devreme sau mai târziu (dar de obicei
mai devreme), te vei ridica pe scara ierarhică şi vei ajunge să faci parte administraţie. După Brooks,
există un singur mod de a neutraliza acest fenomen. Pentru a ne asigura că aceşti oameni sclipitori
rămân proiectanţi de sistem, avem nevoie de o schemă de personal duală, în care proiectanţii buni
au aceleaşi perspective în ceea ce priveşte slujba ca şi administratorii buni.
„Reutilizarea codului altor persoane ar demonstra că mie nu îmi pasă de munca mea. Nu voi
mai reutiliza cod, aşa cum nici Hemingway nu foloseşte paragrafele altor scriitori.” (Cox, 1990)
Reutilizarea înseamnă că programatorii trebuie să se adapteze, să încorporeze sau să
„reîntinerească” codul scris de alţi programatori. Există două aspecte importante ale acestui proces:
14
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
6. Concluzii
Reutilizarea software este mai degrabă un obiectiv decât un domeniu. A apărut ca o
problemă în cadrul ingineriei programării, nu din cauza fascinaţiei sale ca problemă ştiinţifică, ci
datorită câştigul scontat în productivitatea dezvoltării software. Prima conferinţă despre reutilizarea
software a avut loc în 1983. De atunci, acest subiect a primit o atenţie din ce în ce mai mare. În
acest curs am discutat principalele perspective ale problemei reutilizării. Diversele tehnologii de
reutilizare discutate pot fi divizate în două mari categorii tehnologii bazate pe compunere şi
tehnologii bazate pe generare. În primul caz, se urmăreşte încorporarea componentelor existente în
noul program ce va fi implementat. Într-o tehnologie bazată pe generare, este mult mai dificil să se
identifice explicit componentele care se reutilizează. Cunoştinţele reutilizate pot fi găsite în
programe care generează alte programe. Au fost descrise în continuare caracteristicile limbajelor de
interconectare a modulelor. S-au menţionat perspectivele reutilizării software şi în final s-a insistat
şi asupra aspectelor non-tehnice ale reutilizării: economia, managementul şi psihologia reutilizării.
15
Ingineria programării
Cursurile 8-9
1. Ingineria cerinţelor
1.1. Cerinţa
1.2 Extragerea cerinţelor
1.3. Metode pentru identificarea cerinţelor utilizatorilor
1.4. Metode pentru specificarea cerinţelor utilizatorilor
1.5. Documentul cerinţelor utilizatorului (DCU)
1.6. Cerinţe SMART
2. Analiza orientată obiect. Metode de analiză orientată obiect
2.1. Principiile analizei orientate obiect
2.2. Abstractizarea
2.3. Moştenirea
2.4. Comunicarea prin mesaje
2.5. Metodele de organizare
3. Metoda de analiză Coad-Yourdon
3.1. Activitatea I: Identificarea claselor şi obiectelor
3.2. Activitatea a II-a: Identificarea structurilor
3.2.1. Structura gen-spec
3.2.2. Structura întreg-parte
3.3. Activitatea a III-a: Identificarea atributelor
3.4. Activitatea a IV-a: Identificarea serviciilor
3.5. Activitatea a V-a: Identificarea subiectelor
4. Concluzii
1. Ingineria cerinţelor
Primul pas logic în dezvoltarea unui program este stabilirea precisă a cerinţelor clientului
(ceea ce vrea clientul să facă programul). Partea cea mai importantă a acestui proces o reprezintă
comunicarea dintre client şi echipa de dezvoltare. Când un inginer de programe lucrează la
stabilirea cerinţelor, el este numit inginer de cerinţe, analist de sistem sau analist. Dezvoltarea unui
program începe de obicei cu o idee a clientului despre un nou sistem sau pentru îmbunătăţirea unui
sistem existent. Clientul angajează un analist cu care va lucra împreună pentru specificarea mai
exactă a cerinţelor. Ideea iniţială a clientului poate fi vagă şi prost definită, sau dimpotrivă, poate fi
clară şi bine definită.
Stabilirea cerinţelor este probabil cea mai importantă activitate în dezvoltarea produselor
program. Dacă un client nu ştie sau nu poate să stabilească în urma unei discuţii cu echipa de
dezvoltare în mod exact ce vrea să facă produsul, este inutil să angajeze o echipă care să
programeze. O echipă de programatori poate să scrie cel mai estetic program din punct de vedere al
tehnicilor de programare folosite, dar dacă nimeni nu va dori să-l folosească, proiectul va fi un eşec.
Multe programe nu se potrivesc cu cerinţele clientului nu din motive de implementare
defectuoasă, ci din cauză că cerinţele nu au fost specificate corect de la început. Persoane ale căror
legături cu ingineria programării sunt mai mult pretinse decât obiective consideră că nepotrivirea
dintre aşteptările clientului şi programul obţinut ţin de lipsa de cultură, simţ artistic şi cunoştinţe
tehnice ale programatorilor. Adevărul este că programatorii nu pot şi nu au cum să cunoască
necesităţile clienţilor, mai ales dacă nu cunosc domeniul pentru care este scrisă o anumită aplicaţie.
Este responsabilitatea clientului de a veni cu cereri exacte şi pertinente. Este obligaţia
inginerului de cerinţe să discute cu clientul pentru a clarifica cerinţele şi a-l ajuta să-şi fixeze
priorităţile. Stabilirea precisă a cerinţelor este primul pas în obţinerea unui program care satisface
nevoile clientului. O specificaţie bună este folosită şi în fazele de validare şi verificare.
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
1.1 Cerinţa
Noţiunea de cerinţă este mai veche decât dezvoltarea programelor. De exemplu, o cerinţă
pentru o locomotivă ar putea arăta astfel: pe o cale ferată uscată, locomotiva trebuie să fie capabilă
să pornească un tren de 100 tone pe o pantă de maxim 5% cu o acceleraţie de cel puţin 0,5 m/s2.
De remarcat că această cerinţă spune ce vrea clientul. Nu spune nimic despre cum ar putea fi
realizată. Nu se specifică tipul motorului (cu aburi, diesel, electric) sau materialul din care să se
confecţioneze roţile.
Apare aici o problemă controversată: este bine să facem specificarea luând în considerare
felul în care sistemul va fi implementat? Fiecare variantă are avantajele şi dezavantajele ei:
Aceste activităţi sunt parte a unui proces iterativ, lung şi complicat. Negocierea este foarte
importantă. Diferenţa culturală dintre client şi analist este câteodată foarte mare. Există situaţii când
există diferenţe semnificative între aşteptările utilizatorilor finali şi ale clientului. Un exemplu
concret al acestui tip de conflict este când un patron ce comandă un program care să fie utilizat de
către angajaţii săi doreşte ca acesta să conţină module de monitorizare a muncii angajaţilor. Aceasta
ridică o dilemă de natură etică pentru inginerul programator: să anunţe angajaţii că sunt spionaţi
fără să ştie sau să fie loial celui care îl plăteşte. O altă problemă o reprezintă literatura SF studiată
de client, înainte de angajarea echipei de dezvoltare. Clientul aşteaptă mult prea mult de la program
şi de la echipa de dezvoltare, închipuindu-şi că programele se scriu ca în filme, într-o scenă de două
minute, ca un amănunt dintr-un film de două ore.
2
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Una sau mai multe din următoarele modele prezentate mai jos pot fi folosite pentru a
identifica cerinţele utilizatorului:
• Interviuri: Interviurile trebuie astfel structurate încât să poată aborda toate aspectele
implicate de sistemul software ce va trebui dezvoltat. Când există foarte mulţi utilizatori, se
va selecta un set reprezentativ dintre aceştia pentru a fi intervievaţi. Interviurile pot fi utile în
a asigura:
o completitudinea cerinţelor utilizatorului;
o existenţa unei acceptări generale a cerinţelor utilizatorului;
• Studiul sistemelor software existente deja: De multe ori, noul sistem software este destinat
înlocuirii altui sistem existent. Investigarea caracteristicilor şi bune şi rele ale sistemului
existent poate ajuta în determinarea cerinţelor pentru ceea ce se doreşte. Examinarea
manualelor utilizatorilor, a documentelor cerinţelor şi a diverselor propuneri poate fi foarte
folositoare;
• Studiul de fezabilitate: Studiul de fezabilitate reprezintă analiza şi proiectarea principalelor
caracteristici ale sistemului în scopul de a determina dacă implementarea este posibilă;
• Prototipul: Un prototip este un model executabil al unor aspecte selectate din sistemul
propus. Dacă cerinţele sunt neclare sau incomplete, ar putea fi utilă dezvoltarea unui
prototip, bazat pe un set de cerinţe de probă, pentru a identifica cerinţele reale ale
utilizatorilor.
Modul evident de specificare a unei cerinţe este limbajul natural. Limbajul natural este
foarte accesibil dar inconsistent şi ambiguu. De exemplu, afirmaţia: Baza de date va conţine o
adresă poate fi interpretată după cum urmează:
Va fi doar o singură adresă
O parte din baza de date va fi desemnată ca o adresă.
Va fi cel puţin o adresă în baza de date.
Formule matematice pot fi folosite pentru a clarifica o cerinţă. Toate simbolurile folosite
într-o expresie trebuie definite sau referite.
3
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Engleza structurată este folosită de obicei pentru a descrie procesele de bază ale sistemului.
Exemple:
• construcţii declarative
GET RAW DATA
REMOVE INSTRUMENT EFFECTS
CALIBRATE CORRECTED DATA
• construcţii decizionale
IF SAMPLE IS OF NOMINAL QUALITY THEN
CALIBRATE SAMPLE
ELSE
STORE BAD SAMPLE
• construcţii repetitive
FOR EACH SAMPLE
GET POINTING DIRECTION AT TIME OF SAMPLE
STORE POINTING DIRECTION WITH SAMPLE
1.4.4. Tabele
Diagramele bloc reprezintă modul tradiţional de prezentare a proceselor dorite. Ele pot, de
asemenea, demonstra contextul în care sistemul va opera atunci când este o parte dintr-un sistem
mai mare.
4
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Acesta este documentul obligatoriu, produs în faza definirii cerinţelor şi trebuie să fie
finalizat înainte de proiectarea sistemului. DCU trebuie:
Costul modificării cerinţelor creşte cu atât mai mult cu cât proiectul înaintează în fazele
următoare. În faza de testare a sistemului, verificarea se va face pe baza DCU. Standardele
ingineriei programării recomandă următoarele caracteristici ale stilului de prezentare a unui DCU:
• DCU trebuie scris folosind un limbaj, vocabular şi stil uşor de înţeles de către toţi
utilizatorii. DCU trebuie să fie clar, consistent, modificabil;
• Un DCU este clar dacă orice cerinţă este neambiguă (are o singură interpretare) şi este
înţeleasă de toţi participanţii la proiect;
• O cerinţă trebuie scrisă într-o singură propoziţie iar justificările trebuie separate de aceasta.
Se recomandă ca cerinţele corelate între ele să fie grupate. Structurarea cerinţelor în
document este foarte importantă;
• Un DCU este consistent dacă nu există conflicte între cerinţe. Folosirea mai multor termeni
pentru a specifica acelaşi lucru este un exemplu de inconsistenţă;
• Un DCU este modificabil dacă orice modificare a cerinţelor poate fi documentată uşor,
complet şi consistent.
1.5.2. Responsabilităţi
5
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
1. Introducere
2. Descriere generală
3. Cerinţe specifice
Secţiunea 1 trebuie să descrie pe scurt scopul sistemului software, listele de definiţii pentru
termeni utilizaţi în document, lista de referinţe bibliografice identificate prin autor, titlu şi date, şi o
trecere în revistă a restului documentului.
6
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
• Coad-Yourdon;
• Rumbaugh-Object Modelling Technique (OMT);
• Shlaer-Mellor;
• Booch (cărţile lui Booch privind metodele OO au fost prezentate de Stroustrup, inventatorul
limbajului C++, ca fiind singurele care merită să fie studiate în acest domeniu datorită
practicilor aprofundate de analiză şi proiectare expuse în lucrările sale. Din păcate însă,
notaţiile utilizate de Booch sunt complicate şi există prea puţine utilitare care să suporte
această metodă.)
În momentul de faţă, analiza orientată obiect evoluează iar analiştii, de obicei, combină
tehnicile diferitelor metode în faza de analiză a problemei.
Ecuaţia utilizată pentru a recunoaşte o metodă OO este:
În acest fel se poate spune dacă un limbaj sau un mediu este sau nu OO.
Analiza orientată obiect nu se utilizează pentru sisteme care au foarte puţine funcţionalităţi
sau pentru sisteme cu 1-2 clase şi obiecte.
2.2. Abstractizarea
Principiul abstractizării presupune ignorarea acelor aspecte ale unui subiect care nu sunt
relevante pentru obiectivul curent. Aceasta este o tehnică importantă pentru a coordona
complexitatea. Aşadar, abstractizarea este un mecanism care permite reprezentarea unei situaţii
complexe a lumii reale printr-un model simplificat. De exemplu, abstractizarea unei culori din
lumea reală poate fi realizată prin modelul RGB (red, green, blue).
Abstractizarea procedurală este principiul conform căruia orice operaţie poate fi tratată ca o
singură entitate în ciuda faptului că ea presupune realizarea mai multor operaţii de pe nivelul
următor de detaliu. Această formă de abstractizare joacă un rol foarte important în definirea
serviciilor sistemului.
Abstractizarea datelor reprezintă un mecanism mai puternic de abstractizare conform căruia
tipurile de date sunt definite în termenii operaţiilor ce se aplică obiectelor de acel tip, cu restricţia că
valorile acelor obiecte pot fi observate şi modificate doar prin intermediul operaţiilor menţionate.
Prin aplicarea acestui principiu, un analist defineşte atributele şi serviciile care manipulează
aceste atribute. Singurul mod de a accesa un atribut este prin intermediul serviciilor. Atributele şi
serviciile pot fi văzute ca formând un întreg.
Abstracţiile sunt încapsulate în obiecte. Stările şi comportamentele comune ale obiectelor
sunt încorporate în clase. Implementarea internă propriu-zisă este ascunsă de restul sistemului. De
exemplu, dacă o culoare este văzută ca o valoare RGB, reprezentarea internă poate folosi modelul
HSV (hue, saturation, value) dacă acesta este mai potrivit, fără ca acest fapt să afecteze restul
sistemului.
2.3. Moştenirea
8
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Se referă la interacţiunea prin mesaje între obiecte, interacţiune care corepunde modului
imperativ al limbajelor (comandă, cerere).
• diferenţierea între obiecte şi atributele lor (de ex.: a distinge între un copac şi înălţimea sa);
• diferenţierea dintre obiectul ca întreg şi părţile sale componente (de exemplu: a distinge
între un copac şi ramurile sale);
• diferenţierea diverselor clase de obiecte (de exemplu: clasa pietrelor şi clasa arborilor).
Puterea metodei constă în descrierea scurtă, concisă şi folosirea unor texte generale ca surse
de definiţii. Un mare avantaj este menţinerea aceleiaşi notaţii atât în faza de analiză cât şi în cea de
proiectare.
Există mai multe motive principale pentru care trebuie identificate clasele şi obiectele:
• găsirea unei reprezentări tehnice a sistemului mai aproape de modul în care concepem lumea
înconjurătoare;
• se creează astfel o bază stabilă pentru analiză şi specificaţii: clasele şi obiectele pentru un
sistem de control al traficului aerian pot fi aceleaşi şi peste cinci ani, doar serviciile şi
atributele se pot modifica radical. Clasele şi obiectele sunt relativ stabile de-a lungul
timpului, urmând ca, la apariţia modificărilor ulterioare, ele să constituie o bază pentru
reutilizare;
• evitarea schimbării reprezentării de bază în momentul trecerii de la faza de analiză la cea de
proiectare. Această problemă se rezolvă prin utilizarea unei reprezentări orientate obiect atât
în faza de analiză cât şi în fazele de proiectare şi implementare.
9
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Notaţii:
Numele clasei:
• este o construcţie substantivală (engl. „noun phrase”, un singur substantiv sau substantiv şi
adjectiv);
• se alege ţinând cont de vocabularul standard al domeniului problemei. Folosirea unei
terminologii nefamiliare clientului îl face să se simtă frustrat şi stânjenit;
• se referă doar la un singur obiect al clasei;
• trebuie să fie sugestiv;
• se recomandă să se scrie folosind litere mari şi mici pentru a face mai uşoară citirea sa.
• Sunt toate aspectele identificate relevante pentru sistem? Poate fi descris orice obiect al unei
clase? Care sunt atributele lui potenţiale? De exemplu, atributele potenţiale pentru un
funcţionar sunt: nume, parolă, autorizaţie. Altele, precum înălţimea, semne particulare,
greutate, nu sunt relevante pentru sistem;
• Este absolut necesar ca un obiect să execute anumite activităţi?
• Este caracterizată o clasă prin mai multe atribute? O clasă cu un singur atribut este
considerată suspectă;
• Este caracterizată o clasă prin mai multe obiecte? O clasă cu un singur obiect este
considerată suspectă;
• Se poate identifica un set de atribute aplicabile întotdeauna tuturor obiectelor clasei?
10
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
11
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
În vârf se află o clasă de generalizare, iar pe nivelele următoare clase de specializare (liniile
unesc clasele şi nu obiectele). Punctul de ramificare a liniilor spre specializări se reprezintă
printr-un semicerc. Fiecare clasă-specializare este denumită astfel încât să aibă un înţeles de sine
stătător. Numele cel mai potrivit este format din numele clasei generalizatoare urmat de numele
naturii specializării. De exemplu, pentru generalizarea Senzor, sunt preferate specializările
SenzorCritic şi SenzorStandard faţă de numele Critic şi Standard.
În clasele specializate se notează numai atributele şi serviciile caracteristice. Nu este
necesară menţionarea atributelor şi serviciilor moştenite din clasa de bază. Dacă sunt posibile mai
multe specializări, este utilă considerarea mai întâi a celei mai simple şi celei mai complicate
specializări, şi apoi găsirea unei variante intermediare.
De exemplu, considerând clasa Avion ca o generalizare, ea poate fi specializată în mai multe
moduri:
• AvioaneCuReacţie şi AvioaneCuElice;
• AvioaneCivile şi AvioaneMilitare;
• AvioaneCuAripiFixe şi AvioaneCuAripiMobile;
• AvioaneComerciale şi AvioaneParticulare.
12
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
În partea superioară este reprezentat obiectul întreg al clasei, iar în partea inferioară părţi ale
acestui obiect. Triunghiul face distincţia dintre întreg şi părţi. Fiecare capăt al liniei este marcat cu
un interval, indicând numărul de părţi pe care un întreg le poate avea şi numărul de întregi
corespunzători unei părţi, la orice moment de timp .
Exemple:
• de 0 motoare (planor)
• de cel mult 4 motoare
13
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
• ansamblu-părţi
• container-conţinut
• colecţie-membri
14
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Exemple:
În acest exemplu, avionul este considerat un container, pilotul găsindu-se înăuntru. Totuşi,
pilotul nu aparţine avionului, nu este o parte a sa, precum motorul. Dacă domeniul problemei şi
responsabilităţile sistemului trebuie să asigneze un pilot unui avion, atunci o clasă Pilot este
necesară.
15
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Exemplu:
Pentru fiecare obiect considerat întreg se verifică, pentru fiecare din părţile sale, dacă:
• acea parte aparţine domeniului problemei şi interesează sistemul din punct de vedere al
serviciilor. De exemplu, dacă domeniul problemei se referă la Serviciul meselor, atunci
partea Motor pentru întregul Avion nu are sens. Dacă însă domeniul problemei este
Transportul aerian, atunci ea are sens;
• acea parte capturează mai mult decât o singură valoare de stare. Dacă responsabilităţile
sistemului includ doar cunoştinţe despre starea avionului (funcţional sau nefuncţional) sau
starea motorului, atunci nu e necesară o clasă Motor. Este suficient să se prevadă un atribut
Stare în clasa Avion. Dacă însă sistemul trebuie să ştie mai mult despre motor (model,
număr de serie, data fabricaţiei etc.), atunci este necesară o clasă Motor.
Considerând apoi fiecare obiect ca o parte potenţială dintr-un întreg, se verifică utilitatea
prezenţei lui în acelaşi mod.
Un atribut este o proprietate, calitate sau caracteristică pentru care fiecare obiect al unei
clase are o anumită valoare. Atributele adaugă detalii abstractizărilor de tip clasă sau structură. Ele
descriu valori încapsulate în obiect şi vor fi manipulate exclusiv de către serviciile acelui obiect.
Atributele şi serviciile obiectului sunt tratate ca un întreg. Dacă o altă parte a sistemului doreşte să
acceseze atributele unui obiect, o poate face doar prin specificarea unui mesaj de conectare,
corespunzător serviciului definit de obiect. De-a lungul timpului, domeniul claselor rămâne relativ
stabil, în schimb, atributele sunt mult mai susceptibile de a se modifica.
16
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
• identificarea atributelor;
• poziţionarea atributelor;
• identificarea conexiunii instanţelor;
• verificarea cazurilor speciale;
• specificarea atributelor.
Fiecare atribut trebuie să captureze un concept atomic (o singură valoare sau un grup de
valori, având acelaşi înţeles, ca de exemplu nume, format din nume şi prenume, sau adresă,
compusă din stradă, număr, cod poştal, oraş, ţară).
Fiecare atribut va fi pus în clasa pe care o descrie mai bine. De exemplu, în sistemul de
înregistrare a unui vehicul, culoarea acestuia este foarte importantă. Ea este reţinută de sistem la
momentul înregistrării vehiculului. Este deci culoare un atribut al clasei EvenimentÎnregistrare sau
al clasei Vehicul? (Răspunsul corect este: Vehicul.)
Pentru clasele care prezintă o structură gen-spec, atributul se poziţionează în cel mai de sus
punct al structurii în care rămâne aplicabil pentru toate specializările sale. Dacă un atribut se aplică
pe un nivel întreg de specializări, atunci este mutat pe nivelul generalizator corespunzător.
17
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Conexiunea instanţelor e reprezentată printr-o linie care uneşte obiectele. Fiecare obiect are
o cantitate (m) sau interval (m,n), marcate pe conexiune, reflectând restricţiile conexiunilor cu alte
obiecte. Limitele intervalului au următoarele semnificaţii:
• Limita inferioară
o 0, dacă este o conexiune opţională;
o ≥ 1 , dacă e o conexiune obligatorie;
• Limita superioară
o 1, dacă este o singură conexiune;
o ≥ 1 , dacă sunt conexiuni multiple.
Pentru fiecare atribut se verifică dacă există cazuri când nu este aplicabil. De exemplu, clasa
Vehicul poate avea atributul Tracţiune cu valorile: benzină, motorină, electric etc. Dar pentru
anumite vehicule, care nu au motor, acest atribut poate avea valoarea „Inaplicabil”. Dacă există un
astfel de caz, trebuie reanalizată strategia gen-spec, verificând dacă nu mai sunt necesare structuri
gen-spec care n-au fost cuprinse în model.
În continuare, se verifică clasele care au un singur atribut. În acest caz, fie clasa are un
singur atribut pentru că aşa impun responsabilităţile sistemului (figura 12), fie atributul nu este bine
poziţionat, el aparţinând unei alte clase (figura 13). Verificând domeniul problemei, se constată că
în loc de două clase (Garanţie şi Adresă) se poate folosi doar una singură (Garanţie).
Figura 12. Plasarea unui singur atribut într-o clasă pe care o descrie
18
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Clasa Senzor
Unele atribute îşi schimbă foarte rar valoarea (Model, SecvenţeInit, Conversie), altele şi-o
schimbă mai des (Interval, Adresă, Prag, Stare), iar altele sunt dinamice (Valoare). Atributul
Valoare este rezultatul citirii unei valori în unităţi fizice (de exemplu volţi) şi al conversiei sale în
unităţi standard de măsură. Poate fi tratată ca o valoare recalculabilă şi în acest caz nu e nevoie de
un atribut Valoare. Dar e posibil ca sistemul să necesite cunoaşterea acestei valori în orice moment,
indiferent de starea senzorului (chiar dacă senzorul este în starea standby, în care valoarea nu poate
fi citită şi deci calculată). Este posibil deci, ca atributul Valoare să nu poată fi recalculat de fiecare
dată şi atunci se justifică prezenţa sa.
19
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Exemplu: care din atributele sistemului Senzor reflectă o schimbare în starea obiectului?
Atributele sistemului sunt cele prezentate mai sus. Pentru acest sistem valorile atributelor Model,
SecvenţaInit, Conversie, Interval, Adresă, Prag nu implică o modificare a comportării sistemului.
Însă atributul Stare reflectă o schimbare în comportarea sistemului. Valorile sale sunt: on, off şi
standby. Diagramele stărilor obiectelor prezintă diferitele stări ale sistemului în timp.
Notaţia pentru diagramele stărilor obiectelor este următoarea:
De exemplu:
În acest exemplu, săgeata din vârf indică starea iniţialã. Sunt prezentate doar stările şi
tranziţiile legale.
Apoi se trece la identificarea serviciilor propriu-zise. Există două tipuri de servicii:
• servicii algoritmic-simple;
• servicii algoritmic-complexe.
20
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Notaţie:
Interacţiunile dintre factorul uman şi sistem sunt şi ele reprezentate în modelul AOO:
Pentru a evidenţia conexiunile prin mesaje între obiecte, se pun următoarele întrebări pentru
fiecare obiect:
• de serviciile căror alte obiecte are nevoie? Se trasează câte o săgeată către fiecare din aceste
obiecte;
• ce alte obiecte au nevoie de serviciile lui ? Se trasează câte o săgeată dinspre fiecare din aceste
obiecte;
• se urmăreşte conexiunea prin mesaj spre obiectul destinatar şi se repetă aceleaşi întrebări;
• se examinează rezultatele AOO anterioare.
21
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
În AOO, termenul de subiect este un mecanism pentru ghidarea cititorului (analist, manager,
expert, utilizator, client) în înţelegerea unui model de analiză foarte mare şi complex. Subiectele
oferă o imagine de perspectivă asupra unui model complex.
Fiecare clasă din vârful fiecărei structuri se asignează unui subiect. Fiecare clasă care nu
face parte din nici o structură se asignează, de asemenea, unui subiect. Se recomandă studiul
rezultatelor anterioare ale AOO pentru probleme similare pentru a se utiliza subiecte identificate
deja.
Subiectele se rafinează apoi utilizând subdomeniile problemei. De fapt, se aplică principiul
întreg-parte pe domeniul problemei. Rafinarea ia în consideraţie interdependenţele şi interacţiunile
minimale între subiecte. Interdependenţele sunt exprimate de structuri atribute, iar interacţiunile
sunt exprimate de servicii.
Un subiect se reprezintă printr-un dreptunghi, fiind etichetat în interior cu un nume şi un
număr şi conţinând şi lista claselor care fac parte din acel subiect. Practic, pentru modele foarte
complexe, subiectele se pot reprezenta în trei moduri:
1. Subiect 1
2. Subiect 2
1. Subiect 1
Clasa1
Clasa2
2. Subiect 2
Clasa3
Clasa4
• Subiecte total expandate, când se reprezintă împreună cu alte straturi ale modelului AOO,
prin zone partiţionate şi numerotate. În interiorul dreptunghiurilor numerotate vor fi incluse
clasele cu notaţia completă: nume, atribute, servicii, eventual şi cu legăturile dintre ele:
22
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
1 1 2 2
1 1 2 2
O clasă poate face parte din mai multe subiecte, când acest lucru este necesar pentru
ghidarea cititorului. Subiectele pot conţine la rândul lor alte subiecte, furnizând astfel o hartă
multinivel. În general, subiectele sunt necesare pentru modele relativ mari, având aproximativ
30-40 de clase. Identificarea subiectelor se va face abia după ce clasele au fost identificate şi bine
înţelese.
4. Concluzii
În acest curs au fost prezentate mai întâi chestiuni legate de ingineria cerinţelor: definirea
cerinţei, extragerea, identificarea şi specificarea cerinţelor. Apoi a fost descrisă problematica
generală a analizei orientate obiect, insistându-se pe metoda de analiză Coad-Yourdon şi pe
activităţile ei specifice: identificarea claselor şi obiectelor, a structurilor, atributelor, serviciilor şi
subiectelor.
23
Ingineria programãrii
Cursul 10
Acest proces se repetã pentru fiecare subsistem pânã când componentele identificate pot fi
mapate direct în componentele limbajului de programare.
Rezultatul principal a fazei de proiectare este un model al codului care aratã cum este
implementat sistemul ºi o diagramã a dependenþelor dintre module, care aratã cum va fi sistemul
divizat în module ºi cum interacþioneazã acestea. Pentru module mai dificile, se includ specificaþii
speciale.
Ce înseamnã o proiectare corectã? Desigur, nu existã o modalitate simplã ºi obiectivã care
sã spunã cã o proiectare este mai bunã decât alta. Dar existã unele proprietãþi cheie care pot fi
utilizate pentru a mãsura calitatea proiectãrii. În mod ideal, o proiectare ar trebui sã aibã toate aceste
caracteristici. În practicã, trebuie fãcute compromisuri între ele. Aceste caracteristici sunt
extensibilitatea, siguranþa ºi eficienþa.
2.1. Extensibilitatea
Proiectarea trebuie sã poatã suporta noi funcþii. Un sistem perfect din toate celelalte puncte
de vedere, dar în care nu poate fi integratã nici o schimbare sau îmbunãtãþire, nu este de prea mare
folos. Chiar dacã nu existã cerinþe pentru trãsãturi suplimentare, probabil vor exista schimbãri în
domeniul problemei care vor necesita schimbãri în program.
Modelul problemei trebuie sã reflecte caracteristicile generale ale problemei. Un obstacol
des întâlnit atunci când se doreºte extinderea unui sistem este faptul cã anumite noþiuni nu sunt
exprimate în cod ºi deci funcþionalitãþile corespunzãtoare care trebuie adãugate nu-ºi gãsesc locul în
program. Un exemplu în acest sens poate fi vãzut în Microsoft Word. Acesta a fost proiectat
plecând de la premisa cã structura fundamentalã de organizare a unui document este paragraful. De
aceea, structurile ierarhice pot fi introduse cu greutate ºi, ca efect, divizarea documentului în
secþiuni nu se face foarte eficient. Optimizarea modelului nu trebuie sã elimine substructurile care
numai par nefolositoare. Abstractizãrile nu trebuie sã înlocuiascã structurile concrete decât dupã o
analizã serioasã.
Chiar dacã programul cuprinde noþiuni generale pe baza cãrora sã poatã fi adãugate noi
funcþionalitãþi, acestea trebuie introduse fãrã a modifica tot codul. De aceea, proiectarea trebuie sã
fie localizatã, adicã problemele diferite trebuie separate în regiuni distincte ale codului. Modulele
trebuie decuplate cât mai mult, astfel încât o schimbare sã nu antreneze modificãri în cascadã.
2.2. Siguranþa
Sistemul trebuie sã aibã un comportament sigur, care nu presupune doar lipsa „prãbuºirilor”
sau a pierderii datelor, ci ºi faptul cã trebuie sã ruleze corect, aºa cum se aºteaptã utilizatorul. Nu e
suficient ca sistemul sã îndeplineascã o specificaþie obscurã, ci trebuie sã îndeplineascã o
specificaþie care poate fi înþeleasã uºor de utilizator, astfel încât acesta sã poatã prezice
comportamentul sistemului. Pentru sistemele distribuite, este importantã disponibilitatea. Pentru
sisteme de timp real, este importantã sincronizarea. Criteriile de stabilire a siguranþei variazã în
funcþie de tipul aplicaþiei. Conexiunile telefonice automate trebuie sã fie în permanenþã disponibile
ºi sã fie foarte sigure, uneori pot fi supraîncãrcate, iar alte ori mai au loc rutãri eronate de mesaje.
Întârzieri mici într-un program de trimitere a email-urilor nu prea conteazã, însã aceleaºi întârzieri
se pot dovedi catastrofale pentru un controler de reactor nuclear.
Modelarea atentã. Siguranþa nu poate fi introdusã uºor într-un sistem existent. Cheia
realizãrii de produse sigure este modelarea ºi dezvoltarea atentã. Problemele serioase în sistemele
critice în general nu apar din erori de programare, ci din erori de analizã a problemei. Programatorii
2
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
pur ºi simplu nu iau în calcul o anumitã proprietate a mediului în care este plasat sistemul. De
exemplu, incidentul din 1993 când un avion Airbus a încercat sã aterizeze pe timp de furtunã pe
aeroportul din Varºovia, însã frânele nu au funcþionat, avionul a ieºit de pe pistã ºi a luat foc.
Analiza ºi testarea. Oricât de atent ar fi cineva, este foarte probabil cã va face pânã la urmã
greºeli. Din punct de vedere al costului, cea mai bunã soluþie este verificarea codului de cãtre altã
persoanã. O testare mai detaliatã ºi care poate descoperi erori mai subtile poate fi realizatã cu un
instrument automat.
2.3. Eficienþa
Dupã cum ºtim, faza de proiectare se aflã situatã între analizã ºi implementare. Toate aceste
faze fac parte din procesul de dezvoltare orientatã obiect. Aºa cum am vãzut în cursul precedent,
analiza orientatã obiect se referã la dezvoltarea unui model orientat obiect al domeniului aplicaþiei.
Obiectele identificate reflectã entitãþi ºi operaþii asociate cu problema care trebuie rezolvatã.
Proiectarea orientatã obiect priveºte dezvoltarea unui model orientat obiect al sistemului software
care trebuie sã implementeze cerinþele identificate. Programarea orientatã obiect urmãreºte
implementarea (punerea în practicã a) proiectãrii software folosind un anumit limbaj de programare
orientat obiect.
Continuând faza de analizã orientatã obiect, proiectarea orientatã obiect este o strategie în
care sistemul se gândeºte în termeni de „obiecte”, în loc de operaþii ºi funcþii. Programul nu este
proiectat ca o mulþime de funcþii care schimbã date prin parametri ºi memorie comunã (variabile
globale), ci ca o mulþime de obiecte care interacþioneazã. Obiectele îºi pãstreazã starea internã ºi îºi
definesc operaþiile pe baza acesteia. Prin ascunderea informaþiilor despre reprezentarea stãrii, ei
limiteazã accesul exterior la starea internã.
3
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
a) sistemul este proiectat ca o mulþime de obiecte care interacþioneazã, îºi gestioneazã propria
stare internã ºi oferã servicii altor obiecte;
b) obiectele sunt create prin instanþierea unei clase care defineºte atributele ºi operaþiile
asociate obiectului; mai multe obiecte ale aceleiaºi clase pot coexista în acelaºi program;
c) clasele sunt abstractizãri ale lumii reale sau entitãþi care încapsuleazã informaþii de stare ºi
care definesc un numãr de servicii care creeazã, acceseazã sau modificã starea;
d) obiectele sunt entitãþi independente în care reprezentarea stãrii poate fi modificatã fãrã a
afecta alte clase din sistem;
e) funcþionalitatea sistemului este datã de serviciile asociate fiecãrui obiect; obiectele
interacþioneazã prin apelarea serviciilor definite de alte obiecte;
f) nu existã zone de memorie comunã; obiectele comunicã prin apeluri de servicii ºi nu prin
variabile partajate; nu existã posibilitatea ca o componentã sã fie afectatã de schimbarea
unei informaþii partajate ºi deci modificãrile sunt mai uºor de implementat;
g) obiectele pot fi distribuite ºi pot fi executate secvenþial sau paralel; deciziile asupra
paralelismului nu trebuie luate încã de la începutul procesului de proiectare.
Aceste etape nu trebuie gândite într-o ordine strictã, ele se întrepãtrund ºi se influenþeazã
reciproc. Pe mãsurã ce sunt realizate modelele obiect, activitãþile de mai sus modificã treptat
arhitectura sistemului. Proiectarea nu este un proces simplu ºi bine structurat. În realitate, se propun
ºi se rafineazã soluþii pe mãsurã ce se adunã mai multe informaþii, se revine asupra unor decizii dacã
acestea se dovedesc greºite; uneori se exploreazã în detaliu toate opþiunile, alteori detaliile sunt
ignorate pentru a fi considerate mai târziu în cadrul procesului.
Prima etapã a procesului de proiectare software este înþelegerea relaþiilor dintre sistemul
proiectat ºi mediul sãu extern. Aceasta va determina alegerea funcþionalitãþilor necesare ºi
structurarea lor astfel încât sistemul sã poatã comunica eficient cu mediul. Contextul sistemului ºi
modelul de utilizare a sistemului reprezintã douã modele complementare ale relaþiei dintre sistem ºi
mediu:
contextul sistemului este un model static care descrie celelalte sisteme din mediu;
modelul de utilizare a sistemului este un model dinamic care descrie interacþiunea dintre
sistem ºi mediu.
4
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Dupã ce au fost definite interacþiunile dintre sistem ºi mediu, aceste informaþii pot fi
utilizate ca bazã pentru proiectarea arhitecturii sistemului. Desigur, informaþiile din etapa
precedentã trebuie combinate cu unele cunoºtinþe generale despre principiile proiectãrii arhitecturii
ºi cu unele cunoºtinþe detaliate despre domeniul problemei.
O regulã euristicã spune cã într-un model de arhitecturã nu trebuie incluse mai mult de
ºapte entitãþi fundamentale. Fiecare din aceste entitãþi poate fi apoi descrisã separat, dar, bineînþeles,
depinde de proiectant dacã vrea sã prezinte în arhitecturã structura tuturor entitãþilor sistemului.
De exemplu, arhitectura unui agent de corectare lexicalã (spell-checker) poate fi urmãtoarea:
În acest stadiu al proiectãrii, deja s-au conturat unele idei despre obiectele esenþiale pentru
sistem. Obiectele ºi clasele identificate în faza de analizã sunt verificate ºi rafinate pentru a se
potrivi în programul care urmeazã a fi realizat.
O tehnicã de identificare a obiectelor este utilizarea unei abordãri comportamentale.
Proiectantul încearcã sã înþeleagã mai întâi comportamentul general al sistemului.
Comportamentelor diferite le corespund pãrþi diferite din sistem. Mai trebuie vãzut cine iniþiazã ºi
participã în aceste comportamente. Participanþii cu rol semnificativ sunt recunoscuþi drept obiecte.
Altã tehnicã se bazeazã pe analiza scenariilor. Diferitele scenarii ale utilizãrii sistemului sunt
identificate ºi analizate pe rând. Pentru fiecare scenariu se identificã obiectele necesare, împreunã
cu atributele ºi serviciile acestora.
5
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
modelele statice, care descriu structura staticã a sistemului în termenii claselor sistemului ºi
ai relaþiilor dintre ele; relaþiile importante care trebuie documentate în aceastã etapã sunt:
relaþiile de generalizare, relaþiile foloseºte/este folosit de, relaþiile de compunere etc.;
modelele dinamice, care descriu structura dinamicã a sistemului, cu interacþiunile dintre
obiectele sistemului (nu dintre clase); interacþiunile care trebuie documentate aici sunt
serviciile solicitate de obiecte ºi modul în care starea sistemului este influenþatã de acestea.
Fiecare componentã este construitã din clase ºi obiecte. Componenta domeniului problemei
se bazeazã pe modelul logic construit în timpul analizei orientate obiect. Dacã sistemul va fi
implementat într-un limbaj de programare orientat obiect, atunci va exista o corespondenþã de 1 la 1
între clasele ºi obiectele domeniului problemei. Componenta interacþiunii cu factorul uman
coordoneazã mesajele dinspre ºi înspre utilizator. Componenta coordonãrii task-urilor se referã la
multiplele fire de execuþie existente într-un sistem. Componenta coordonãrii datelor furnizeazã
infrastructura pentru înregistrarea ºi regãsirea datelor.
6
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
În aceastã primã activitate, componentele domeniului rezultate din analizã sunt utilizate ºi
suplimentate. Deoarece se bazeazã pe reutilizarea claselor identificate de faza de analizã,
modificãrile trebuie menþinute pe cât posibil la un nivel redus.
Apoi clasele specifice domeniului problemei sunt grupate împreunã prin adãugarea unei
clase rãdãcinã. Vizând direct limbajul de programare care va fi utilizat pentru implementare, poate
fi necesarã modificarea structurilor gen-spec pentru a se potrivi nivelului de moºtenire oferit de
limbajul ales.
Dacã structurile gen-spec ale modelului analizei orientate obiect includ moºteniri multiple,
trebuie fãcute câteva modificãri ale acestora atunci când se va utiliza un limbaj de programare
orientat obiect care nu suportã mecanismul moºtenirii sau care nu suportã decât moºtenirea simplã.
Douã tipuri de moºtenire multiplã sunt prezentate în figurile 2 ºi 3.
7
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Chiar dacã limbajul permite moºtenire multiplã (de exemplu C++), se recomandã evitarea
acesteia datoritã complexitãþii pe care o presupune. În cazul limbajelor care suportã doar moºtenire
simplã (Java, C#) se pot aplica metode de a transforma o moºtenire multiplã într-o moºtenire
simplã:
Figura 4. Formarea unor ierarhii separate, mapate între ele prin structuri întreg-parte
8
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Ierarhia multiplã poate fi transformatã direct într-o ierarhie simplã, caz în care anumite
atribute ºi servicii vor fi repetate în clasele specializate.
nivel de pregãtire;
nivel de organizare;
apartenenþa la diferite grupuri (de exemplu: funcþionar sau client).
9
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
ce este;
scopul;
caracteristici (de exemplu: vârstã, educaþie etc.);
factori critici de succes (necesitãþi/dorinþe, preferinþe/antipatii/tendinþe subiective);
nivel de pregãtire;
scenarii ale task-urilor.
Apoi trebuie proiectatã o ierarhie de comenzi pentru sistem prin studierea sistemelor
existente de interacþiune cu factorul uman. Pentru rafinarea ierarhiei, trebuie considerate
urmãtoarele:
ordonarea serviciilor;
partiþionarea modelelor întreg-parte ºi verificarea dimensiunilor structurii de comenzi
(lãþime: numãrul de categorii de opþiuni, adâncime: numãrul de nivele pentru fiecare
categorie de opþiuni);
minimizarea numãrului de paºi necesari pentru efectuarea unui serviciu.
consistenþa;
minimizarea numãrului de paºi;
acordarea de feedback prompt ºi semnificativ utilizatorilor;
asigurarea funcþiilor „undo”;
minimizarea rolului capacitãþii de memorare a utilizatorilor pentru reamintirea opþiunilor;
minimizarea timpului de învãþare ºi a efortului;
proiectarea esteticã a sistemului (este important ca utilizatorilor sã le placã sistemul).
Mai întâi trebuie vãzut dacã e nevoie de task-uri în sistem. Urmãtoarele tipuri de sisteme
necesitã task-uri:
10
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Când sunt necesare trei sau mai multe task-uri, se recomandã adãugarea unui task special de
coordonare. Dupã identificarea task-urilor, necesitatea lor trebuie reverificatã. Pentru fiecare task,
trebuie specificat:
ce este task-ul;
cum este coordonat task-ul;
cum comunicã task-ul.
Mai întâi trebuie selectatã o abordare pentru coordonarea datelor: fiºiere simple sau un
sistem de gestionare a bazelor de date, pe baza unor criterii potrivite. Apoi, în funcþie de abordarea
aleasã, trebuie proiectatã componenta de coordonare a datelor, adicã a formatului de date ºi a
serviciilor corespunzãtoare.
6. Concluzii
11
Ingineria programãrii
Cursurile 11-12
1. Limbaje de modelare
Acest model face câteva simplificãri evidente. Forma corpului este desenatã ca un pãtrat.
Diverse alte caracteristici sau interacþiuni cu mediul sunt ignorate. Pe de altã parte, viteza ºi forþa
sunt reprezentate prin vectori. Viteza este notatã cu v, forþa cu F, masa cu m. Toate aceste elemente
formeazã un model. Pentru un iniþiat, desenul de mai sus este consistent, expresiv ºi compact. O
descriere alternativã, sub forma de text, deºi posibil mai exactã, ar fi mai greoaie:
„fie un corp de masã m asupra cãruia acþioneazã o forþã F cu punctul de aplicaþie în centrul sãu de
greutate. Corpul se deplaseazã cu o vitezã v perpendicularã pe direcþia forþei”.
Limbajul natural pare sã fie cel mai la îndemânã limbaj de modelare. Experienþa aratã însã
cã folosirea sa induce adesea neclaritãþi ºi inconsistenþe. Apare astfel necesitatea definirii unui
limbaj neambiguu pentru specificarea modelelor. Se convine asupra unui set de elemente ale
limbajului precum ºi asupra semanticii acestora. Evident, descrierea elementelor ºi a semanticii se
face în ultimã instanþã în limbaj natural, deci pot apãrea aici unele ambiguitãþi. În acest caz însã,
limbajul natural este folosit numai într-o micã parte a sistemului iar problemele de semanticã pot fi
localizate. Eliminarea ambiguitãþilor din modele poate fi fãcutã îmbunãtãþind precizia limbajului de
modelare ºi nu cãutând erori de semanticã în întreaga descriere a modelului.
2. Ce este UML?
Limbajul unificat de modelare (engl. „Unified Modeling Language”), UML, este un limbaj
pentru specificarea, vizualizarea, construirea ºi documentarea elementelor sistemelor software, însã
poate fi folosit ºi pentru alte sisteme, cum ar fi cele de modelare a afacerilor. UML reprezintã o
colecþie de practici inginereºti optime, care au fost încununate de succes în modelarea sistemelor
mari ºi complexe.
2
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Dezvoltarea unui model pentru sisteme software industriale înainte de începerea construcþiei
efective este esenþialã. Modelele bune sunt absolut necesare pentru comunicarea dintre echipele
care lucreazã la acelaºi proiect ºi pentru asigurarea soliditãþii arhitecturale. Odatã cu creºterea
complexitãþii sistemului, creºte ºi importanþa unor tehnici potrivite de modelare. Existã mulþi factori
suplimentari pentru succesul unui proiect, dar un factor esenþial este respectarea riguroasã a
standardelor cu ajutorul unui limbaj de modelare.
UML nu garanteazã succesul proiectului, dar perfecþioneazã multe lucruri. De exemplu,
scade în mod semnificativ costul instruirii în cazul schimbãrilor legate de proiecte sau organizaþii.
Limbajul asigurã posibilitatea integrãrii instrumentelor, proceselor ºi domeniilor. Însã mai
important este faptul cã asigurã dezvoltatorilor un mod general de rezolvare a problemelor de
concepþie ºi planificare.
Mai înainte de UML, nu exista un limbaj clar ºi standardizat de modelare. Utilizatorii erau
nevoiþi sã aleagã unul dintre multele limbaje similare, cu diferenþe minore asupra puterii de
expresie. Cele mai multe limbaje împãrtãºeau o serie de concepte unanim recunoscute, care erau
exprimate uºor diferit prin diverse notaþii. Aceste diferenþe au fragmentat industria orientatã obiect
ºi au descurajat noii utilizatori sã înveþe modelarea vizualã. De fapt, utilizatorii doreau un limbaj
standardizat, o lingua franca a modelãrii.
Între 1989 ºi 1994 erau folosite mai mult de 50 de limbaje de modelare software, fiecare cu
propriile notaþii. Fiecare limbaje avea elemente de sintaxã specifice ºi în acelaºi timp elemente
comune cu alte limbaje. Mai mult, nici un limbaj nu era complet, inginerii software apelând deseori
la mai multe limbaje.
La mijlocul anilor ’90 trei metode s-au dovedit mai eficiente:
Booch: potrivitã mai ales pentru proiectare ºi implementare, cu dezavantajul unor notaþii
complicate;
OMT (Object Modeling Technique): potrivitã pentru analizã ºi sisteme informaþionale cu
multe date;
OOSE (Object Oriented Software Engineering): aceastã metodã a propus aºa-numitele
cazuri de utilizare, care ajutau la înþelegerea comportamentului întregului sistem.
În 1994, Jim Rumbaugh, creatorul OMT, a uimit întreaga comunitate software când a pãrãsit
General Electric, alãturându-se lui Grady Booch la Rational Corp. Scopul parteneriatului era
combinarea ambelor perspective într-o metodã unificatã. În 1995 ºi Ivar Jacobson, creatorul OOSE,
a venit la Rational, iar ideile lui (în special conceptul de cazuri de utilizare) au fost adãugate
„Metodei unificate”; metoda rezultantã a fost numitã „Limbajul de modelare
unificatã” – UML. În ciuda disputelor iniþiale, noua metodã a început sã aibã din ce în ce mai mulþi
susþinãtori în industria software, formându-se un consorþiu UML din care fãceau parte giganþi
precum Hewlett-Packard, Microsoft ºi Oracle.
UML 1.0 a fost propus spre standardizare în cadrul OMG (Object Management Group) în
ianuarie 1997. Pânã la sfârºitul anului 1997 echipa care lucra la UML s-a extins, urmând o perioadã
în care UML a primit o specificare formalã mai riguroasã. Versiunea UML 1.1 a fost adoptatã ca
standard de catre OMG în noiembrie 1997. În martie 2003 a fost publicatã versiunea 1.5. În
momentul de faþã se lucreazã la versiunea 2.0.
În UML existã numeroase diagrame (modele), aceasta favorizând existenþa mai multor
puncte de vedere privind sistemul. Dupã cum am vãzut, procesul de dezvoltare software are multe
componente, fiecare cu propria sa perspectivã: analiºti, proiectanþi, programatori, testeri, echipe de
asigurarea calitãþii, autori ai documentaþiei, clienþi. Fiecare dintre aceºti este interesat de un alt
aspect al sistemului, la un nivel diferit de detaliu. De exemplu, programatorul trebuie sã înþeleagã
arhitectura sistemului pentru o converti în cod de nivel scãzut. Dimpotrivã, autorul documentaþiei
trebuie sã înþeleagã comportamentul global al sistemului pentru a ºti cum funcþioneazã produsul.
UML încearcã sã rezolve problema modelãrii la toate aceste nivele de detaliu.
3
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Un caz de utilizare trebuie sã aibã un iniþiator al acþiunii, numit actor. În cazul unui sistem
bancar, retragerea banilor este fãcutã de clienþi, astfel încât clientul devine unul din actori:
Actorii nu sunt numai oameni, ci orice cauzã externã care iniþiazã un caz de utilizare, de
exemplu un alt sistem de calcul sau un concept mai abstract, precum timpul sau o anumitã datã
calendaristicã: în ultima zi a lunii se actualizeazã registrele de salarii. Pentru majoritatea sistemelor,
un anumit actor poate interacþiona cu mai multe cazuri de utilizare, iar un anumit caz de utilizare
poate fi iniþiat de actori diferiþi:
Deºi par foarte simple, ignorarea cazurilor de utilizare este o mare greºealã. Acestea sunt
foarte importante deoarece:
sunt similare cerinþelor, dar cazurile de utilizare sunt mai clare ºi mai precise datoritã
structurii riguroase de notaþie;
suma cazurilor de utilizare este sistemul ca întreg; ceea ce nu este acoperit de un caz de
utilizare se situeazã în afara sistemului de construit;
permit comunicarea dintre client ºi dezvoltatori, de vreme ce diagrama este foarte simplã ºi
poate fi înþeleasã de oricine;
ghideazã echipele de dezvoltare în procesul de dezvoltare;
ajutã echipele de testare ºi autorii manualelor de utilizare.
Dacã pentru un sistem suficient de complex am urma aceastã strategie, ar rezulta un numãr
imens de cazuri de utilizare, care nu ºi-ar mai servi practic scopul de descriere a comportamentului
sistemului la nivel înalt.
Când se ia decizia includerii unui nou caz de utilizare, trebuie sã se respecte urmãtoarea
regulã euristicã: un caz de utilizare trebuie sã satisfacã un scop pentru actor.
În exemplul anterior, se poate pune problema: preluarea chitanþei este un scop al clientului?
Nu neapãrat. La fel se procedeazã ºi pentru celelalte cazuri de utilizare. Nici unul nu descrie
suficient de corect scopul clientului, care este de fapt retragerea unei sume de bani.
5
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
4.1. Asocierea
Urmãtorul pas este definirea relaþiilor dintre concepte. Sã presupunem urmãtoarele douã
concepte:
Dacã fiecare manager conduce o maºinã în compania respectivã, între aceste concepte existã
o relaþie:
Linia simplã în UML are rolul de asociere. Numerele descriu cardinalitatea asocierii, adicã
ne spun câte instanþe sunt permise din fiecare concept. Urmãtoarea figurã prezintã câteva
cardinalitãþi posibile, deºi din punct de vedere al notaþiei nu existã restricþii asupra cardinalitãþilor
care pot fi specificate.
6
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
O greºealã care poate fi fãcutã în aceastã fazã este sã decidem cã existã o relaþie între douã
concepte, sã trasãm o linie între ele, dar sã nu notãm tipul de asociere. Dupã ce vom trasa toate
liniile nu vom mai ºti ce înseamnã fiecare ºi va trebui sã o luãm de la început.
În figura urmãtoare este prezentat un exemplu de asociere între clase:
4.2. Agregarea
Un aspect important al proiectãrii orientate obiect este agregarea, ideea cã un obiect poate fi
construit din altele. De exemplu, un calculator este o agregare între procesor, placã video, placã de
sunet etc.
7
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
4.3. Compunerea
Compunerea este un concept similar cu agregarea, însã mai puternic deoarece implicã faptul
cã un întregul nu poate exista fãrã pãrþi. În exemplul de agregare de mai sus, dacã se înlãturã placa
de sunet, calculatorul rãmâne calculator. Însã o carte nu poate exista fãrã pagini; o carte este
compusã din pagini. Notaþia este asemãnãtoare, dar rombul este plin:
4.5. Moºtenirea
De multe ori, mai multe clase din arhitecturã au atribute ºi operaþii comune. Acestea pot fi
introduce într-o singurã clasã ºi moºtenite în celelalte. De exemplu:
8
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Dacã am mai vrea sã adãugãm o clasã Pisicã, ar trebui sã repetãm atributele ºi operaþiile
comune. Soluþia este moºtenirea dintr-o clasã mai generalã. Notaþia UML pentru moºtenire este
urmãtoarea:
Trebuie sã subliniem cã atributul vârstã a fost transformat din privat în protejat, pentru a
putea fi moºtenit în clasele derivate.
O greºealã frecventã în proiectarea orientatã obiect este utilizarea abuzivã a moºtenirii, ceea
ce conduce la probleme în întreþinerea programului. Dacã mai multe clase sunt legate de una
singurã, schimbãrile în clasa de bazã vor afecta ºi clasele derivate. De asemenea, când derivãm o
clasã trebuie sã parcurgem întreaga ierarhie pentru a vedea ce face implicit clasa respectivã. Aceastã
problemã este cunoscutã sub denumirea de proliferarea claselor.
Moºtenirea nu trebuie folositã decât ca mecanism de generalizare, adicã se foloseºte numai
dacã clasele derivate sunt specializãri ale clasei de bazã.
De asemenea, toate definiþiile clasei de bazã trebuie sã se aplice tuturor claselor derivate.
Aceasta este regula 100%. Dacã nu se aplicã aceastã regulã, clasele derivate nu sunt specializãri ale
clasei de bazã. Un exemplu de derivare greºitã este urmãtorul:
9
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
4.6. Polimorfismul
Clasele derivate pot redefini implementarea unei metode. În exemplul urmãtor, clasele Pian
ºi Vioarã sunt derivate din Instrument. Totuºi, fiecare implementeazã metoda cântã în felul sãu
specific. Notaþia în acest caz este repetarea numelui metodei în fiecare clasã.
De multe ori avem nevoie sã lãsãm o metodã neimplementatã într-o clasã (metodã abstractã
sau virtualã), pe care sã o implementãm pe un nivel mai de jos al ierarhiei. O metodã abstractã se
noteazã cu italice.
4.7. Interfeþe
Dacã o clasã implementeazã o interfaþã, între cele douã existã o relaþie de realizare. Sã
presupunem cã Instrument din exemplul precedent e acum o interfaþã iar clasele Pian ºi Vioarã
trebuie sã implementeze metoda cântã. Notaþia este asemãnãtoare celei de la moºtenire, dar cu linie
punctatã, iar interfaþa e declaratã explicit. Cuvintele introduse între „<<” ºi „>>” se numesc
stereotipuri. În figura 20 se poate observa stereotipul UML <<interface>>.
10
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
5. Diagrame de interacþiune
11
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
12
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
În exemplul de mai sus, asteriscul semnificã posibilitatea mai multor interogãri SQL ºi a mai
multor rezultate.
6. Diagrame de activitãþi
Diagramele de activitãþi sunt folosite pentru modelarea proceselor sau a algoritmilor din
spatele unui anumit caz de utilizare. Din multe puncte de vedere, diagrama de activitãþi din UML
este echivalentul orientat obiect al diagramei fluxurilor de date din dezvoltarea structuratã.
Notaþia este urmãtoarea:
nod iniþial: un cerc plin este punctul de start al diagramei; deºi nu este obligatoriu, prezenþa
sa face diagrama mai lizibilã;
nod final: un cerc plin înconjurat de un alt cerc; o diagramã poate avea 0, 1 sau mai multe
noduri finale;
activitate: dreptunghiurile rotunjite reprezintã activitãþile care au loc;
fluxuri: sãgeþile diagramei;
punct final al fluxului: un cerc cu un X în interior; indicã faptul cã procesul se opreºte în
acest punct;
ramificaþie (engl. „fork”): o barã neagrã cu un flux de intrare ºi mai multe fluxuri de ieºire;
denotã începutul unor activitãþi desfãºurate în paralel;
reunire (engl. „join”): o barã neagrã cu mai multe fluxuri de intrare ºi un flux de ieºire;
denotã sfârºitul prelucrãrilor paralele;
condiþie: text asociat unui flux, care defineºte o condiþie care trebuie sã fie adevãratã pentru
traversarea nodului;
decizie: un romb cu un flux de intrare ºi mai multe fluxuri de ieºire; fluxurile de ieºire
includ condiþii;
îmbinare (engl. „merge”): un romb cu mai multe fluxuri de intrare ºi un flux de ieºire; toate
fluxurile de intrare trebuie sã atingã acest punct pentru ca procesul sã continue;
partiþie (engl. „swimlanes”): o parte a diagramei care indicã cine/ce îndeplineºte activitãþile;
notã: o specificaþie suplimentarã sub formã de text.
13
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
14
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
15
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
7. Diagrame de stãri
Obiectele au atât comportament, cât ºi stare internã, cu alte cuvinte, îndeplinesc acþiuni ºi
deþin informaþii. Unele obiecte au comportamente foarte complexe, care depind foarte mult de
starea internã. Pentru a le înþelege, dezvoltatorii utilizeazã diagramele de stãri, care descriu modul
de funcþionare a instanþelor.
Diagramele de stãri UML descriu diferitele stãri în care se poate gãsi un obiect ºi tranziþiile
dintre aceste stãri. O stare reprezintã o etapã în modelul comportamental al unui obiect ºi, le fel ca
în cazul diagramelor de activitãþi, este posibil sã avem stãri iniþiale ºi stãri finale. O stare iniþialã
este cea în care se gãseºte obiectul când este creat. O stare finalã este o stare din care nu mai existã
tranziþii. Tranziþia reprezintã schimbarea stãrii, trecerea dintr-o stare în alta, ºi poate fi determinatã
de un eveniment extern sau intern.
Figura urmãtoare prezintã un exemplu de diagramã de stare pentru înscrierea la un curs
opþional propus cu un numãr limitat de studenþi. Dreptunghiurile rotunjite reprezintã stãri: instanþele
clasei Curs pot fi în urmãtoarele stãri: Propus, Planificat, Disponibil pentru înscrieri, Ocupat,
Închis pentru înscrieri. Starea iniþialã este notatã tot printr-un cerc plin, iar starea finalã printr-un
cerc plin înconjurat de un alt cerc, la fel ca în diagramele de activitãþi, dovadã a consistenþei
limbajului UML.
În figura 28 putem observa cum stãrile din figura 27 se grupeazã într-o aºa numitã
superstare, pentru a putea descrie un sistem complex la un nivel superior de abstractizare.
16
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
8. Diagrama pachetelor
Entitãþile UML pot fi grupate în pachete – containere logice în care pot fi plasate elemente
înrudite, ca ºi directoarele din sistemele de operare. Deºi orice entitate UML poate fi introdusã
într-un pachet, de obicei rolul pachetelor este de a grupa clase ºi uneori cazuri de utilizare înrudite.
Într-un pachet UML numele elementelor trebuie sã fie unice. Totuºi, un avantaj important al
pachetelor este cã mai multe clase pot avea acelaºi nume dacã aparþin unor pachete diferite. Dacã
douã echipe A ºi B lucreazã în paralel, echipa A nu va trebui sã se preocupe de conþinutul pachetului
echipei B, cel puþin din punctul de vedere al denumirilor. Aºadar, utilitatea pachetelor apare
deoarece: elementele sistemelor mari pot fi grupate în subsisteme mai mici ºi este permisã
dezvoltarea iterativã în paralel.
La formarea pachetelor trebuie sã se þinã seama de urmãtoarele deziderate. Un pachet
trebuie sã aibã o funcþionalitate bine precizatã, el nu trebuie sã îndeplineascã funcþii multiple,
deoarece devine greu de înþeles. De asemenea, dependenþele dintre pachete trebuie sã fie minime.
17
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
9. Diagrame de implementare
9.1. Diagrama componentelor
18
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
19
Florin Leon – Ingineria programãrii, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
10. Concluzii
În aceste cursuri a fost mai întâi argumentatã nevoia de un sistem de notaþie standardizat
pentru etapele dezvoltãrii unui proiect software. Apoi au fost prezentate notaþiile principale ale
limbajului UML: diagramele de clase, de interacþiune, de activitãþi, de stãri, de pachete ºi de
implementare.
20
Ingineria programării
Cursul 13
Implementarea
1. Introducere
2. Limbaje de programare
2.1. Limbaje imperative
2.2. Limbaje declarative
3. Analiza unor limbaje de programare
3.1. C/C++
3.2. Basic
3.3. Pascal
3.4. Java
3.5. C#
4. Comparaţie între unele limbaje de programare
5. Utilitare pentru implementare şi testare
6. Concluzii
1. Introducere
Implementarea este faza în care este produs codul corespunzător proiectului furnizat de faza
anterioară, îndeplinind restricţiile de resurse, acurateţe şi performanţă indicate de specificaţii.
Procesul implementării este cel mai dificil de descris, nefiind riguros definit. Implementarea
este procesul transformării abstractizării prezentate în proiect într-o realizare fizică utilizând
limbajul arhitecturii ţintă. Este o fază concretizată într-o stare confuză, deseori haotică şi instabilă
pentru sistemele software complexe, în care coordonarea este dificilă.
O problemă majoră care cauzează instabilitatea constă în dificultatea translării proiectării în
cod sursă. În primul rând, de cele mai multe ori proiectarea nu va realiza o mapare de 1 la 1 către
implementare. De aceea, oricât de bun ar fi proiectul, este necesar un oarecare efort pentru a scrie
codul corespunzător, ori aceasta este o sursă de erori.
În al doilea rând, procesul de transformare proiect-implementare este şi mai dificil când
proiectul nu este complet, consistent sau nu comunică exact şi inteligibil ceea ce se doreşte din
partea sistemului. Erorile de proiectare determină pierderea timpului programatorilor în a rezolva
probleme greşite puse. Acestea sunt erorile de logică şi sunt cele mai frecvente. De aceea este foarte
importantă utilizarea unor metode riguroase pentru prezentarea proiectării.
În al treilea rând, unele aspecte sunt în afara domeniului proiectantului. Efectele exacte ale
utilizării unui anumit sistem de operare sau limbaj de programare sunt în afara scopului
proiectantului dar reprezintă o importantă decizie a programatorului.
În cele din urmă, implementarea însăşi este predispusă către erori, fiind un proces creator
uman. Limbajul de programare poate fi folosit incorect, aceasta însemnând că un anumit timp şi
efort se vor consuma pentru corectarea acestor erori. Din păcate, corectarea erorilor nu este o
sarcină uşoară. S-a constatat că un programator are 50% şanse să descopere eroarea într-un interval
de 5-10 linii de cod şi numai 20% şanse să o descopere într-un domeniu de 40-50 linii.
Documentele de bază produse în această fază sunt:
• codul sursă şi obiect comentate într-o formă standard sau respectând anumite convenţii;
• pliante (dosare) ale software-ului, prezentând modulele software individuale;
• manualul de utilizare a produsului software, prezentând convenţiile utilizate în programe, o
prezentare generală a implementării, descrierea particularităţilor;
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
2. Limbaje de programare
În faza de implementare se extinde mai întâi proiectul din faza anterioară la componentele
primitive ale sistemului. Se vor folosi aceleaşi metode din faza de proiectare (proiectarea
structurată, proiectarea orientată obiect, metode formale). Următorul pas este a defini prelucrarea
fiecărui modul prin metode ca: hărţi de fluxuri (engl. „flowcharts”), rafinarea pas cu pas, limbaje de
proiectare a programelor, pseudocodul, etc.
Implementarea implică scrierea codului într-un limbaj de programare, verificarea şi
integrarea sa cu alte programe pentru obţinerea unui sistem final.
O decizie foarte importantă este alegerea unui limbaj potrivit de programare. Din punct de
vedere al semanticii, următoarele două clase mari de limbaje sunt larg recunoscute:
• limbaje imperative (numite uneori şi procedurale): Fortran, Basic, Pascal, C/C++, Java, C#
• limbaje declarative: Lisp, Prolog, Clips.
2
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
• moştenirea: este tehnica prin care modulele pot prelua funcţionalităţi de la modulele de
nivel superior;
• polimorfismul: este abilitatea unui program de a lucra cu diferite tipuri de date sau de a
realiza acţiuni diferite pentru instanţe ale unor clase diferite. Ideal ar fi ca un limbaj OO să
fie complet polimorf, astfel încât să nu mai fie necesare porţiuni de cod pentru fiecare tip de
dată. Polimorfismul implică suportul pentru legare dinamică (engl. „dynamic binding”),
adică legarea metodelor obiectelor de selectarea mesajului în momentul execuţiei şi nu al
compilării;
• mesajele: mesajele sunt utilizate pentru implementarea interfeţelor. Un mesaj conţine
detaliile acţiunii care trebuie realizată şi este trimis de către un obiect către un alt obiect
pentru a invoca un serviciu al celui din urmă.
3.1. C/C++
C/C++ este folosit pe scară largă pentru programarea profesionistă. Acesta combină virtuţile
unui limbaj de nivel înalt cu eficienţa limbajului de asamblare. C a fost dezvoltat de Dennis Ritchie
în 1972 la AT&T's Bell Laboratories şi a fost folosit pentru scrierea sistemului de operare UNIX.
Datorită legilor antitrust, Laboratoarelor Bell li s-au interzis drepturile de autor asupra C-ului şi
UNIX-ului. De aceea, compilatoarele de C sunt în domeniul public şi au fost adoptate de
majoritatea universităţilor. Limbajul C a fost standardizat (ANSI C) în 1990. În 1981, Bjarne
Stroustrup a propus C++. C-ul originar este utilizat foarte rar, de aceea mulţi programatori se referă
la C++ cu termenul de „C”. C++ conţine toate elementele de bază ale C-ului, la care s-au adăugat
numeroase trăsături de programare orientată obiect. Şi C++ a fost standardizat (ISO C++) în 1997.
Avantaje:
• Eficienţă: C/C++ pot crea programe mai rapide şi cu dimensiuni mai mici decât aproape
orice alt limbaj de programare, cu excepţia limbajului de asamblare;
• Portabilitate: Un program scris în C/C++ poate fi uşor copiat şi compilat pe alt calculator,
cu unele modificări. Pentru majoritatea sistemelor de operare există compilatoare de C/C++;
• Flexibilitate: Pentru un programator experimentat, conversia liberă a tipurilor de date este
un avantaj. Pentru începători, totuşi, acest lucru poate genera confuzie şi erori;
• Număr mare de programatori: Acest limbaj este cunoscut de foarte mulţi programatori, care
ar putea modifica mai târziu un program existent.
3
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Dezavantaje:
• Dificil de stăpânit: C/C++ este unul din cele mai dificile limbaje de programare. În timpul
necesar învăţării complete a C/C++, un program poate fi deja terminat în alt limbaj;
• Complexitate: C/C++ are puterea de a manipula direct memoria şi hardware-ul
calculatorului. Acest lucru sporeşte şansele apariţiei unei erori şi timpul necesar pentru
debug;
• Dificil de citit şi înţeles: Programele sunt create o dată şi modificate de multe alte ori.
Datorită naturii criptice a C/C++, înţelegerea unui program poate ridica probleme.
Pentru a exemplifica ultima afirmaţie, oricât ar părea de ciudat, următorul program C este
corect sintactic:
#include <stdio.h>
#define O (b=b?b-1:(p++,5),*p&1<<b)
#define o O?O
char*p,j=2,b,c;e(n){for(p="|'8I0>+@{=#_P0-]PV.]F>TM!YK'?? |T\"Z8}aE<&D-!:-T'\"\
O<~cG5$,<2'#;/UI.0{d^HV6817-2F95-T7X|c^/1XB]*)3WHG0/0}dN>G RMZB.12.P] ~hM^J\\[\
<R^ (7;)R9A78{gU!:N)E5OPUR><29A6|e&9V;E[Q:,S1.P] }eES.$Z):B.*O+$G_ ~fWU8)75?I#\
75?WHN0{jE=]<V*1]JI#5VK)R9A6~J5X9X#69/+VX4 =S%!X-[)OE #1XRZ\"?~%^-#Dz&M\\RST|%\
G66*~&^HV0> {%^-8_P}%N>FO(}'M^JQ=z&U!:O(J{%&9G4|%ERO(~(WU8)G4{'E=]^G4",b=n;*p++
<122||--b;);c=*p;while(--c>31&&c!=79)putchar(44+(o?o?o?-34:68:O?60:74:O?64:o?o?
2:54:O?23:63:77:O?55:o?76:15:35:-12:o?61:O?56:65:O?66:53:o?o?O?75:58:0:70:57:o?
71:o?73:1:67:O?72:59));c>32?e(n-1):0;}main(){while(++j<15)e(1),e(13+j),e(15),e(
j-(j<4));}
On the first day of Christmas my true love gave to me and a partridge in a pear tree.
a partridge in a pear tree.
On the eighth day of Christmas my true love gave to
On the second day of Christmas my true love gave to me
me eight maids a-milking, seven swans a-swimming,
two turtle doves six geese a-laying, five golden rings;
and a partridge in a pear tree. four calling birds, three french hens, two turtle doves
and a partridge in a pear tree.
On the third day of Christmas my true love gave to me
three french hens, two turtle doves On the ninth day of Christmas my true love gave to me
and a partridge in a pear tree. nine ladies dancing, eight maids a-milking, seven
swans a-swimming,
On the fourth day of Christmas my true love gave to me six geese a-laying, five golden rings;
four calling birds, three french hens, two turtle doves four calling birds, three french hens, two turtle doves
and a partridge in a pear tree. and a partridge in a pear tree.
On the fifth day of Christmas my true love gave to me On the tenth day of Christmas my true love gave to me
five golden rings; ten lords a-leaping,
four calling birds, three french hens, two turtle doves nine ladies dancing, eight maids a-milking, seven
and a partridge in a pear tree. swans a-swimming,
six geese a-laying, five golden rings;
On the sixth day of Christmas my true love gave to me four calling birds, three french hens, two turtle doves
six geese a-laying, five golden rings; and a partridge in a pear tree.
four calling birds, three french hens, two turtle doves
and a partridge in a pear tree. On the eleventh day of Christmas my true love gave to
me
On the seventh day of Christmas my true love gave to eleven pipers piping, ten lords a-leaping,
me nine ladies dancing, eight maids a-milking, seven
seven swans a-swimming, swans a-swimming,
six geese a-laying, five golden rings; six geese a-laying, five golden rings;
four calling birds, three french hens, two turtle doves four calling birds, three french hens, two turtle doves
4
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
and a partridge in a pear tree. nine ladies dancing, eight maids a-milking, seven
swans a-swimming,
On the twelfth day of Christmas my true love gave to six geese a-laying, five golden rings;
me four calling birds, three french hens, two turtle doves
twelve drummers drumming, eleven pipers piping, ten and a partridge in a pear tree.
lords a-leaping,
3.2. Basic
Basic (Beginner's All Purpose Symbolic Instruction Code) a fost dezvoltat la Dartmouth
College în 1964 sub coordonarea lui J. Kemeny şi a lui T. Kurtz. Ideea era crearea unui limbaj
foarte simplu de învăţat care să servească drept treaptă intermediară pentru studenţii care învăţau
Fortran şi Algol. Basic a fost primul produs vândut de Microsoft şi primul caz major de piraterie
software: a fost copiat şi distribuit pe scară largă încă înainte de a fi lansat (Bill Gates a pierdut o
copie în timpul unei demonstraţii publice).
Fiind un limbaj de nivel înalt uşor de folosit, este foarte nimerit pentru a preda fundamentele
programării începătorilor şi a devenit un limbaj utilizat de mulţi amatori pentru realizarea de
programe simple. La început necesita un interpretor, astfel încât începătorii să poată crea programul
într-o manieră interactivă, să-l ruleze, testeze şi corecteze. Limbajele interpretate favorizează
învăţarea programării, dar rulează mult mai lent decât programele compilate. De aceea,
programatorii profesionişti le evită în general. Noile versiuni de Basic posedă compilatoare, cum ar
fi Visual Basic, în care pot fi create programe de calitate comercială. Şi Basic-ul a fost standardizat:
ANSI Minimal Basic (1978) şi ANSI Full Basic (1987).
Avantaje:
• Uşor de învăţat: Poate fi învăţat şi folosit mai repede decât majoritatea celorlalte limbaje;
• Permite prototipizare rapidă: În Visual Basic, prototipurile se pot crea repede. Apoi acestea
pot fi transformate în programe reale funcţionale. Alte limbaje, precum C/C++, sunt prea
greu de utilizat pentru a crea un prototip.
Dezavantaje:
• Lent: Programele scrise în Visual Basic în general rulează mult mai încet decât programele
echivalente în C/C++. Dacă viteza este o condiţie importantă pentru program, Visual Basic
nu este o alegere potrivită;
• Claritate redusă: Datorită sintaxei limbajului, programele de mari dimensiuni devin greu de
citit şi înţeles;
• Inflexibil: Visual Basic este uşor de învăţat, însă ascunde detaliile tehnice ale programării. În
acelaşi timp, împiedică programatorul să controleze total calculatorul, ceea ce limitează
puterea programelor;
• Portabilitate limitată: Visual Basic rulează numai pe platforme Windows.
3.3 Pascal
Este un limbaj de nivel înalt care încurajează programarea modulară, bine structurată. Pascal
este acceptat pe scară largă ca limbaj educaţional şi de dezvoltare a aplicaţiilor. Este mai puţin
flexibil decât C/C++, compilatorul face mai multe verificări, însă în acest fel scade riscul apariţiei
erorilor. Având la început scop didactic, conversiile de tip sunt mult mai stricte decât în C, ceea ce
scade riscul apariţiilor erorilor, dar în acelaşi timp scade şi flexibilitatea, libertatea şi imaginaţia pe
care o pot pune în practică programatorii. Limbajul a apărut în 1971, creat de Nicklaus Wirth. Un
5
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
pas înainte important faţă de celelalte limbaje existente la momentul respectiv a fost faptul că
suporta recursivitatea.
În prezent, Pascal stă la baza mai multor medii de dezvoltare a aplicaţiilor comerciale, cu
suport orientat obiect. Borland Delphi (sub Windows) şi CodeWarrior Pascal şi THINK Pascal (sub
Macintosh) sunt cele mai utilizate.
3.4. Java
Limbajul Java este rezultatul „Stealth Project” al Sun Microsystem, care avea ca scop
cercetarea în domeniul aplicabilităţii calculatoarelor pe piaţa produselor electronice în vederea
creării de produse electronice inteligente care să poată fi controlate şi programate centralizat,
printr-un dispozitiv asemănător cu o telecomandă. Aceleaşi cerinţe de stabilitate şi independenţă în
sisteme eterogene existau şi pentru Internet. De aceea, deşi proiectat iniţial în alte scopuri, Java s-a
potrivit perfect aplicaţiilor world-wide-web.
Sun a prezentat formal Java în 1995. În curând, Netscape Inc. a anunţat că va încorpora
suport pentru Java în browser-ul lor. Mai târziu, şi Microsoft a făcut acelaşi lucru, întărind rolul
limbajului Java în zona Internet.
Java aparţine unei noi generaţii de limbaje de programare, care şi-a câştigat în ultima
perioadă o mare popularitate. În Java se pot crea programe complexe sau mini-programe
(applet-uri) care să ruleze pe Internet. Deoarece Java este încă la început, Sun Microsystems,
creatorul său, încearcă permanent să îmbunătăţească limbajul. Din acelaşi motiv, multe companii au
încă o atitudine de expectativă în legătură cu Java. Totuşi, dacă portabilitatea este importantă, Java
este alegerea cea mai bună.
O caracteristică a limbajului este maşina virtuală (sau interpretorul) care trebuie să existe pe
calculatorul client. Fiecare platformă are propria maşină virtuală, care execută de fapt programul pe
sistemul de operare respectiv. Când un program este compilat, rezultă un fişier de „byte-codes”,
foarte asemănătoare cu instrucţiunile maşină, fără însă a fi specifice unui anumit procesor. Procesul
transformării „byte-codes” în cod maşină este foarte simplu şi se face la runtime. Din acest motiv
însă, există clare diferenţe de performanţă între un program Java interpretat şi un program C,
compilat în cod nativ. Bruce Eckel, în „Thinking in Java”, consideră că, în medie, un program Java
este de aproximativ 20 de ori mai lent decât un program C echivalent.
Un alt avantaj îl constituie „garbage collector”-ul, care scuteşte programatorul de sarcina
dezalocării memoriei alocate dinamic. Această trăsătură face programarea mai uşoară şi elimină o
întreagă clasă de erori.
Avantaje:
• Portabilitate deplină: Orice program scris în Java poate rula (teoretic) pe toate sistemele de
operare importante (Windows, Linux, Macintosh) fără modificări suplimentare;
• Siguranţă: Java a preluat trăsăturile pozitive ale C/C++ şi a evitat multe din neajunsuri.
Neavând pointeri, programele Java au mai puţine şanse de eroare la accesarea memoriei;
• Bazat pe C/C++: Deoarece Java e derivat din C/C++, oricine ştie C/C++ poate învăţa rapid
Java.
Dezavantaje:
• Lent şi mai puţin eficient: Deoarece este un limbaj interpretat de către maşina virtuală,
programele Java rulează mai lent decât programele echivalente în cod nativ;
• Dificil de învăţat: Java arată ca C/C++, deci este la fel de greu de învăţat ca şi acesta.
6
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
3.5. C#
C# este cel mai nou limbaj important de programare, dezvoltat de Anders Hejlsberg la
Microsoft. C# este primul limbaj proiectat de la început pentru Internet. Este un limbaj modern care
combină cele mai bune caracteristici ale celor mai folosite limbaje de programare. Odată cu C#,
Microsoft a lansat şi platforma .NET, care permite compilarea şi interfaţarea de programe scrise în
limbaje diferite.
C# este foarte asemănător în ceea ce priveşte sintaxa cu Java, însă păstrează o apropiere mai
mare de C++. Atât Java cât şi C# compilează mai întâi într-un limbaj intermediar: Java byte-code,
respectiv Microsoft Intermediate Language (MSIL). În C#, compilarea codului intermediar în cod
nativ este însă mai eficientă. C# are de asemenea un garbage collector, care s-a demonstrat
matematic că este foarte aproape de optimul posibil. Ca şi Java, C# a renunţat la moştenirile
multiple, în favoarea unui model de moştenire simplă extins de moştenirea multiplă a interfeţelor.
Spre deosebire de Java, care a renunţat total la pointeri, C# permite folosirea acestora, dar numai în
cazuri speciale, marcate „unsafe”.
Un alt avantaj îl constituie posibilitatea generării automate a documentaţiei pe baza codului
sursă.
Limbajul C:
#include <stdio.h>
#include <stdlib.h>
void main()
{
int dim, i;
float *vector; // float vector[10];
float max;
7
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Limbajul C++
#include <iostream.h>
void main()
{
int dim;
cout << "Dimensiunea vectorului: ";
cin >> dim;
float *vector;
vector = new float[dim];
float max;
for (int i=0; i<dim; i++)
{
cout << "Vector[" << (i+1) << "]: ";
cin >> vector[i];
if (i==0)
max = vector[i];
else
{
if (max < vector[i])
max = vector[i];
}
}
cout << "Maximul: " << max;
delete []vector;
}
Limbajul Basic
10 INPUT "Dimensiunea vectorului: ", dimvect
20 DIM vector(dimvect)
30 LET max = 0
40 FOR i=1 TO dimvect
50 INPUT ("Vector ("; i ; "): "); vector(i)
60 IF i = 1 THEN LET max = vector(i): GOTO 80
70 IF max < vector(i) THEN max = vector(i)
80 NEXT i
90 PRINT "Maximul: "; max
For i = 1 To dimens
If i = 1 Then max = vector(i)
Else: If max < vector(i) Then max = vector(i)
Next i
End Sub
8
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Limbajul Pascal
begin
writeln('Dimensiunea vectorului: ');
readln(dim);
if i=1 then
max := vector[i]
else begin
if max < vector[i] then
max := vector[i];
end;
end;
Limbajul Java
import java.io.*;
9
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Limbajul C#
using System;
class Simple
{
[STAThread]
static void Main(string[] args)
{
Console.Write("Dimensiunea vectorului: ");
string s = Console.ReadLine();
int dim = int.Parse(s);
float[] vector = new float[dim];
float max = 0;
for (int i=0; i<dim; i++)
{
Console.Write("Vector[{0}]: ", i+1);
s = Console.ReadLine();
vector[i] = float.Parse(s);
if (i==0)
max = vector[i];
else
{
if (max < vector[i])
max = vector[i];
}
}
Console.WriteLine("Maximul: {0}", max);
}
}
(defrule introd_dim
=>
(printout t "Dimensiunea vectorului: ")
(assert (dim (read)))
)
(defrule init_max
(dim ?)
=>
(printout t "Vector(1): ")
(bind ?val (read))
(assert (vector ?val))
(assert (max ?val))
(assert (i 2))
)
(defrule continue
(dim ?d)
?f1 <- (i ?i)
(test (<= ?i ?d))
?f2 <- (vector $?vect)
?f3 <- (max ?max)
=>
(printout t "Vector(" ?i "): ")
(bind ?val (read))
(retract ?f1 ?f2)
(assert (i (+ ?i 1)))
(assert (vector $?vect ?val))
(if (< ?max ?val) then
10
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
(retract ?f3)
(assert (max ?val))
)
)
(defrule print_max
(dim ?d)
(i ?i)
(test (> ?i ?d))
(max ?max)
=>
(printout t "Maximul: " ?max crlf)
)
Notă: găsirea maximului într-un şir se poate realiza mult mai uşor în Clips:
(defrule max
(vector $? ?v1 $?)
(forall (vector $? ?v2 $?)
(test (<= ?v2 ?v1))
)
=>
(printout t "Maximul: " ?v1 crlf)
)
În primul program s-a încercat însă implementarea algoritmului secvenţial. Se vede astfel
diferenţa de concepţie între abordările imperativă şi declarativă.
automată care fac programul mai uşor de înţeles şi reduc erorile. E posibil, de asemenea, de a
furniza scheme (tipare) pentru construirea programelor, conţinând headere standard, secţiuni
obligatorii pentru declararea constantelor şi tipurilor de date. Acestea pot fi generate automat de
utilitarele de modelare. Editoarele care recunosc aceste scheme pot reduce timpul de dezvoltare
şi pot preveni erorile;
• Analizoare statice: Examinează codul sursă. Analiza statică este procesul de scanare a textului
unui program pentru detectarea unor erori:
o identifică variabile neutilizate sau utilizate înainte de a fi asignate;
o verifică dacă valoarea variabilei este în intervalul admis;
o furnizează o prezentare a structurii aplicaţiei;
o măsoară complexitatea codului în termenii unei metrici;
o transformă codul sursă într-un limbaj intermediar pentru verificare formală;
o măsoară anumite atribute ale codului cum ar fi numărul de linii de cod şi nivelul
maxim de imbricare.
Cele mai multe compilatoare furnizează unele din caracteristicile analizoarelor statice (cum ar fi
prima caracteristică). Analizoarele statice dedicate de obicei furnizează funcţii de analiză statică
avansate, cum ar fi analiza structurii codului;
• Compilatoare: Transformă codul sursă în cod obiect. Acestea variază în viteză, completitudinea
verificărilor, uşurinţa utilizării, folosirea sintaxei standard, calitatea codului şi afişărilor şi
prezenţa caracteristicilor de programare. Alegerea compilatorului este de importanţă crucială.
Viteza compilatorului afectează costul produsului şi uşurinţa în dezvoltarea, depanarea şi
întreţinerea produsului, în timp ce calitatea codului afectează performanţele produsului în
timpul execuţiei. Compilatoarele ar trebui comparate ţinând cont de viteza lor, de timpul de
execuţie al programului şi dimensiunea codului. Dimensiunile stivei şi a memoriei heap pot fi,
de asemenea, importante. Compilatoarele variază mult şi funcţie de caracteristicile care suportă
programarea:
o listare completă;
o cross-referenţierea;
o dimensiunea datelor şi modulelor;
o diagnosticare;
o verificare completă;
o verificarea limitelor vectorilor.
Cele mai avansate compilatoare execută anumite optimizări pentru maşini secvenţiale sau
paralele, încercând să descopere şi să elimine deficienţele codului sursă. Aceste optimizări pot fi
impuse prin switch-uri, de exemplu prin directive în codul sursă. Utilizatorii ar trebui să verifice
dacă optimizările pe care le doresc sunt implementate în compilatoarele candidat.
• Linkeditoare: Reunesc modulele obiect în programe executabile. Acestea sunt furnizate de
maşină, sistemul de operare sau compilator. De aceea, utilizatorul are în foarte mică măsură
controlul asupra alegerii acestora. Este util ca linkeditorul să determine automat bibliotecile şi
directoarele pe care trebuie să le utilizeze şi care sunt modulele sau componentele care trebuie
linkeditate. Cele mai multe linkeditoare pot fi controlate de parametri creaţi de utilitare build
sau make;
• Depanatoarele: Localizează erori în timpul execuţiei programului. Utilizarea depanatoarelor
simbolice interactive este puternic încurajată, mai ales pentru verificare. Un depanator bun este
integrat cu editorul şi compilatorul/interpretorul şi permite o gamă de moduri de investigare: pas
cu pas, trasare prin breakpoint, vizualizarea valorilor variabilelor, setarea unor condiţii;
• Analizoarele dinamice: Examinează programele în curs de execuţie. Analiza dinamică este
procesul de măsurare a resurselor (timp CPU, timp intrare-ieşire, memorie) consumate de
fiecare modul şi linie de cod. În contrast cu analizoarele statice, cele dinamice se folosesc pentru
programe în curs de execuţie. Analizoarele dinamice se mai numesc şi profilers. Ele mai pot fi
folosite şi pentru a determina dacă toate instrucţiunile programului au fost executate în timpul
testului (test de acoperire). Unele analizoare dinamice verifică dacă programul utilizează corect
12
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
6. Concluzii
În concluzie, procesul de implementare este dificil de caracterizat şi de descris. Aici se
regăsesc aspecte ale analizei cerinţelor, ale specificaţiilor şi proiectării. Programatorul va trebui să
facă un număr de importante compromisuri între siguranţa produsului, costul, eficienţa, timpul de
execuţie, posibilitatea lui de întreţinere, etc. Sarcina cea mai dificilă a programatorului este de a
realiza această pluralitate a scopurilor. Este dificil de a spune care va fi rezultatul acestor
compromisuri atât timp cât produsul nu este complet şi testat. Odată ce produsul a fost finalizat, în
sensul că toate părţile sale componente au fost reunite şi sistemul poate funcţiona, începe etapa
următoare a procesului de dezvoltare şi anume faza testării.
13
Ingineria programării
Cursul 14
Primele cercetări empirice despre maniera în care lucrează programatorii au fost sever
criticate; nevoia unei metodologii solide pentru aceste experimente a fost subliniată în mod repetat.
În ultimii ani calitatea experimentelor legate de factorii umani a crescut şi au fost formulate câteva
teorii utile.
În momentul când se fac experimente, o atenţie sporită trebuie acordată aplicabilităţii
ecologice (adică a relevanţei rezultatelor în cazul generalizării în situaţii reale). Aplicabilitatea
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
ecologică trebuie luată în calcul atât atunci când e vorba de utilizarea practică a interfeţei grafice cât
şi atunci când se fac studii experimentale asupra comportamentului programatorului. Factorii care
trebuie luaţi în considerare sunt:
Programatorii scriu programe. Ei exprimă algoritmi folosind construcţii proprii unui limbaj
de programare. Pentru aceasta, trebuie să cunoască sintaxa şi semantica acestor construcţii. Mai
trebuie să ştie cum să construiască programe logice din blocurile de construcţie pe care le au la
dispoziţie. Cu cât ştim mai bine să folosim un limbaj de programare, cu atât mai bine vom reuşi să
îndeplinim aceste sarcini.
De obicei, simpla scriere de cod nu este singura sarcină pe care trebuie să o îndeplinească
programatorul. Acesta trebuie să conceapă programul, să-l testeze, să-l documenteze şi să ofere
suport. Programatorul trebuie să înţeleagă şi programele scrise de altcineva. De multe ori
programele sunt mai mult citite decât folosite practic. Un model descriptiv despre modul în care
trebuie să-şi desfăşoare lucrul programatorii ar trebui să includă toate aceste activităţi prezentate
mai sus.
Un model al memoriei umane distinge trei tipuri de memorie: memoria senzorială, de scurtă
durată şi de lungă durată. Memoria senzorială reţine pentru un timp foarte scurt informaţiile oferite
de sistemul perceptiv, în vederea prelucrării acestora. Informaţiile cu un anumit grad de relevanţă
sunt păstrate apoi în memoria de scurtă durată. Aceasta are o capacitate limitată. Capacitatea ei se
estimează la aproximativ 7 unităţi.
Entităţile din memoria de scurtă durată nu necesită conexiuni cu informaţii elementare.
Oamenii combină informaţiile în unităţi cât mai mari. Acest proces este numit chunking, iar
unităţile de informaţie sunt numite chunks (bucăţi, porţiuni). De exemplu, secvenţa de cifre
„234567” poate ocupa 6 intrări în memoria de scurtă durată. Totuşi, dacă este recunoscută ca fiind
un număr de telefon, este codată şi ocupă o singură intrare. Astfel intrările în memorie de scurtă
durată pot fi văzute ca etichete pentru o anumită informaţie, ca o cifră, un număr de telefon sau o
rutină de sortare. Când informaţia este prelucrată şi manipulată pentru executarea unor operaţii,
memoria de scurtă durată se comportă ca o memorie activă, ca şi registrele unui calculator.
Informaţiile relevante pe termen lung sunt păstrate în memoria de lungă durată.
Având la bază acest model, apar două întrebări importante: ce tip de cunoştinţe posedă
programatorul în memoria de lungă durată şi ce tip de procese cognitive apar în vederea găsirii
soluţiei în memoria sa activă?
2
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
• înţelegerea problemei;
• realizarea unui plan, descoperirea unei strategii de obţinere a soluţiei;
• executarea planului;
• verificarea rezultatelor.
începătorii. Acest fapt reflectă asocierea mai puternică a jalonului cu un anumit tip de program,
datorită experienţei. De asemenea, experţii încearcă să „recunoască” părţi mari din program, să
înţeleagă ideea din program, fără să ia în calcul detaliile.
1.1.1 Comentariile
Experţii nu sunt întru totul de acord cu tipul şi volumul de comentarii ce trebuie incluse în
codul sursă. Orice carte de programare ne va spune să documentăm programele create, deşi acestea
pot avea şi efecte negative: în cazul în care comentariile nu sunt rescrise în momentul modificării
codului, comentariile mai vechi pot să inducă în eroare cititorul.
Shneiderman (1980) prezintă un experiment în care 62 de studenţi au studiat un program
FORTRAN lung de 26 de linii. Un grup (H) a primit programul având comentarii de nivel înalt la
începutul codului, care descriau funcţionalitatea programului. Cel de-al doilea grup (L) a primit un
cod având comentarii detaliate pentru instrucţiunile folosite. Ambelor grupuri li s-a cerut să facă
mici schimbări asupra programului. De asemenea, li s-a mai dat şi un test de memorie: cât de multe
linii pot fi reproduse. La ambele teste, grupul H a obţinut punctaj mai mare decât L.
Woodfield (1981) a studiat legătura între modularitate şi comentarii. Pentru acest
experiment au fost testaţi, în mare parte, programatori experimentaţi. Woodfield a folosit patru
variante ale unui program de 100 de linii: o variantă monolitică formată dintr-un singur modul, două
variante folosind descompunere funcţională, şi o variantă orientată obiect. Toate versiunile având
comentarii au obţinut un scor mai bun decât cele fără. Totuşi, pentru versiunea monolitică,
diferenţele au fost mici. Versiunea orientată obiect a obţinut performanţele cele mai bune.
Comentariile nu sunt stocate în structura semantică internă construită. Ele doar conduc la o
obţinere mai uşoară a acesteia. Programatorii începători sunt mai atenţi la comentarii decât cei
experimentaţi. Pentru programe scurte, profesioniştii nu au nevoie de comentarii, mai ales dacă
structura programului poate fi obţinută prin alte mijloace. De exemplu, se pot folosi nume
mnemonice, care sunt suficiente pentru a determina structura semantică a programului. Când
numele nu au înţeles, comentariile reprezintă singurul ajutor. Combinaţia între nume fără sens şi
programe monolitice poate explica dificultăţile pe care le întâmpină programatorii.
Comentariile care explică funcţionalitatea sunt preferate comentariilor de nivel scăzut.
Comentariile nu trebuie să imite codul, ca în exemplul următor:
x = 0; // x devine 0
4
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
1.1.3. Indentaţia
Indentaţia (engl. spaţiere, zimţuire) se referă la determinarea distanţei faţă de margine (în
caractere albe) a liniilor de program, astfel încât să poată fi puse în evidenţă anumite structuri ale
programului.
Scopul indentaţiei este creşterea lizibilităţii codului. Stilul indentaţiei poate diferi
considerabil, chiar dacă este folosit acelaşi limbaj de programare. Mulţi programatori şi-au
dezvoltat propriul stil de aliniere. Ei folosesc acest stil când citesc programe sau când îşi construiesc
structura internă corespunzătoare. Când sunt confruntaţi cu un program cu o indentaţie diferită,
forma vizuală diferită poate stânjeni, cel puţin la început, înţelegerea codului.
În prezent există şi alte mijloace de punere în evidenţă a structurii unui program. De
exemplu, cuvintele cheie pot fi scrise cu litere aldine iar comentariile cu litere cursive.
• Programele cu rezultate eronate aveau de două ori mai multe „goto”-uri decât programele cu
rezultate corecte;
• Programele cu „goto”-uri aveau o structură cu mult mai proastă decât cele fără „goto”;
• Timpul mediu pentru depanarea programelor cu „goto”-uri era mai mare decât cel al
programelor fără „goto”.
5
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
de erori pe care le are un program este direct proporţional cu numărul de „goto”-uri. Totuşi, un
program fără goto-uri nu este obligatoriu fără erori.
Un alt rezultat important este faptul că utilizarea structurilor şi claselor creşte gradul de
abstractizare al programului, cu efecte benefice asupra înţelegerii. Astfel se explică şi tendinţa de
înlocuire a programării structurate cu programarea orientată obiect.
În timpul proiectării, problema este descompusă în module. Pentru programe de mici
dimensiuni, avantajul modularizării nu este evident. Totuşi, atunci când dimensiunea programelor
creşte, tehnica modularizării devine foarte importantă, iar modificările sunt mult mai rapide decât în
cazul programelor monolitice echivalente.
• Modelul mental al utilizatorului: Este modelul maşinii pe care îl creează utilizatorul, pe baza
educaţiei şi cunoştinţelor despre sistem sau domeniul aplicaţiei. În timpul interacţiunii cu
sistemul, acest model este utilizat pentru a planifica acţiunile, pentru a prezice şi interpreta
reacţiile sistemului. Modelul mental reflectă înţelegerea utilizatorului asupra a ceea ce
conţine sistemul, cum funcţionează acesta şi de ce. Modelul este determinat iniţial de meta-
comunicare, adică instruire şi documentare. El evoluează în timp, pe măsură ce utilizatorul
dobândeşte o înţelegere din ce în ce mai exactă asupra sistemului;
• Imaginea sistemului: Include toate elementele sistemului cu care vine în contact utilizatorul.
Modelul mental al utilizatorului se bazează în cea mai mare parte pe această imagine. Ea
poate include stilul de interacţiune, forma şi conţinutul schimbului informaţional, chiar şi
aspectul fizic al sistemului, dispozitivele externe conectate etc.;
• Modelul conceptual: Este modelul precis din punct de vedere tehnic creat de proiectanţi, o
reprezentare completă a sistemului asupra tuturor aspectelor legate de interacţiunea cu
utilizatorul. Pe baza acestui model reacţionează sistemul la acţiunile utilizatorului.
Problema fundamentală a realizării unei interfeţe între om şi calculator este apropierea cât
mai mare a modelului conceptual de modelul mental al utilizatorului.
Utilizatorul unui sistem interactiv trebuie să îndeplinească anumite sarcini: trimiterea unui
e-mail, tehnoredactarea unui document etc. Toate aceste sarcini au o anumită structură, prezentă în
memoria umană. Interacţiunea om-calculator trebuie să aibă aceeaşi structură, pe cât posibil. Dacă o
anumită acţiune este percepută ca o singură unitate conceptuală în domeniul sarcinii, este derutantă
acţionarea unui întreg şir de comenzi pentru a realiza efectul dorit. De asemenea, sarcinile care sunt
apropiate în domeniul problemei (adică sunt conectate semantic) trebuie să fie apropiate şi în
interacţiunea cu sistemul computerizat.
Inginerii programatori sunt înclinaţi să modeleze interfaţa cu utilizatorul după structura
mecanismului de implementare şi nu după structura domeniului problemei. De exemplu, un
program poate solicita introducerea secvenţei „ ↑ 10 2 ” pentru a obţine 10 2 , doar pentru că sistemul
recunoaşte şi prelucrează mai uşor prima variantă. În acelaşi fel, documentaţia urmăreşte de multe
ori detaliile şi modelele de implementare, iar mesajele de eroare sunt formulate în termeni care
reflectă punctul de vedere al programatorului şi nu problemele utilizatorului.
6
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
Pe măsură ce utilizatorul traversează curba de învăţare, mai devreme sau mai târziu el atinge
un nivel acceptabil pentru el la momentul respectiv, deşi nu este încă expert. Acest fenomen apare
la un mare număr de aplicaţii. De exemplu, majoritatea utilizatorilor de Linux cunosc bine numai o
submulţime a comenzilor disponibile. Despre o altă submulţime de comenzi au doar o idee vagă,
însă nu le cunosc exact funcţionalităţile. Despre restul de comenzi, utilizatorii respectivi nu au auzit.
Prin instruire şi documentare, utilizatorul îşi construieşte mai întâi o imagine despre
facilităţile oferite de sistem şi apoi încearcă să realizeze ceva practic cât mai devreme, înainte să
aibă o înţelegere deplină asupra sistemului. Dacă nu beneficiază de ajutor în continuare, sunt mari
şansele să nu înţeleagă niciodată noţiunile respective.
Pentru submulţimea de comenzi abia cunoscute, se recomandă un sistem de help pasiv,
on-line sau off-line (sub formă de documentaţie). Pentru comenzile necunoscute de utilizator, se
recomandă un help activ, care să-l îndrume pe utilizator şi să-i explice noile noţiuni necunoscute
întâlnite.
Pentru realizarea interfeţei grafice cu utilizatorul, se recomandă următoarele principii de
proiectare:
2. Etica programării
Programarea este rezultatul unui comportament uman intenţionat, realizat de persoane cu
anumite valori (şi deci etică). Tehnologia nu este neutră din punctul de vedere al valorilor. Chiar şi
scrierea unui program simplu presupune, într-un anumit fel, valorile programatorului. Mai mult,
programele sunt mijlocare de comunicare, codul sursă este o formă de exprimare. În consecinţă,
prin codul scris, programatorii au ocazia să-şi comunice valorile.
Un exemplu de cod etic este cel al IEEE (The Institute of Electrical and Electronics
Engineers, 1990), care conţine 10 puncte:
h) tratarea nepărtinitoare a tuturor persoanelor, fără a ţine seama de factori precum rasă, religie,
sex, handicap, vârstă sau naţionalitate;
i) evitarea defavorizării altora, a proprietăţii, reputaţiei sau serviciului lor prin acţiuni incorecte
sau răuvoitoare;
j) asistenţa acordată colegilor în dezvoltarea lor profesională şi în respectarea prezentului cod
de etică.
patent trebuie acordat fie în aşa fel încât să poată fi folosit gratuit şi fără restricţii de oricine, fie
deloc.
Dintre termenii şi condiţiile licenţei GNU, menţionăm:
• Puteţi copia şi distribui copii nemodificate ale codului sursă al Programului în forma în care
îl primiţi, prin orice mediu, cu condiţia să specificaţi vizibil pe fiecare copie autorul şi lipsa
oricărei garanţii, să păstraţi intacte toate notele referitoare la această Licenţă şi la absenta
oricărei garanţii şi să distribuiţi o copie a acestei Licenţe cu fiecare copie a Programului.
Puteţi pretinde o retribuţie financiară pentru actul fizic de transfer al unei copii, şi puteţi
oferi garanţie contra cost.
• Puteţi efectua modificări asupra copiilor Programului (sau asupra oricăror porţiuni ale sale),
creând astfel un "proiect bazat pe Program". Copierea şi distribuirea unor asemenea
modificări sau proiecte se pot face conform termenilor secţiunii precedente (1), doar dacă
toate condiţiile următoarele sunt îndeplinite:
o Toate fişierele modificate trebuie să conţină note foarte vizibile menţionând faptul că
dumneavoastră le-aţi modificat, precum şi data fiecărei modificări;
o Orice proiect pe care îl distribuiţi sau publicaţi, care în întregime sau în parte conţine
sau este derivat din Program (sau orice parte a acestuia), trebuie să poată fi folosit de
oricine, gratuit şi în întregime, în termenii acestei Licenţe;
o Daca programul modificat citeşte comenzi în mod interactiv, trebuie să îl modificaţi
în aşa fel încât atunci când este pornit în mod interactiv să afişeze un mesaj referitor
la drepturile de autor precum şi o notă menţionând lipsa oricărei garanţii (sau să
menţioneze faptul că dumneavoastră oferiţi o garanţie).
• Puteţi copia şi distribui Programul (sau un proiect bazat pe el, conform Secţiunii 2) în format
obiect sau executabil conform termenilor Secţiunilor 1 şi 2 de mai sus, cu condiţia să
îndepliniţi una dintre condiţiile de mai jos:
o Să îl oferiţi însoţit de codul sursă corespunzător, în format citibil de către maşină,
care trebuie să fie distribuit în termenii Secţiunilor 1 şi 2 de mai sus pe un mediu de
distribuţie uzual transportului de software; sau
o Să îl oferiţi însoţit de o ofertă scrisă, (validă pentru cel puţin trei ani, pentru o taxă
care să nu depăşească costul fizic al efectuării distribuţiei sursei), de a oferi o copie
completă, în format citibil de către maşină, a codului sursă, distribuit în termenii
Secţiunilor 1 şi 2 de mai sus, pe un mediu de distribuţie uzual transportului de
software; sau
o Să îl oferiţi însoţit de informaţia pe care aţi primit-o referitoare la oferta de a
distribui codul sursa corespunzător. (Această alternativă este permisă numai pentru
distribuiri necomerciale şi doar dacă aţi primit programul în format obiect sau
executabil împreună cu această ofertă, în conformitate cu Subsecţiunea b de mai
sus.)
• Deoarece programul este oferit sub o licenţă ce nu implică nici un cost, nu există nici o
garanţie pentru program, în măsura permisă de legile ce se aplică. Exceptând situaţiile unde
este specificat altfel în scris, deţinătorii drepturilor de autor şi/sau alte părţi implicate oferă
programul „în forma existentă” fără nici o garanţie de nici un fel, explicită sau implicită,
incluzând, dar fără a fi limitată la, garanţii implicite de vandabilitate şi conformitate unui
anumit scop. Vă asumaţi în întregime riscul în ceea ce priveşte calitatea şi performanţa
acestui program. În cazul în care programul se dovedeşte a fi defect, vă asumaţi în întregime
costul tuturor serviciilor, reparaţiilor şi corecţiilor necesare.
10
Florin Leon – Ingineria programării, http://eureka.cs.tuiasi.ro/~fleon/curs_ip.htm
3. Concluzii
În acest curs s-a urmărit trecerea în revistă a unor probleme de psihologie cu aplicabilitate în
domeniul programării, care pot determina eficientizarea scrierii codului, precum şi realizarea unor
interfeţe cu utilizatorul uşor de învăţat şi de folosit. S-a insistat apoi asupra eticii programării,
detaliindu-se un cod etic şi, în final, s-au prezentat unele chestiuni de ordin legal, privind drepturile
de autor asupra programelor realizate.
11
Referinþe