Curs 1 Curs 3

[Curs nr.2]

 

2. Produse si procese



    In foarte multe privinte procesul de dezvoltare software se aseamana cu procesul de construire a unei case. Astfel, daca cineva doreste sa-si construiasca o casa, in primul rand va purta discutii cu arhitectul, discutii care vor ajuta  la  formarea unei viziuni comune asupra produsului care sa satisfaca nevoile familiei si sa se incadreze in limitele impuse de buget si de timp. Schitele de proiect sunt importante atat pentru beneficiar cat si pentru arhitect, deoarece constituie un mijloc de comunicare a intentiilor intr-un mod mai formal si mai precis. Uneltele de constructie performante si masuratorile sunt necesare pentru toti tamplarii, electricienii, instalatorii si zugravii pentru a materializa proiectul. Inspectiile periodice la fata locului, din partea beneficiarului ca si din partea unor experti in domeniul constructiilor, sunt esentiale pentru a verifica daca ceea ce se face este conform cu dorintele clientului si cu standardele in vigoare. In final, insa, ceea ce este mai important pentru client si familia sa este produsul finit - insasi casa. Orice alta activitate este secundara, dar constituie pasi inevitabili in procesul de elaborare a produsului final.
Pe masura ce nevoile familiei se modifica (de ex. poate aparea necesitatea amenajarii unei parti din subsol ca birou sau dormitor), respectiv pe masura ce lucrurile se uzeaza (de ex. este necesara renovarea bucatariei sau repararea unui robinet fisurat in baie), urmatorul lucru important il constituie existenta unei marturii palpabile sau conceptuale care sa reflecte scheletul arhitectural al casei. Un asemenea document:


Tot acest proces descris mai sus este analog procesului de dezvoltare software. O organizatie care dezvolta un software de calitate niciodata nu pierde din vedere obiectivul primordial: dezvoltarea, furnizarea si intretinerea in mod eficient a unor produse care sa multumeasca utilizatorul si care sa satisfaca toate cerintele stabilite.

Un program care face ceva util este cel mai important produs al oricarui proiect.
Activitatile de analiza, proiectare, implementare, verificare a calitatii si documentare sunt importante numai in masura in care contribuie la atingerea acestui scop. Orice altceva (discutii, intalniri, programare, redactare, testare) este secundar.
Al doilea ca importanta dupa programul insusi, este documentul care descrie arhitectura produsului.
Acesta este esential pentru evolutia si intretinerea programului. Toate modificarile impuse fie de aparitia unor cerinte noi, fie de eliminarea unor erori,  trebuie facute de asa maniera incat sa se armonizeze cu arhitectura originala. Altminteri, in timp, se va ajunge la un sistem "peticit", de calitate slaba, a carui structura originala devine din ce in ce mai obscura, pana cand nu mai poate fi modificata.
Asemenea sisteme fragile sunt un adevarat balast pentru compania care trebuie sa se ocupe de ele in continuare, mentinerea lor "in viata" costand adesea mai mult decat o reproiectare de la zero.
Pentru personalul tehnic din domeniul software, dezvoltarea de software la nivel industrial ar putea parea mult mai usor de abordat, daca totul s-ar rezuma la chestiuni de programare. Din nefericire foarte rar cerintele, chiar daca sunt complete, sunt si corecte, neambigue si fixe. Regulile afacerilor fluctueaza, tehnologia se schimba, programatorii vin si pleaca. Din aceste motive, in special proiectele care urmaresc sa creeze produse durabile trebuie sa elaboreze un set de produse intermediare care faciliteaza scopul final - furnizarea unui sistem software de calitate. Ca urmare, orice organizatie care urmareste sa livreze si sa intretina programe in mod sistematic, trebuie sa adopte un model de proces suficient de bine definit, astfel incat, cu fiecare aplicatie complexa abordata, echipa sa poata imbunatati continuu procesul, facandu-l mai eficient si mai flexibil la schimbarile de cerinte.

Dezvoltarea unor produse de calitate

Ce este un produs software de calitate ? Din perspectiva unui programator OO, raspunsul este unul obiectiv:  un program de calitate este  acela care functioneaza si care realizeaza tot ce se asteapta de la el, nimic mai mult sau mai putin. Din perspectiva utilizatorului final si a echipei de dezvoltare in ansamblu, raspunsul este unul subiectiv: un program de calitate este unul elegant.
Eleganta este un atribut care presupune simplitate, claritate, usurinta in intelegere. Software-ul de calitate rezolva probleme complexe prin intermediul unor mecanisme a caror bogatie semantica permite aplicarea lor in domenii complet diferite. Eleganta inseamna a gasi abstractizarea potrivita, inseamna utilizarea resurselor limitate in maniere mereu noi.
 

Exemplu: O companie de transporturi intampina probleme cu gestionarea miilor de colete care circula prin reteaua sa. Ea a creat un sistem orientat pe obiecte pentru managementul operatiilor zilnice. Un model obiectual de abordare a problemei a parut a fi solutia potrivita. Pe baza acestei structuri a fost posibila adaugarea incrementala a noi module care au dus la eficientizarea procesului ca timp si cost. Folosind algoritmi genetici, ruland pe un supercalculator independent, consultantii software au putut sa supravegheze constant reteaua si sa ofere sfaturi personalului de deservire. A reiesit ca algorimii de baza puteau fi utilizati si pentru determinarea rutelor optimale de livrare a pachetelor catre clienti.
Acest exemplu arata importanta unei arhitecturi orientate pe obiecte. Vom discuta arhitectura unei asemenea aplicatii, pornind de la clase si ajungand la generatoare de aplicatii (frameworks).

Pentru un programator individual, care utilizeaza programarea orientata pe obiecte (OOP, Object Oriented Programming), unitatea primara de compunere a programului este clasa.
Pentru programatorii care folosesc metode neobiectuale, unitatea primara a unui program este algoritmul.
 

Pentru proiectele OO unitatea de baza in care ele se pot descompune este clasa, nu algoritmul.

Trebuie insa precizat ca, asa cum s-a aratat in capitolul anterior clasa este necesara dar nu suficienta ca element de descompunere. 

O clasa de calitate are urmatoarele caracteristici:

Furnizeaza o abstractizare clara, bine delimitata a unui concept din vocabularul domeniului problemei sau al solutiei ei.
O clasa denota un set de obiecte care impartasesc o structura si un comportament comun. Astfel, intr-un sistem OO se pot identifica multe clase care materializeaza limbajul specific domeniului abordat. De exemplu, intr-un sistem de plati, se vor gasi clase reprezentand clienti, furnizori, plati si produse. Intr-un sistem medical veti identifica clase reprezentand pacienti, prestatori si servicii. In amandoua cazurile prezentate, daca privim din perspectiva implementarii, se pot identifica clase care reprezinta elemente specifice limbajului programatorilor: tranzactii, liste (de produse sau servicii) si tabele (de furnizori sau prestatori de servicii). Cu alte cuvinte:
 
Orice clasa dintr-un sistem OO trebuie sa corespunda unei entitati tangibile sau unei abstractiuni conceptuale din domeniul utilizatorului final sau al implementatorului. Se recomanda inlaturarea tuturor claselor care nu satisfac aceasta cerinta.

Prin "abstractizare clara", se intelege ca o clasa trebuie sa aiba frontiere bine definite in spatiul problemei. O clasa reprezentand toti furnizorii costisitori dintr-un sistem medical nu este o abstractizare buna, deoarece notiunea de "costisitor" este vaga si subiectiva.
Un alt element legat de delimitarea unei abstractizari se refera la structura si comportamentul incapsulate intr-o clasa. De exemplu, o clasa reprezentand clientii dintr-un sistem de evidenta a platilor va contine atribute ca: numele, adresa si telefonul clientului. A atasa insa ca atribut al clientului o lista a angajatilor ar fi o eroare, deoarece lista respectiva nu face parte din vocabularul sistemului de plati. Similar, comportamentul asociat cu clasa ce reprezinta clientii poate include posibilitatea de a consulta istoricul platilor sau esalonarea creditului unui anumit client. In schimb, ar fi deplasat sa se dea posibilitatea de a consulta numele copiilor unui client, deoarece acest lucru este total irelevant in contextul sistemului abordat.

Incorporeaza un set minimal si bine definit de responsabilitati, pe care le indeplineste foarte bine. O responsabilitate denota obligatia unui obiect de a manifesta un anumit comportament. Deoarece orice obiect este instanta unei anumite clase, rezulta ca o clasa trebuie sa inglobeze responsabilitatile tuturor instantelor sale.
Distribuirea responsabilitatilor intr-un sistem OO reprezinta o decizie critica. Nu trebuie nici sa avem putine clase care "au prea multe de facut", dar nici sa avem multe clase care "au prea putine de facut". Ca regula generala:

O clasa obisnuita trebuie sa inglobeze cam 3-5 responsabilitati.
Desigur ca de la regula generala vor exista unele exceptii. De exemplu o clasa care modeleaza o fereastra dintr-o interfata grafica este o abstractiune fundamentala si ea va contine foarte multe responsabilitati. In schimb, o clasa care modeleaza o valva sau un aparat de masura dintr-un sistem de automatizari industriale poate include doar 1-2 responsabilitati. Important este ca, la aplicarea regulii de mai sus sa se aiba in vedere caracteristici ca: usurinta in intelegere si flexibilitate.
Trebuie subliniat faptul ca responsabilitatea unei clase nu se confunda cu operatiile sale. O responsabilitate este dusa la indeplinire printr-un set de operatii care coopereaza intre ele
Realizeaza o separare clara a interfetei (comportamentului) abstractiunii, in raport cu implementarea.
Este usor de inteles si simpla dar totodata extensibila si adaptabila. Daca distribuirea responsabilitatilor in cadrul sistemului s-a facut bine, atunci clasele rezultate vor fi clare si simple.
Dezvoltarea claselor in conditii de izolare este dificila. A crea un sistem OO de calitate inseamna mai mult decat a crea niste clase individuale de calitate. Intr-un sistem complex nici o clasa nu exista in izolare, dimpotriva: clasele colaboreaza unele cu altele pentru a se obtine functionarea sistemului. De aceea, din perspectiva unui arhitect software, a construi sisteme de calitate implica luarea in considerare a unor structuri software care transcend clasele individuale si anume, ca urmare a unei distribuiri judicioase a responsabilitatilor se va urmari obtinerea unor colaborari calitative intre clase.
Exemplu: Sistemul MacApp este poate cel mai elocvent exemplu de generator de aplicatii de calitate. Prima versiune disponibila a aparut la inceputul lui 85 si folosea limbajul Object Pascal. MacApp a reprezentat unul dintre primele generatoare de aplicatii disponibile comercial. Domeniul sau era general, intentia fiind de a permite crearea de aplicatii conforme cu standardul Macintosh in ceea ce priveste interfata utilizator. Dupa cum era de asteptat, primele versiuni ale bibliotecii generatorului nu au fost prea grozave, in principal din cauza ca biblioteca, in ansamblul ei, se prezenta ca o mare de clase, ale caror interrelatii trebuiau descoperite de catre cei ce aveau indrazneala sa se avante pe acest teritoriu neexplorat. Mai mult, responsabilitatile nu erau clar distribuite intre clase, adaptarea unor clase individuale fiind astfel mult ingreunata. Dupa considerabile experimentari si utilizari reale, biblioteca a fost radical modificata (clase, ierarhii, distributia responsabilitatilor), pe la sfarsitul anilor 80. In jurul anului 1990 biblioteca a fost in intregime rescrisa in C++, operandu-se si alte modificari in structura claselor. O versiune relativ stabila a fost gata in 1992. De aceasta data biblioteca a fost mult mai abordabila, inteligibila si adaptabila. Important a fost ca avea o arhitectura explicita, nu mai era doar o mare de clase, iar colaborarile intre clase erau bine documentate.
O colaborare de clase bine conceputa se caracterizeaza prin:
Este rezultatul contributiei unui set restrans de clase care conlucreaza in vederea obtinerii unui anumit comportament.
Genereaza un comportament care nu reprezinta responsabilitatea unei singure abstractiuni, ci care deriva din ansamblul de responsabilitati distribuite pe setul de clase.
Crearea colaborarilor importante dintr-un sistem este activitatea centrala a arhitectilor software pe durata fazei de proiectare. Aceste colaborari reprezinta "sufletul" unei arhitecturi, deoarece ele sunt deciziile strategice care realizeaza conturul de baza al sistemului.
Distribuirea responsabilitatilor intr-un sistem OO trebuie sa se faca pe 2 directii:
  • verticala: de-a lungul ierarhiilor de clase care sunt legate prin relatii de generalizare/specializare;
  • orizontala: intre perechi de clase care colaboreaza una cu alta.

 
Aceste 2 dimensiuni - ierarhiile de clase si colaborarile la nivel de perechi de clase - reprezinta elementele de baza ale oricarei arhitecturi OO bine structurate.
 
Ca atare, reluand ideea initiala a acestui paragraf, putem spune ca:

 

Arhitecturi OO

O arhitectura OO bine structurata consta din:

Un set de clase organizate de regula in ierarhii multiple.
Un set de colaborari care specifica modul in care clasele coopereaza intre ele pentru a genera diversele functiuni ale sistemului.
Prima dimensiune a unei arhitecturi OO (setul de clase) realizeaza captarea MODELULUI STATIC al abstractiunilor care formeaza vocabularul domeniului problemei. Spunem vocabular, deoarece fiecare abstractiune reprezinta ceva din limbajul unui utilizator sau al unui implementator.
De exemplu, intr-un sistem de procesare a cartilor de credit vom gasi ierarhii aproape independente corespunzatoare unor notiuni cum ar fi: conturi, institutii si vanzari. Fiecare dintre acestea sunt cate o ierarhie, nu doar cate o singura clasa. De exemplu, daca ne referim la conturi, exista conturi individuale, conturi colective, conturi cu dobanda sau fara etc. Pentru toate acestea sunt necesare clase distincte care insa pot fi relationate intr-o ierarhie de tipul generalizare/specializare.

Ca regula generala, intr-un sistem trebuie prevazuta cate o ierarhie pentru fiecare abstractiune fundamentala a modelului. Folosind ierarhii distincte pentru reprezentarea abstractiunilor cu semantica foarte diferita, se asigura o mai buna distributie a resonsabilitatilor in sistem.
Fiecare ierarhie independenta, impreuna cu clasele primitive care asigura suport pentru clasele abstracte, reprezinta o grupare naturala a abstractiunilor ce compun un sistem. Vom numi o asemenea grupare categorie de clase, deoarece ea denota un element arhitectural mai cuprinzator decat o clasa sau chiar decat o ierarhie. Daca ar fi sa facem o corespondenta cu structurile dintr-un limbaj de programare OO, intuitiv, categoria de clase corespunde cu spatiul de nume (namespace) din C++ sau cu pachetul (package) din Java.
Intr-un proiect OO cea mai mare unitate de descompunere o reprezinta categoria de clase.
In interiorul unei categorii se gasesc clase inrudite din punct de vedere semantic.
Ca regula generala, daca intr-un sistem se gasesc mai mult decat 50 de clase, e cazul sa se realizeze o descompunere a sistemului in categorii de clase. O categorie tipica este formata cam dintr-o duzina de clase.
A doua dimensiune a unei arhitecturi OO (setul de colaborari) realizeaza captarea MODELULUI DINAMIC al sistemului.
Fiecare colaborare reprezinta un anumit comportament generat de un set de obiecte distincte dar care coopereaza intre ele, de multe ori aceste cooperari traversand granitele categoriilor de clase.
Atunci cand se abordeaza dinamica unui sistem trebuie evidentiat modul in care anumite grupuri de obiecte colaboreaza, astfel incat aspectele comune ale comportamentului sa fie tratate cu ajutorul unor mecanisme comune. Majoritatea sistemelor OO necesita mai putin de 10-20 de mecanisme centrale.
Un mecanism central are adanci implicatii arhitecturale si reprezinta o decizie strategica.
Cele mai multe dintre sistemele OO trebuie sa includa mecanisme centrale care rezolva urmatoarele aspecte:
Persistenta obiectelor (adica pastrarea valorilor lor pentru perioade nedefinite de timp; cel mai adesea mecanismele de asigurare a persistentei presupun existenta unor baze de date OO sau relationale).
Gestionarea memoriei.
Gestionarea proceselor.
Transmiterea/receptionarea mesajelor.
Distribuirea si migrarea obiectelor.
Conectarea in retea.
Tranzactiile.
Tratarea evenimentelor.
Tratarea exceptiilor.
Interfata utilizator.
Aceasta lista nu este exhaustiva, dar reprezinta cele mai des intalnite mecanisme dintr-un sistem. Pentru fiecare din aspectele enumerate exista mai multe solutii, iar sarcina echipei de proiectanti este de a alege una dintre ele, tinand cont si de cerinte precum: performanta, capacitate, fiabilitate, securitate etc. Fiecare asemenea decizie strategica implica, la un nivel de detaliu mai mare, o suma de decizii tactice.
Se poate spune ca structura logica si fizica a arhitecturii unui sistem OO este creata prin aplicarea tuturor deciziilor strategice si tactice adoptate de-a lungul procesului de dezvoltare.
Un aspect foarte important aici il constituie detectarea aspectelor comune (sabloane) ale comportamentului care permite obtinerea unei arhitecturi mai simple. Toate sistemele OO bine structurate sunt pline de sabloane, incepand cu cadre (frameworks) si mecanisme, la nivel global (strategic), si terminand cu idiomuri, la nivel local (tactic).
Un sablon reprezinta o solutie comuna a unei probleme intr-un anumit context.
Considerand arhitectura unui sistem OO din perspectiva sabloanelor pe care le inglobeaza, putem spune ca infrastructura unei arhitecturi OO canonice include de regula urmatoarele straturi, fiecare strat fiind reprezentat printr-un anumit cadru : Pe scurt, o arhitectura bine structurata

Produsele unui proiect software

Reluand exemplul cu casa, prezentat la inceputul acestui capitol, putem spune ca, pe langa casa propriu-zisa, pe durata constructiei ei rezulta si alte produse care, fie sunt derivate din procesul de construire, fie sunt ajutatoare acestui proces. De exemplu, constructorul si proprietarul semneaza un constract in care se specifica ce anume se va construi. Pentru unele constructii se elaboreaza si un model la scara redusa. Se elaboreaza, de asemenea, desene care vor reda aspectul general al casei, precum si schite de detaliu necesare executiei instalatiilor electrice si de canalizare. Constructorii vor avea nevoie la un moment dat de niste schele, pe care le vor demonta la terminarea lucrului. Se pot folosi fotografii ale unor case existente sau desene care sa serveasca drept model pentru anumite detalii de constructie sau decorare. La sfarsit, din toate acestea, proprietarul va ramane probabil cu casa, un rand de chei, cateva schite generale si cu instructiunile de intretinere si garantie.
Si procesul de dezvoltare software este similar. La dezvoltarea unui proiect, o echipa bine organizata va elabora doar cantitatea necesara de produse, pe langa aplicatia propriu-zisa.
Lista produselor reutilizabile a oricarui sistem software include:

Aceste produse sunt reutilizabile deoarece ele pot fi folosite si dupa ce se incheie procesul de dezvoltare in cadrul caruia au fost create, servind la dezvoltarea in continuare si a altor proiecte.
Produsele din lista de mai sus pot fi clasificate in functie de principalele categorii de persoane care le folosesc: In functie de gradul de formalizare aplicat in procesul de dezvoltare, produsele se pot manifesta in forme tangibile sau intangibile (dar nu paranormale :)).

Pentru echipa de dezvoltare produsele relevante sunt:

Arhitectura este produsul cel mai important pentru echipa de dezvoltare, pentru ca ea defineste forma sistemului si se pastreaza mai mult timp dupa ce aplicatia a fost livrata. In forma intangibila, arhitectura se regaseste in insasi structura aplicatiei, precum si in mintea arhitectilor. In forma tangibila, arhitectura poate lua forma unui prototip sau a unui document care descrie ierarhiile principale si mecanismele de baza folosite.
Deciziile de proiectare la nivel tactic se refera la proiectarea claselor, a categoriilor de clase si a interfetelor subsistemelor principale. In forma intangibila aceste decizii se manifesta ca un set de idiomuri si mecanisme aplicate in dezvoltare. In forma tangibila, ele se regasesc in documentatii gen ghiduri (indrumatoare).
Codul este cel mai tangibil produs al echipei de dezvoltare. El cuprinde implementarea claselor si documentatia referitoare la semantica fiecarui membru important al claselor.

Pentru utilizatorul final, in afara de aplicatia executabila propriu-zisa, produsele relevante sunt:

Cerintele sunt importante din urmatoarele motive: In forma intangibila cerintele exista in mintea utilizatorului final. In forma tangibila, ele sunt cuprinse intr-un document aflat intr-o continua actualizare. Experienta arata ca un asemenea document ar trebui sa fie unul bazat pe scenarii, insotite eventual de prototipuri care sa ofere modele operationale ale comportamentului dorit al sistemului.

Datele se refera la totalitatea informatiilor brute care sunt prelucrate de aplicatie. In cazul in care datele nu apar descrise explicit intr-un document, pe masura ce sistemul dezvoltat creste in dimensiuni, se ajunge adesea la situatii de fragmentare a datelor si de inmultire a redundantelor.

Interfetele utilizator, ca produse reutilizabile, reprezinta aspectul general (look & feel) al unei familii de programe. Desi platformele grafice bazate pe ferestre existente la ora actuala propun un "look & feel" propriu, ele lasa totusi programatorilor un grad considerabil de libertate in alegerea stilului interfetei, mai ales la utilizarea elementelor de control din ferestre.
In forma intangibila, o interfata exista ca stil aplicat in realizarea ferestrelor si controalelor ce intervin intr-o aplicatie aflata in executie. Forma tangibila presupune de regula un document cu rol de ghid de stil. Un asemenea ghid poate oferi o baza pentru stabilirea consensului intre dezvoltatori si utilizatori. In plus, el ar putea fi folosit si in proiecte viitoare. In felul acesta se pot economisi costurile de instruire necesare atunci cand aplicatiile livrate nu prezinta o consistenta a interfetelor.

Pentru echipa de management produsele importante sunt:

Estimarile provin din experienta unor proiecte anterioare, adaptata la specificul noului proiect si la cultura organizatiei ce realizeaza proiectul. Aceasta deoarece s-a observat ca cel mai mare impact asupra productivitatii unei organizatii il are capacitatea echipei de dezvoltare ca intreg.
Intrucat tehnologia obiectuala este relativ noua, inca nu exista o experienta bogata care sa permita aplicarea unor metode traditionale de estimare (de exemplu COCOMO) la proiecte OO. Ca urmare, multe dintre aceste proiecte trebuie sa se bazeze pe estimari informale, calibrate pe cateva proiecte pilot.

Planificarea proiectului cuprinde infrastructura necesara pentru stabilirea termenelor si alocarea resurselor umane.
In cazul proiectelor care se desfasoara intr-un mod haotic, termenele sunt stabilite ad-hoc, iar alocarea resurselor reprezinta putin mai mult decat angajarea in proiect a persoanelor care in momentul respectiv nu sunt prea incarcate.

Curs 1 Curs 3