Documente Academic
Documente Profesional
Documente Cultură
Descrierea CIP a Bibliotecii Nationale a Romaniei OANCEA, MARIUS VIRGIL Ingineria programarii : indrumar de laborator/ Oancea Marius Virgil, Seiceanu Marius Mihail. Sibiu : Editura Universitatii Lucian Blaga din Sibiu, 2007 Bibliogr. ISBN 978-973-739-448-4
I.
1.2.1 1.3
Exemplu ................................................................................................................................. 16
Usecase si Diagrame Usecase ................................................................................................. 19 2.1 2.2 Ce sunt Usecase-urile ........................................................................................................ 19 Exemplu Usecase ................................................................................................................ 19 Usecase : Cumpara .................................................................................................... 19 Alternativa 1 : Utilizatorul nu se poate autoriza ........................................... 20
2.2.1 2.2.2
2.2.3
Cumpara) ........................................................................................................................................ 20 2.3 2.4 Cantitatea de detalii........................................................................................................... 20 Diagrame UC ......................................................................................................................... 21 Actori .............................................................................................................................. 21
Diagrame de Clase ....................................................................................................................... 25 3.1 3.2 Importanta diagramelor de clase si definitie .......................................................... 25 Componente ......................................................................................................................... 26 Clasele si interfetele.................................................................................................. 27 Asocieri intre clase .................................................................................................... 29 Relatia is a sau generalizarea ......................................................................... 31 Atribute.......................................................................................................................... 32 Operatii .......................................................................................................................... 33 Constrangeri ................................................................................................................ 33
4.1 4.2
Diagramele de secventa ................................................................................................... 36 Diagrame de colaborare si contradictia static-dinamic....................................... 43 Modelul static ales ..................................................................................................... 44 Usecase sistem ............................................................................................................ 44 Noul model static reiterat dupa diagrama de colaborare....................... 47
Model View Controller ....................................................................................................... 53 6.1 Modelul MVC standard ..................................................................................................... 53 Modelul .......................................................................................................................... 53 Vizualizarea.................................................................................................................. 53 Controlerul ................................................................................................................... 53
Implementarea unei aplicatii utilizand modelul MVC standard ...................... 54 Exercitii .................................................................................................................................. 56 Cateva notiuni de Java ...................................................................................................... 57 Ferestre ......................................................................................................................... 57 Interceptarea evenimentelor ................................................................................ 59 Desenarea intr-un JPanel ........................................................................................ 60
6.6 7
Exercitii .................................................................................................................................. 62
Principii de proiectare obiectuala ........................................................................................ 71 7.1 7.2 7.3 7.4 7.5 7.6 Introducere ........................................................................................................................... 71 Arhitectura si dependente .............................................................................................. 71 Simptomele designului putred ...................................................................................... 71 Schimbarea cerintelor ...................................................................................................... 72 Managementul dependentelor. ..................................................................................... 72 Principii ale proiectrii claselor.................................................................................... 72 Principiul Open Closed (OCP) ............................................................................... 72 Principiul substitutiei Liskov (LSP) .................................................................... 78 Principiul dependentei inverse (DIP) ................................................................ 83 Principiul segregarii interfetelor (ISP).............................................................. 87
Principiile arhitecturii pachetelor ............................................................................... 88 Principii de coeziune ................................................................................................ 88 Principii de cuplare ................................................................................................... 89
7.7.1 7.7.2 8
Sabloane de proiectare ............................................................................................................. 98 8.1 Cateva observatii generale legate de OOP ................................................................ 99 Caracterizarea mostenirii ....................................................................................... 99 Compunerea obiectelor ........................................................................................... 99 Delegarea ....................................................................................................................100
Delegare (Delegation) .............................................................................................................102 9.1 9.2 9.3 9.4 9.5 9.6 9.7 9.8 Motivatie: .............................................................................................................................102 Aplicabilitate: .....................................................................................................................105 Structura: .............................................................................................................................105 Participanti: ........................................................................................................................106 Colaborare:..........................................................................................................................106 Consecinte: ..........................................................................................................................106 Implementare: ...................................................................................................................106 Probleme propuse:...........................................................................................................107 Factory Method......................................................................................................................108 Scopul ...............................................................................................................................108 Also Known As...............................................................................................................108 Motivarea ........................................................................................................................108 Aplicabilitatea................................................................................................................109 Structura ..........................................................................................................................109 Participani .................................................................................................................109 Consecinele ...................................................................................................................110
10
11.1 11.2
Also Known As...............................................................................................................112 Cand se utilizeaza .........................................................................................................112 Structura ..........................................................................................................................114 Participants ................................................................................................................114 Consecinele ...................................................................................................................114 Pattern-uri nrudite .....................................................................................................115
Singleton...................................................................................................................................116 Scopul ...............................................................................................................................116 Motivarea ........................................................................................................................116 Aplicabilitatea................................................................................................................116 Structura ..........................................................................................................................116 Implementare in Java ......................................................................................................117 Problema propusa .......................................................................................................117
Builder.......................................................................................................................................119 Intentie (cu ce scop) ....................................................................................................119 Motivatie (de ce se foloseste) ..................................................................................119 Aplicabilitate (cand il aplicam) ...............................................................................121 Structura ..........................................................................................................................121 Participanti .....................................................................................................................122 Builder (TextConvertor) .......................................................................................122
ConcreteBuilder (ASCII Convertor) ..................................................................122 Director (RTFReader) ............................................................................................122 Product (ASCIIText, TeXText, TextComponent) ..........................................122 Interactiune (Colaborari si colaboratori) ..........................................................122 Consecinte .......................................................................................................................123 Implementare ................................................................................................................124 Exemplu restaurant ................................................................................................124 Problema propusa .......................................................................................................130
Alte cateva sabloane ............................................................................................................132 Adapter.............................................................................................................................132 Scopul ...........................................................................................................................132 Bridge ...............................................................................................................................132 Scopul ...........................................................................................................................132 Composite .......................................................................................................................133 Structura .....................................................................................................................133 Participani .................................................................................................................134 Decorator ........................................................................................................................135 Scopul ...........................................................................................................................135 Participani .................................................................................................................135
14.1 14.1.1 14.2 14.2.1 14.3 14.3.1 14.3.2 14.4 14.4.1 14.4.2 15
Bibliografie ..............................................................................................................................136
10
11
GLOSAR
UML Universal Modeling Language Software aplicatie, framework sau librarie OOAD Object Oriented Analysis and Design Interfata interfata unei clase, tip; se refera la operatiile publice expuse de o clasa OCL Object Constraining Language limbaj de descriere a constringerilor
12
1.2 CONVENTII
1.2.1 NUME DE FISIERE
Fisierele java au extensia .java Fisierul in care se conspecteaza continutul unui director se numeste README
13
Nu avem voie sa depasim 2000 linii intr-un fisier. Daca ne apropiem de aceasta limita inseamna ca am incalcat unul dintre pricipiile OOP.
Cel putin clasa si metodele publice trebuie commentate (utilizand standardul javadoc - /** */)
Membrii clasei se pun in ordinea relevantei clientului clasei (public, protected, private)
Liniile de program nu depasesc 80 de caractere Cand expresiile nu intra pe o singura linie, departirea se face dupa cum urmeaza: o Rupeti dupa virgule o Rupeti inainte de un operator o In expresii complexe, nu rupeti paranteze daca este posibil
Cateva exemple:
someMethod(longExpression1, longExpression2, longExpression3, longExpression4, longExpression5);
var = someMethod1(longExpression1, someMethod2(longExpression2, longExpression3)); longName1 = longName2 * (longName3 + longName4 - longName5) + 4 * longname6; // DAAAAA
14
// NU if ((condition1 && condition2) || (condition3 && condition4) ||!(condition5 && condition6)) { //BAD WRAPS doSomethingAboutIt(); MISS } //MAKE THIS LINE EASY TO
Comentarii: In java exista 3 tipuri de comentarii: comentarii de linie Comentarii de bloc documentatie
/** /* mai multe linii ca */ html
// .
si comentarii de
cand se genereaza
...
comentariu */.
exportat
documentatia proiectului
Regula
Exemplu
Substantive in mixed case in care class StudentBun fiecare cuvand incepe cu litera mare.
interfete
Vezi clase
15
metode
Metodele trebuie sa fie verbe in acordaCalificativ() mixed case cu prima litera mica.
variabile
Asemeni
metodelor.
Nu
se int i;
recomanda ca o variabila sa inceapa float myWidth; cu _ sau $ desi este permis. Este interzisa utilizarea variabilelor scurte exeptie facand cele consecrate (i,j,k pt intregi, c,d,e pt caractere) constante Litere mari separate prin _
static final int MIN_WIDTH = 4;
// DA MyClass.doSomething();
16
if (booleanExpression) { return true; } else { return false; } // AR TREBUI SCRIS return booleanExpression; if (condition) { return x; } return y; // AR TREBUI SCRIS return (condition ? x : y);
1.4 EXEMPLU
/* * @(#)Test.java * * Copyright (c) 2007 ULBS * All rights reserved. * * This software is the confidential and proprietary information of ULBS * */ 3.82 07/02/18
package java.test;
import java.test.AnotherClass;
/**
17
* Class description goes here. * comentariu ce apare in * docum HTML ... * * @version * @author */ public class Test extends AnotherClass { /* Aici se pune implemetarea clasei */ 3.82 18 Feb 2007 Firstname Lastname
/** var1 - comentariu ce apare in * docum HTML ... */ public static int var1;
/** * var2 documentation comentariul este * mai lung de o linie - comentariu ce apare in * docum HTML ... */ private static Object var2;
18
/** * ...method doSomething comentariu ce apare in * docum HTML ... */ public void doSomething() { // ...implementare aici... } }
19
20
5. Utilizatorul introduce nr. card 6. Sistemul autorizeaza plata 7. Sistemul confirma plata 8. Sistemul trimite email de confirmare la utilizator
21
2.4 DIAGRAME UC
Diagramele usecase sunt parte a UML. Se considera ca sunt utile dar nu sunt obligatorii in dezvoltarea unui proiect.
ud: Exemplu diagrame UC
Plateste << include >> Cumpara Extension Points completeaza date livrare:location
Cumparator fidel
2.4.1 ACTORI
Actorul este un utilizator sau sistem extern cu un anumit rol (cumparator inregistrat, cumparator neinregistrat, cumparator premium, sistem de plata). Rol - Role in engleza - nu este cea mai fericita traducere dar este un termen acceptat in letaqratura. Acest termen a fost preluat pentru prima data dintr-o traducere din suedeza. Un actor poate juca mai multe roluri si poate executa mai multe usecase-uri. Exista doua abordari diferite : se pot identifica intai usecase-urile unui sistem (usecase centric) si apoi actorii sau invers. Din experienta am observat ca abordarea in care sunt descoperiti actorii si apoi se identifica usecase-urile lor este mai simpla (actor centric).
22
Un actor nu trebuie sa fie neaprat uman. Un sistem poate sa fie si el actor atata timp cat face parte din lista de interactiuni.
Exista mai multe puncte de vedere in legatura cu cine sunt actorii unui sistem. Un punct de vedere este cel in care actorii sunt oamenii si sistemele externe. Altii prefera sa considere ca actori sunt initiatorii de usecase-uri. Cea mai acceptata abordare in practica si literatura este cea in care actorii sunt cei care au ceva de castigat din usecase (In usecase-ul Cumpara, actorul este utilizatorul , el este cel servit de sistem).
23
utilizati extends cand doriti sa descrieti o variatie a comportamentului normal (declarand punctele de extensie in usecase-ul de baza)
Relatia de generalizare se utilizeaza atunci cand intr-un scenariu, la un anumit pas utilizatorul are mai multe optiuni de a alege. De exemplu in usecase-ul Utilizeaza Bancomat utilizatorul intr-un anumit punct poate face oricare dintre tranzactiile Retrage numerar , Interogheaza sold sau Plateste facturi . Desenarea corecta a acestui fapt se face ca in figura urmatoare :
ud: Use Case cu optiuni
Utilizare bancomat
Executa Tranzactie
Plateste Facturi
Retrage numerar
Interogheaza sold
24
afara asupra acestuia deci nu se asteapta o legatura stransa intre usecase-uri si clasele din program.
2.7 EXERCITII
1. Creati UseCase-uri (fiecare usecase este un document word sau text cu un continut asemanator cu cel din capitolul 2.2.1) pentru o parte a unui sistem de evidenta a cartilor inchiriate dintr-o biblioteca. 2. Creati diagramele usecase utilizand Poseidon for UML.
25
3 DIAGRAME DE CLASE
3.1 IMPORTANTA DIAGRAMELOR DE CLASE SI DEFINITIE
Diagramele de clase sunt elementele cele mai importante in metodele de proiectare orientate obiect. Toate metodele de proiectare OOP utilizeaza intr-un moment sau altul diagrame de clase. Diagramele de clase sunt reprezentari grafice ale stucturii statice ale programelor care ajuta la intelegerea mai usoara a acestei stucturi. Diagramele de clase descriu tipurile de obiecte care alcatuiesc un sistem si relatiile existente intre acestea.
26
3.2 COMPONENTE
Diagramele de clase sunt compuse din : clase (student, persoana, masina, roata) interfete relatii de asociere (o masina are patru roti) relatii de mostenire (un student este o persoana)
27
Diagramele de clase arata si componenta claselor din punctul de vedere al operatiilor si al atributelor acestora.
Zona de operatii ( C )
Desenarea diagramelor de clase se poate aborda din trei puncte de vedere diferite : perspectiva conceptuala perspectiva de specificatii perspectiva de implementare
Perspectiva conceptuala
28
In perspectiva conceptuala, proiectantul de software deseneaza conceptele domeniului de studiu. Chiar daca aceste concepte se refera la clasele care le implementeaza, destul de des nu se poate face o mapare unu la unu intre acestea. Acest lucru se datoreaza in special faptului ca modelul conceptual nu tine cont de detalii de implementare ale aplicatiei (e.g. de limbajul de programare ales). In perspectiva conceptuala, zona B si C pot sa lipseasca.
Specificatii In perspectiva de specificatii trebuie sa ne aplecam asupra interfetelor softwareului. Implementarea inca nu intereseaza. Toate metodele de proiectare OOAD pun mare accent pe diferenta intre interfata si implementare dar din pacate in practica cele doua concepte sunt combinate si amestecate (in majoritatea limbajelor de programare clasele sunt interfete si implementari in acelasi timp). In unele discutii vati auzi cuvantul tip referindu-se de fapt la conceptul de interfata unei clase. Un tip poate poate sa fie implemetat de mai multe clase iar o clasa poate implementa mai multe tipuri (interfete).
Implementare In perspectiva de implementare, clasele se refera la clase reale din program. Chiar daca aceasta perspectiva este cel mai des utilizata in practica, ea este mult mai putin importanta decat primele doua. Faptul ca este mai des utilizata in practica este datorita faptului ca extista unelte care genereaza cod (in limbaj de nivel inalt) din ele. Deasemenea esista posibilitatea ca din cod sa se extraga astfel de diagrame in mod automat (reverse engineering).
29
Cunoasterea perspectivei este de o importanta cruciala in desenarea si citirea corecta a unei diagrame de clase. Toate cele trei perspective pot fi desenate utilizand limbajul UML. Chiar daca limbajul UML in sine nu are un formalism de descriere al perspectivei putem adnota clasele cu un stereotip ca <<implementation class>> sau <<interface>>. Aceste adnotari (stereotipuri) se pun in zona de nume ( A ) Reprezentarea unei clase in UML (in diagramele de clase). - vezi Figura 4 -
Asocierile reprezinta relatiile intre instante de clase (o comanda Order este format din mai multe OrderLine).
ORDER
OrderLine
Putem sa ne imaginam ca in clasa Order este un vector (sau un Set) de OrderLine. Un OrderLine este se refera la un produs din comanda. O asociere are doua capete (roles). Fiecare capat este legat la o clasa si poate sa fie optional numit . Un astfel de nume este regasit in literatura ca role name . Fiecare capat al asocierii poate avea un ordin de multiplicitate (numit si cardinalitate). Putem face distinctie intre urmatoarele tipuri de cardinalitate :
30
fixa o 1..1 sau 1 exact o data o 4..4 sau 4 exact de 4 ori o masina are exact patru roti o samd
variabila o 0..1 asociere optionala la capatul unde este marcat in acest fel o 0..n sau * (un Order poate avea oricate (inclusiv 0) OriderLines)
In principal, cardinalitatea defineste limita minima si maxima de asociere pentru obiectele participante. Din Figura 5 putem spune ca un OrderLine poate face parte exact dintr-un Order iar un Order este format din zero sau mai multe obiecte OrderLine. Cand ordinul de multiplicitate lipseste, se presupune ca este 1. Sagetile asocierilor reprezinta navigabilitatea. Se observa ca avem o sageata dinspre Order inspre OrderLine. Aces lucru inseamna ca in clasa Order avem un membru orderLines ce este o lista, vector sau set (sau alt tip de acest fel).
public class Order { private Set orderLines;
31
Asocierile generice sunt asocierile discutate anterior, simbolizate printr-o linie trasata intre cele doua clase. Compozitiile sunt relatii stranse intre obiecte ce spun mai multe despre asociere si anume faptul ca Order si OrderLine au acelasi ciclu de viata. Un OrderLine nu poate exista fara un Order. Un OrderLine independent in sistem indica chiar o inconsistenta a acestuia. Compozitia se sinbolizeaza printr-un romb plin.
Relatiile slabe dintre obiecte se simbolizeaza prin romburi goale. Aceste relatii se numesc relatii de agregare.
Figura 8 - Agregare. O Persoana poate sa existe independent de faptul ca face sau nu parte dintr-un grup
32
Aceasta relatie se traduce la nivelul limbajului java prin class Student extends Persoana.
3.2.4 ATRIBUTE
Atributele sunt foarte asemanatoare cu asocierile. Atributul name din clasa Customer indica faptul ca un client are un nume. La nivel de specificatii, acest atribut ar spune faptul ca un Customer poate sa-si spuna numele si exista o posibilitate de schimbare a acestuia. La nivel de implementare, putem spune ca esista un membru name in clasa Customer . In functie de gradul de detaliere a diagramei, un atribut poate sa descrie in diagrama: numele sau, tipul, valoarea implicita si vizibilitatea acestuia. Sintaxa UML pentru descrierea unui atribut este : VIZIBILITATE NUME : TIP = VAL_IMPLICITA
33
Din perspectiva conceptuala nu este diferenta intre un atribut si o asociere. La nivelul specificatiei si implementarii exista diferente : diagramele nu specifica daca atributele sunt optionale sau obligatorii navigabilitatea atributelor este implicita, de la clasa la atribut semantic, legatura dintre clasa si atribut este una prin valoare nu prin referinta
3.2.5 OPERATII
Operatiile sunt procese implementate de catre clase. Operatiile corespund evident metodelor claselor. Sintaxa UML pt o operatie este: VIZIBILITATE NUME(LISTA-PARAMETRI):TIP-INTORS unde: VIZIBILITATE poate sa fie : + - public, # - protected, - - privat NUME un sir de caractere LISTA-PARAMETRI : lista de parametrii cu sintaxa ca la atribute, separati prin virgula. In fata unui parametru se poate specifica in , out , inout indicand directia de utilizare a parametrului TIP-INTORS: lista de tipuri de return separate prin virgula.
3.2.6 CONSTRANGERI
La nivelul aplicatiei exista diverse constrangeri care nu pot sa fie descrise de diagramele UML (de exemplu legatura intre doua atribute). Limbalul UML permite utilizarea tuturor mijloacelor pentru descriere constrangerilor cu singura regula ca aceste constrngeri sa fie puse intre acolade ({}). In practica se utilizeaza o engleza informala pentru imbunatatirea lizibilitatii. UML chiar pune la dispozitie un limbaj
34
de descriere a constrangerilor (OCL) dar pentru proiecte mici si medii nu se utilizeaza in mod curent. Regulile ar trebui sa fie implementate la nivelul limbajului de programare ca asertii corespunzand astfel notiunii de invariant in Design by contract . Pe scurt Design by contract este o tehnica dezvoltata de Bertrand Mayer pentru limbajul Eiffel. In principal este vorba de o afirmatie care trebuie sa fie adevarata indiferent de context. Oricand afirmatia este falsa avem un indiciu ca avem de-a face cu o eroare in program. Programele nu trebuie sa se bazeze ca afirmatiile sunt verificate. De obicei ele se verifica doar in faza de dezvoltare nu si in faza de productie a aplicatiei. Exista pentru metode : preconditii, postconditii iar in special pentru membrii, invarianti. Preconditiile/Postconditiile indica felul in care ar trebui sa arate aplicatia (sau o parte a acesteia) ininte/dupa executia unei metode. Daca avem operatia radical , o postconditie ar putea sa fie : input = result * result. Postconditiile sunt un mod de a spune ce si nu cum se face un anumit lucru.
In practica : nu se utilizeaza toate elementele mentionate in laborator ci doar cele care au sens intr-un context dat
35
nu se deseneaza modelul pentru toate clasele din sistem. Totdeauna trebuie sa ne concentram pe o parte a acestuia eliminand partile neinteresante in contextul respectiv.
3.4 EXERCITII
1) Creati clasele java prezentate in modelul (diagrama de clase) din Figura 3. 2) Creati modelul conceptual pentru un program utilizat intr-o biblioteca. 3) Realizati diagrama de clase utilizand Poseidon for UML..
36
4 DIAGRAME DE INTERACTIUNE
Diagramele de interactiune modeleaza modul in care un grup de obiecte colaboreaza pentru a realiza un deziderat comun. De obicei aceste diagrame captureaza un singur usecase. In general modelele bune se concentraza pe un singur aspect al unei probleme. Diagramele de interactiune prezinta obiecte exemplu (instante) si mesajele schimbate de acestea. Pentru a explica mai bine consideram urmatorul scenariu: comanda mai multor produse intr-un magazine virtual. STEP1 : Fereastra de introducere a comenzii trimite un mesaj pregateste la o Comanda. STEP2 : Comanda trimite mai departe aces mesaj la fiecare inregistrare din comanda (numita scurt Inregistrare) STEP3 : Fiecare Inregistrare, verifica Stoc-ul corespondent STEP4: daca Stocul este disponibil, Inregistrarea sterge cantitatea corespunzatoare din Stoc si creaza o unitate de livrare. Exista doua tipuri de diagrame de interactiune: de secventa si de colaborare.
37
obiectul 2 :
obiectul 3 :
2) .mesaj la ora 13
2) 1) raspuns
Figura 10 - Obiectele ce interactioneaza sunt puse pe orizontala. Pe verticala, de sus in jos este curgerea timpului. Nota: lifeline-ul obiectului 1 nu are dreptunghi. Aceasta nu este neaparat o greseala deoarece nu este evidentiat in diagrama apelul unei metode ale obiectului 1.
Liniile verticale punctate sunt numite lifelines . Sagetile reprezinta mesajele intre obiecte. Ordinea acestora este data de pozitia lor (sagetile pozitionate mai sus in lifetime reprezinta mesajele trimise mai devreme) Pe lifelines se pot observa niste dreptunghiuri care se pot interpreta ca pozitii ale apelurilor pe stiva. Un nou dreptunghi inseamna un nou apel. Aceste dreptunghiuri de activare se pot omite in unele desene (in special in cazul in care se deseneaza pe hartie) desenarea devenind astfel mai usoara dar desenul mai greu de citit si inteles motiv pentru care se recomanda desenarea acestor dreptunghiuri.
38
fereastraIntroducere: 1) .pregateste
oComanda:
oInregistrareComanda:
stocCorespondent:
unitateLivrare:
3) 4) .[disponibil] sterge
In figura de mai sus se observa o constructie in formatul [disponibil] sterge. Ea se poate citi ca : mesajul sterge este trimis doar daca conditia disponibil este adevarata. In pseudocod:
If (disponibil) { sterge() ; }
Constructia
3).
disponibil=verifica()
se
citeste
ca :
obiectul
oInregistrareComanda pe parcursul executarii metodei sale pregateste face un apel stocCorespondent.verifica() . Rezultatul acestui apel este introdus in variabila disponibil .
39
oComanda trimite mesajul pregateste de mai multe ori la divese instante oInregistrareComanda Putem sa ne imaginam ca oComanda face o iteratie si cheama oInregistrareComanda.pregateste pentru fiecare inregistrare din comanda. Diagrama de mai sus nu spune nimic despre aceasta iteratie. Iteratia se poate specifica fie prin prefixarea mesajului cu * fie utilizand o notatie cadru exemplificata cateva pagini mai jos.
mesaje
Dreptunghi activare
participant
lifeline
40
obiectul 2 :
obiectul 3 :
obiect de distrus :
2) .mesaj la ora 13 3)
3) 2) 1) raspuns 4)
4)
mesaj distrugere
Figura 13 - Obiectul 2 trimite un mesaj de distrugere obiectului de distrus. In punctul in care mesajul atinge obiectul de distrus "lifeline" ul acestuia nu mai continua si se termina cu un "X".
Modelele trebuie sa prezinte doar ceea ce este relevant. O problema comuna a acestor diagrame este modul de desenare a iteratiilor dupa cum s-a mai amintit si mai sus. Diagramele de secventa ca si usecase-urile nu sunt facute pentru acest lucru. Pentru a desena structuri de control se recomanda diagramele de activitati sau codul insusi. Diagramele de secventa trebuie tratate ca o
41
explicatie a modului in care obiectele interactioneaza nu un mod de descriere a unui algoritm. Iteratiile si conditiile se pot reprezenta in diagramele de secvente utilizand cadre de interactiuni . Un cadru de interactiune este o zona marcata corespunzator intr-o diagrama de secventa.
sd: Sequence diagram: Blocuri de control
oComanda :
oInregistrare :
loop(1,100) [] 1) .pregateste
1)
Figura 14 - Dreptunghiul (cadrul) loop se citeste ca: oComanda cheama metoda pregateste pentru fiecare inregistrare (minim 1, maxim 100)
42
oComanda :
oInregistrare :
stocCorespondent :
oUnitateLivrare :
[! disponibil ]
1)
Cadrele alt au de obicei mai multe zone, fiecare zona fiind gardata cu o expresie (conditie). Cadrul loop are o singura regiune, expresia de garda fiind chiar o reprezentare a ce se itereaza. Mai jos se prezinta lista tuturor tipurilor de cadre si intelesul acestora : alt mai multe alternative (ca switch) opt regiune ce se executa doar daca conditia este adevarata (echivalent IF) par fragmentele ruleaza in paralel loop fragmentul se executa de mai multe ori
43
region fragmentul reprezinta o sectiune critica (doar un fir are acces in regiune la un moment dat)
neg fragmentul contine interactiuni invalide ref refera o interactiune din alta diagrama sd utilizata pentru a marca o intreaga diagrama de secventa
Mesajele transmise pot avea si parametri. In unele cazuri dorim sa trasmite un mesaj fara a tranfera controlul nemaifind vorba despre apeluri de proceduri ci crearea de noi fire sau comunicare prin sisteme de mesagerie. Se folosesc sagetile corespunzatoare mesajelor asincrone. Pe hartie se sugereaza utilizarea unei jumatati se sageata cum se poate vedea mai jos.
Diagramele de secvente sunt utilizate cand se descrie comportamentul mai multor obiecte in acelasi usecase. Cand se doreste descrierea comportamentului unui singur obiect acoperind comportamentul din mai multe usecase-uri se foloseste o diagrama de stare State diagram .
44
interactioneaza le poate plasa in pachete sau poate evoca alte trasaturi ale acestora. Ca si scop, in timp ce diagramele de secventa isi propun sa arate ordinea temporala a mesajelor, cele de colaborare arata relatia structurala intre obiectele ce colaboreaza.
CellularRadio
Display
Microphone
Telephone
Button
Speaker
Dialer
Pentru a intelege mai bine o sa incercam sa modelam modul in care un utilizator de telefonie mobila da un telefon. Prin intelegerea fenomenului putem descrie scenariul prezentat mai jos.
45
Usecase : Da un telefon (versiunea 1) 1. Utilizatorul apasa butoanele pentru a forma numarul 2. Utilizatorul apasa butonul verde Send 3. Se stabileste conexiunea cu reteaua 4. Se stabileste conexiunea cu interlocutorul Prima versiune de usecase este doar o versiune de inceput ce ignora multe detalii necesare realizarii unui astfel de sistem. Spre exemplu, nu se specifica faptul ca la apasarea butoanelor se aude un mic ton si se actualizeaza ecranul telefonului. Este necesara asadar elaborarea unei noi versiuni imbunatatite. Usecase : Da un telefon (versiunea 2) 1. Utilizatorul apasa butoanele pentru a forma numarul 2. La fiecare digit apasat, ecranul se actualizeaza si digital de adauga la numarul de telefon de format 3. La fiecare digit apasat se genereaza tonul corespondent ce se emite in difuzor 4. Utilizatorul apasa butonul verde Send 5. Indicatorul in use se aprinde pe ecran 6. Se stabileste conexiunea cu reteaua 7. Digitii acumulati se trimit retelei 8. Se stabileste conexiunea cu interlocutorul Chiar daca si aceasta versiune este simplista ea serveste scopului nostru cu succes. Usecase-ul de mai sus expliciteaza destul de clar procedura de telefonare. Ceea ce nu este evident este cum vor interactiona obiectele din modelul static (din diagrame de
46
clase) in cadrul acestei proceduri. Putem sa ne imaginam urmatoarele : obiectul Button emite un mesaj Digit la Dialer. Dialer-ul cere obiectului Display sa afiseze digitul si cere difuzorului (Speaker) sa emita tonul corespunzator. Digitii apasati se acumuleaza intr-o lista ce formeaza numarul de telefon. Cand se apasa butonul Send obiectul Button trimite mesajul Send la Dialer. Dialer trimite un mesaj de conectare connect la sistemul celular (trimitand si numarul de telefon din lista acumulata). Acesta din urma cere obiectului Display sa lumineze indicatorul in use .
cld: Collaboration diagram: Telefoneaza
:Speaker
1.2 : EmitTone(code)
Send :Button
De mentionat : Dreptungiurile din aceste diagrama reprezinta obiecte NU clase. Nu este corect sa cream o asociere intre doua obiecte in aceste diagrame daca nu exista una corespunzatoare in diagrama de clase (liniile conectoare din diagramele de colabore sunt instante ale asocierilor din diagrama de clase).
47
Se observa ca obiectele si mesajele utilizate in diagrama de colaborare sunt altele decat ceea ce s-a prezentat in modelul static. S-a incalcat chiar ultima regula mentionata mai sus. Avem legaturi care nu reflecta asocieri din structura statica. Problema poate sa fie faptul ca modelul nostru dinamic (Figura 18 - Diagrama Colaborare pentru telefonare) este incorect. Ar trebui sa fortam modelul dinamic sa respecte modelul static dar cum ar arata noul model dinamic ? Noul model dinamic ar avea un element central Telefon care va receptiona mesaje de peste tot pe care le va transfora in mesaje trimise de catre el la componenta respectiva. Un astfel de sistem este deosebit de cuplat. Telefon ar fi un controler principal care va trebui sa stie cate ceva despre toate obiectele din jurul lui si toate ar trebui sa stie despre el. Intreaga inteligenta a sistemului este plasata in acest obiect. Astefel de obiecte principale devin puternic interconectate ducand la o rezistenta sporita la modificari (cand se schimba o componenta a sistemului este posibil ca toate celelalte sa trebuiasca modificate). Un model ca in Figura 18 este cel corect din punct de vedere OOP, responsabilitatile obiectelor sunt puternic separate, fiecare obiect avand cate putina inteligenta si nu exista un obiect special atotstiutor. Astgel de modele duc la cuplaje reduse si implicit la sisteme modificabile. Totusi spuneam ca nu respectam diagrama de clase (modelul static). Nu ramane de facut decat sa-l schimbam pe acesta. Noul model facut conform cu modelul dinamic este prezentat mai jos.
48
cd: Telefon v2
Display
Speaker
Dialer
CellularRadio
Button
Figura 19 - Noul model static pentru Telefon. De aceasta data respecta modelul dinamic
De aceasta data se observa ca s-au adaugat si ceva operatii in clasele modelului static. Acest lucru a fost posibil datorita faptului ca modelul dinamic a cerut aceste metode. Noul model ar putea parea inconfortabil pentru ca nu arata intocmai un telefon asa cum el este in lumea reala clasa Telephone a disparut clasa Microphone a disparut
49
Toate cele mentionate mai sus sunt bazate pe structura fizica a telefonului si nu are nici o legatura cu comportamentul acestuia. Noul model se bazeaza pe comportamentul real al telefonului. Clasele disparute nu sunt o problema deoarece ele nu au nici un sens din moment ce nu sunt prinse in niciun comportament descris. (este posibil ca ele sa reapara in alt usecase, caz in care vor fi reinserate)
50
Button
+doSomething ():void
DigitButton
SendButton
Dialer
51
Ideea este de a avea o clasa generala Button care nu stie sa faca nimic altceva decat sa cheme propria metoda doSomething . O clasa particulara (SendButton) strans legata de Dialer, implementeaza acea metoda. Intreaga interactiune (mouse, tastatura, apasare buton) este incapsulata in clasa generica Button. Din exterior clasa Button ofera doar o metoda care se poate rescrie. Tot un exemplu de cuplaj este clasa Display. De aceasta data problema este diferita dar solutia este asemanatoare. Nu se pune problema reutilizarii clasei Diplay fiind o clasa particulara acestui sistem dar exista posibilitatea ca la un moment dat telefonul sa fie produs cu un alt display decat cel proiectat initial. In acest caz ar trebui sa avem la fel ca mai sus, sistemele dependente de AbstractDisplay iar Display sa implenteze interfata respectiva. O a treia problema a clasei Display este faptul ca ea are doua responsabilitati diferite : sa afiseze digitul apasat sa afiseze mesaje in use
Clasa CellularRadio este interesata de Display doar din puncul de vedere al mesajului InUse. Dialler-ul este interesat de Display numai din punctul de vedere al metodei de afisare a digitului. Daca cele doua puncte de vedere sunt strans legate este posibil ca la o modificare a clasei Display venita ca o cerinta a clasei Dialer sa fie afectata si clasa CellularRadio. Un astfel de lucru este de nedorit si poate sa fie evitat prin segregarea interfetelor.
52
Dialer
CellularRadio
DialerDisplay
CRDisplay
+displayDigit():void
+inUse():void
Display
In afara acestor probleme prezentate aici se vor prezenta si alte probleme pe parcursul laboratoarelor urmatoare.
5.1 EXERCITII
1. Creati modelul de clase actualizat cu cele doua modificari 2. Creati noua diagrama de colaborare conforma cu noul model in care sau operat modificarile din acest laborator
53
6.1.2 VIZUALIZAREA
Vizualizarea afiseaza continutul modelului. El acceseaza date din model si specifica cum se vor prezenta utilizatorului aceste date. Cand modelul se schimba, este responsabilitatea vizualizarii sa mentina consistenta prezentarii. Vizualizarea inainteaza gesturile utilizatorului controlerului.
6.1.3 CONTROLERUL
Controlerul defineste comportamentul aplicatiei; interpreteaza gesturile utilizatorului si le transforma in actiuni asupra modelului. Intr-o aplicatie GUI standalone, gesturile utilizatorului pot fi clickurile de butoane sau selectii in meniuri. Intro aplicatie WEB, aceste gesturi apar ca cereri http GET sau POST la nivelul Web (Web tier).
54
In figura urmatoare se pot regasi cele explicate mai sus. Liniile intrerupte sunt evenimente iar cele pline sunt invocari de metode. In dreptunghiuri s-au scris responsabilitatiile claselor cu rolul respectiv.
55
3) CONTROLLER-ul pastreaza o referinta inspre MODEL. Atunci cand un utilizator actioneaza asupra VIEW-ului, au loc urmatoarele: 1) VIEW-ul simte ca are loc o actiune (de exemplu, are loc apasare unui buton sau mutarea unui scrollbar) utilizand un listener inregistrat pentru acel eveniment specific. 2) VIEW-ul invoca metoda aleasa din CONTROLLER 3) CONTROLLER-ul acceseaza modelul eventual modificandu-l in concordant cu actiunea utilizatorului 4) MODEL-ul notifica listenerii interesati despre schimbare (de exemplu VIEWul). In unele arhitecturi este posibil ca CONTROLLER-ul sa fie responsabil si cu actualizarea VIEW-ului (des utilizat in arhitectura J2EE)
56
6.3 EXERCITII
1) Trasati diagrama de clase pentru exemplul de mai sus 2) Trasati diagrama de colaborare si actualizati diagrama de clase daca este necesar 3) Realizati un program pe arhitectura MVC care sa realizeze o fereastra in care sa fie trei controale JTextField (XPosition, YPosition, Radius) JTextField si un JPanel in care o sa fie pus un cerc la coordonatele date de XPosition si YPosition, de raza Radius. Utilizatorul va putea influenta modelul prin modificarea valorilor din JTextField-uri. Atentie !!! Modelul NU trebuie sa pastreze vreo referinta catre VIEW. Pt cateva consideratii Java-Interfata vezi urmatoarea sectiune.
57
4) Inlocuiti controalele JTextField cu JSlider 5) Adaugati in interfata un buton VIEW2X care deschide o fereastra noua in care se poate vedea cercul pozitionat intr-un panel de doua ori mai mare decat primul. Modificarea campurilor XPosition, YPosition si Radius va actualize ambele VIEW-uri.
6.4.1 FERESTRE
Pentru a crea o fereastra se foloseste clasa JFrame. Zona pe care se pun componentele se obtine prin apelul frame.getContentPane(). Mai jos oferim un exemplu de cod ce va deschide o fereastra si va pune un buton pe aceasta.
JFrame f = new JFrame(Titlul Ferestrei); f.getContentPane().add(new JButton(Test)); f.setSize(320,200); f.setVisible();
Adaugarea componentelor se poate face la coordinate fixe sau putem lasa java sa le aranjeze dinamic dupa reguli stabilite de noi astfel ca la redimensionarea ferestrei acestea sa fie plasate automat in pozitii optime. Sectiunea de cod de mai jos, adauga un buton intr-o fereastra, la coordinate fixe:
JFrame f = new JFrame(Titlul Ferestrei); JButton b = new JButton(Test); f.getContentPane().setLayout(null); f.getContentPane().add(b); b.setBounds(10 /* X */,40 /* Y */, 50 /* latime */, 20 /* inaltime */);
58
f.setSize(320,200); f.setVisible();
Layout-ul null se poate folosi in special in ferestre ce nu se pot redimensiona. Pentru celelalte cazuri se recomanda utilizarea unui layout existent sau construirea unui nou layout (pentru programatorii experimentati). Cele mai des utilizate layout-uri sunt: BorderLayout, FlowLayout, GridLayout, GridBagLayout. 6.4.1.1 BorderLayout, FlowLayout Prin utilizarea acestui mod de aranjare se pot aranja maxim cinci component pe un
JPanel (una la nord, una la sud, una la est, una la vest si una in centru).
Chiar daca un layout ce permite adaugarea a doar cinci component pare foarte limitat, acesta este cel mai utilizat deoarece spre exemplu componenta plasat la nord poate sa fie un panel cu mai multe component plasate in el.
JPanel panelNorth = new JPanel(); // panoul Nordic va avea doua butoane pe el plasate unul dupa altul panelNorth.setLayout(new FlowLayout()); panelNorth.add(new JButton(buton 1)); panelNorth.add(new JButton(buton 2)); // in fereastra, panoul Nordic este plasat la nord frame.getContentPane().setLayout(new BorderLayout()); frame.getContentPane().add(panelNorth, BorderLayout.NORTH); // in centru se plaseaza un JTextArea frame.getContentPane().add(new JTextArea(), BorderLayout.CENTER);
6.4.1.2 GridLayout Utilizand GridLayout componentele sunt aranjate intr-o tabela secvential incepand din coltul stanga sus. De exemplu:
59
JPanel p = new JPanel(); p.setLayout(new GridLayout(3,2)); p.add(new JLabel(label 1)); p.add(new JLabel(label 2)); p.add(new JButton(Buton 1)); p.add(new JLabel(label 4));
va avea ca effect o plasare a celor patru component ca mai jos: Label1 Label2 Button1 Label4
De remarcat ca in java apparent se poate construe o instanta de interfata (ActionListener este o interfata nu o clasa). De fapt nu se construieste o interfata ci se creaza o instant a unei clase interne (in care actionPerformed nu mai este abstract).
60
Daca dorim sa fim anuntati cand un JTextField este modificat putem sa inregistram un listener la documentul (modelul componentei JTextField in concordant cu MVC) acestuia:
textField.getDocument().addDocumentListener().
61
In acest design, VIEW-ul nu se inregistreaza direct la model ca in MVC standard ci prin intermediul CONTROLLER-ului. Din acest motiv, CONTROLLER-ul mediaza curgerea de informatii de la MODEL la VIEW (in ambele directii). Cand cele trei component sunt instantiate, CONTROLLER-ul se inregistraza ca listener la VIEW si MODEL. Cand utilizatorul interactiuneaza cu VIEW-ul: 1) VIEW-ul simte ca are loc o actiune (de exemplu, are loc apasare unui buton sau mutarea unui scrollbar) utilizand un listener inregistrat pentru acel eveniment specific. 2) VIEW-ul invoca metoda aleasa din CONTROLLER 3) CONTROLLER-ul acceseaza modelul eventual modificandu-l in concordanta cu actiunea utilizatorului
62
4) MODEL-ul notifica listenerii interesati despre schimbare (CONTROLLER-ul in acest caz) Avantajul major al acestei tehnici este posibilitatea de decuplare completa a MODEL-ului de VIEW. CONTROLLER-ul poate dicta proprietatile ce il intereseaza din unu sau mai multe modele. Deasemenea, CONTROLLER-ul poate oferi metodele de modificare a acestor proprietati mai multor VIEW-uri .
6.6 EXERCITII
1) Creati o clasa AbstractModel
public abstract class AbstractModel { protected PropertyChangeSupport propertyChangeSupport; public AbstractModel() { propertyChangeSupport = new PropertyChangeSupport(this); } public void addPropertyChangeListener(PropertyChangeListener listener) { propertyChangeSupport.addPropertyChangeListener(listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { propertyChangeSupport.removePropertyChangeListener(listener); } protected void firePropertyChange(String propertyName, Object Object newValue) { propertyChangeSupport.firePropertyChange(propertyName, newValue); } } oldValue, oldValue,
2) Extindeti aceasta clasa intr-o noua clasa TextElementModel avand urmatorii membri:
63
private String text; private private private private private Font font; Integer x; Integer y; Integer opacity; Integer rotation;
3) Puneti la dispozitie mijloace de initializare, set, get pentru acesti membrii 4) Creati un controller abstract ca in secventa de mai jos:
64
public abstract class AbstractController implements PropertyChangeListener { private ArrayList registeredViews; private ArrayList registeredModels; public AbstractController() { registeredViews = new ArrayList(); registeredModels = new ArrayList(); } public void addModel(AbstractModel model) { registeredModels.add(model); model.addPropertyChangeListener(this); } public void removeModel(AbstractModel model) { registeredModels.remove(model); model.removePropertyChangeListener(this); } public void addView(AbstractViewPanel view) { registeredViews.add(view); } public void removeView(AbstractViewPanel view) { registeredViews.remove(view); } // Use this to observe property changes from registered models // and propagate them on to all the views. public void propertyChange(PropertyChangeEvent evt) { for (AbstractViewPanel view: registeredViews) { view.modelPropertyChange(evt); } } /** * This is a convenience method that subclasses can call upon * to fire property changes back to the models. This method * uses reflection to inspect each of the model classes * to determine whether it is the owner of the property * in question. If it isn't, a NoSuchMethodException is thrown, * which the method ignores. * * @param propertyName = The name of the property. * @param newValue = An object that represents the new value * of the property. */ protected void setModelProperty(String propertyName, Object newValue) { for (AbstractModel model: registeredModels) { try { Method method = model.getClass(). getMethod("set"+propertyName, new Class[] { newValue.getClass() } ); method.invoke(model, newValue); } catch (Exception ex) { // Handle exception. } } } }
65
5) In AbstractController se pastreaza doi vectori: unul reprezinta lista de obiecte MODEL inregistrate, iar unul lista de obiecte VIEW inregistrate. Cand un nou MODEL se inregistreaza, CONTROLLER-ul se inregistreaza si el ca un listener la model. Construiti un DefaultController (extensie a celui abstract) si completati partile omise.
public class DefaultController extends AbstractController { public public public public public public // static static static static static static final final final final final final String String String String String String ELEMENT_TEXT_PROPERTY = "Text"; ELEMENT_FONT_PROPERTY = "Font"; ELEMENT_X_PROPERTY = "X"; ELEMENT_Y_PROPERTY = "Y"; ELEMENT_OPACITY_PROPERTY = "Opacity"; ELEMENT_ROTATION_PROPERTY = "Rotation";
public void changeElementText(String newText) { setModelProperty(ELEMENT_TEXT_PROPERTY, newText); } public void changeElementFont(Font newFont) { setModelProperty(ELEMENT_FONT_PROPERTY, newFont); } public void changeElementXPosition(int newX) { setModelProperty(ELEMENT_X_PROPERTY, newX); } public void changeElementYPosition(int newY) { setModelProperty(ELEMENT_Y_PROPERTY, newY); } public void changeElementOpacity(int newOpacity) { setModelProperty(ELEMENT_OPACITY_PROPERTY, newOpacity); } public void changeElementRotation(int newRotation) { setModelProperty(ELEMENT_ROTATION_PROPERTY, newRotation); } }
6) Construiti doua VIEW-uri: unul editor al proprietatilor din model (PropertyEditorPanel) si un VIEW grafic. Ambele VIEW-uri vor fi extensii ale lui JPanel inserate intr-un JFrame respectiv intr-un JDialog. Dialogul va
66
permite utilizatorului sa actualizeze valorile din model. Panel-ul din Frame va permite vizualizarea grafica a textului asa cum va arata acesta la momentul respectiv. Constructorul clasei PropertyEditorPanel si a metodei localInitialization:
public PropertiesViewPanel(DefaultController controller) { this.controller = controller; initComponents(); localInitialization(); } public void localInitialization() { opacitySpinner.setModel(new SpinnerNumberModel(100, 0, 100, 1)); opacitySlider.setModel(new DefaultBoundedRangeModel(100, 0, 0, 100)); rotationSpinner.setModel(new SpinnerNumberModel(0, -180, 180, 1)); rotationSlider.setModel(new DefaultBoundedRangeModel(0, 0, -180, 180)); text.getDocument().addDocumentListener(new DocumentListener() { public void insertUpdate(DocumentEvent e) { textDocumentChanged(e); } public void removeUpdate(DocumentEvent e) { textDocumentChanged(e); } public void changedUpdate(DocumentEvent e) { textDocumentChanged(e); } }); }
7) Sectiunea de cod de mai jos este chemata de catre CONTROLLER de fiecare data cand MODEL-ul raporteaza ca a fost modificat.
67
if (evt.getPropertyName().equals( DefaultController.ELEMENT_X_PROPERTY)) { String newStringValue = evt.getNewValue().toString(); xPositionTextField.setText(newStringValue); } else if (evt.getPropertyName().equals( DefaultController.ELEMENT_Y_PROPERTY)) { String newStringValue = evt.getNewValue().toString(); yPositionTextField.setText(newStringValue); } else if (evt.getPropertyName().equals( DefaultController.ELEMENT_OPACITY_PROPERTY)) { int newIntegerValue = (Integer)evt.getNewValue(); opacitySpinner.setValue(newIntegerValue); opacitySlider.setValue(newIntegerValue); } else if (evt.getPropertyName().equals( DefaultController.ELEMENT_ROTATION_PROPERTY)) { int newIntegerValue = (Integer)evt.getNewValue(); rotationSpinner.setValue(newIntegerValue); rotationSlider.setValue(newIntegerValue); } else if (evt.getPropertyName().equals( DefaultController.ELEMENT_TEXT_PROPERTY)) { String newStringValue = evt.getNewValue().toString(); text.setText(newStringValue); } else if (evt.getPropertyName().equals( DefaultController.ELEMENT_FONT_PROPERTY)) { Font f = (Font)evt.getNewValue(); String fontString = f.getFontName() + " " + f.getSize(); font.setText(fontString); currentFont = f; } // } Remainder of the code omitted
8) Cand se transmit evenimente GUI, se vor chema listeneri din codul urmator:
68
private void yPositionTextFieldFocusLost(java.awt.event.FocusEvent evt) { try { controller.changeElementYPosition( Integer.parseInt(yPositionTextField.getText())); } catch (Exception e) { // Handle exception. } } private void yPositionTextFieldActionPerformed(java.awt.event.ActionEvent evt) { try { controller.changeElementYPosition( Integer.parseInt(yPositionTextField.getText())); } catch (Exception e) { // Handle exception. } } // // { JFontChooserDialog fontChooser = new JFontChooserDialog((Dialog)this.getTopLevelAncestor()); fontChooser.setSelectedFont(currentFont); fontChooser.setVisible(true); Font returnedFont = fontChooser.getSelectedFont(); if (returnedFont != null) { controller.changeElementFont(returnedFont); } } private void opacitySliderStateChanged(javax.swing.event.ChangeEvent evt) { controller.changeElementOpacity((int)opacitySlider.getValue()); } private void rotationSliderStateChanged(javax.swing.event.ChangeEvent evt) { controller.changeElementRotation((int)rotationSlider.getValue()); } private void opacitySpinnerStateChanged(javax.swing.event.ChangeEvent evt) { controller.changeElementOpacity((Integer)opacitySpinner.getValue()); } private void rotationSpinnerStateChanged(javax.swing.event.ChangeEvent evt) { controller.changeElementRotation((Integer)rotationSpinner.getValue()); } private void textDocumentChanged(DocumentEvent evt) { Document document = evt.getDocument(); try { controller.changeElementText(document.getText(0, document.getLength())); } catch (BadLocationException ex) { // Handle exception. } } Code omitted -- code for xPosition is nearly the same as for yPosition.
69
9) Creati intregul program urmarind cu atentie pasii de mai sus. Rulati programul si observati ce se intampla. Tocmai ati realizat un program bine proiectat dar care din pacate functiuneaza. Sa incercam sa intelegem ce se intampla si sa reparam problema. Pentru aceasta sa consideram urmatorul lant de evenimente: a. Utilizatorul actioneaza un buton b. Componenta Swing receptioneaza evenimentul si anunta listenerii c. Se cheama metoda asociata din controller d. Modelul se actualizeaza. e. Modelul notifica controlerul ca si-a schimbat valoarea f. Vizualizarea (VIEW) receptioneaza un eveniment de la controller si incearca sa reseteze valoarea componentei Swing asociate g. Metoda asociata din controller ere anuntata In acest moment in functie de component Swing utilizata si robustetea modelului utilizat se pot intampla urmatoarele: a. Componenta swing de la care a pornit modificarea refuza sa se reactualizeze stiind ca ea este chiar sursa evenimentului. Componentele text din Swing intra in aceasta categorie b. Modelul sesizeaza ca a doua cerere de update este echivalenta cu prima si nu mai trimite notificari de schimbare in acest caz. Acest model este implementat implicit in PropertyChangeSupport din pachetul java.beans. Totusi al doilea update al modelului este totusi chemat. c. Nu se pune nici un fel de conditie de siguranta (nici in model nici in componenta Swing) aplicatia cicland la infinit
70
71
72
Fragilitate. Aceasta este tendinta sofwarelui de a "crapa" in multe locuri la fiecare schimbare a lui. De multe ori defectul apare in arii care nu au nici o legatura dpdv concepual cu modulul ce a fost modificat. Uneori astfel de sisteme sunt imposibil de mentinut. La fiecare rezolvare unui bug, apar si mai multe problem decat s-au rezolvat. Imobilitate. Se refera la imposiilitatea reutilizarii softwarelui provenit de la alte proiecte sau chiar de la acelasi. Vascozitatea. Are doua forme: vascozitatea designului si cea a mediului. Cand sunt pusi in fata unei schimbari, inginerii gasesc mai multe posibilitati de a realiza aceasta, unele respectand designul iar altele nu. Cand metodele care respecta designul sunt mai greu de implementat decat "hack"-urile, se spune ca vascozitatea designului este mare. Vascozitatea mediului apare cand mediile de dezvoltare cu care se lucreaza sunt incete si ineficiente. De exemplu daca timpul de compilare este foarte mare, inginerii vor fi tentati sa faca schimbari care nu obliga la compilari mari, chiar daca aceste schimbari nu sunt foarte curate dpdv design.
73
Un modul trebuiste sa fie deschis pentru extindere dar inchis pentru modificari. Este cel mai important principiu de proiectare oriectat obiect. Ideea e ca trebuie sa scriem modulele in asa fel incat sa poata fi extinse fara a fi necesar modificarea lor. Cu alte cuvinte dorim sa putem schimba ceea ce modulele fac fara a schimba nimic in codul acestora. Aceasta ar putea suna contradictoriu dar exista mai multe tehnici de realizare acestui lucru. Toate aceste tehnici se bazeaza pe abstractiune. Mai jos se prezinta mai multe astfel de tehnici. 7.6.1.1 Polimorfism dinamic Se dau urmatoarele structuri C++
struct Modem { enum Type { hayes, courrier, ernie} type; } struct Hayes { Modem::Type type; } struct Courrier { Modem::Type type; } struct Ernie { Modem::Type type; } si functia void LogOn(Modem& m ,string& pno, string& user, string& pw) { if (m.type == Modem::hayes) DialHayes((Hayes&)m, pno); else if (m.type == Modem::courrier) DialCourrier((Courrier&)m, pno);
74
else ...... }
Aici fiecare tip de modem depinde de structura Modem::Type si la adaugare unui nou tip de modem, modulul trebuie remodificat si recompilat. In acest stil, codul este presarat cu sectiuni if else si pentru o modificare in acest cod trebuie platit un tribut foarte mare si anume faptul ca trebuie reanalizata intreaga clasa Modem pentru a vedea ce implicatii are modificarea. Rezolvarea acestei probleme se face prin implementarea unei arhitecturi ca mai jos.
Respectand OCP, functia LogOn depinde doar de interfata Modem, adaugarea unui modem nu mai are nici un impact asupra lui LogOn. In acest caz s-a creat un modul care poate fi extins fara a fi modificat. In acest caz LogOn s-a transformat in ceva de genul
void LogOn(Modem& m ,string& pno, string& user, string& pw) {
75
m.Dial(pno); }
7.6.1.2 Problema propusa Implementati in Java arhitectura propusa mai sus. 7.6.1.3 Polimorfism static O alta tehnica de a fi conform metodei OCP este folosirea template-urilor ca in exemplul de mai jos.
template <typename MODEM> void LogOn(MODEM& m ,string& pno, string& user, string& pw) { m.Dial(pno); }
7.6.1.4 Scopurile arhitecturale ale metodei OCP Folosind aceste tehnici se pot crea module extensibile fara schimbari. Idealul poate fi foarte dificil de atins dar chiar si un OCP partial poate duce la imbunatatirea de neneglijat a structurii aplicatiei. 7.6.1.5 Un alt exemplu de incalcare OCP in Java In urmatorul exemplu vom prezenta o clasa utilizata pentru alocarea resurselor.
76
package ro.ulbs.ip.ocpvio; public class ResourceAlocator { public int allocate(int resourceType) { int resourceId = -1; switch (resourceType) { case 2 /* TIME SLOT */: resourceId = findFreeTimeslot(); markTimeslotBusy(resourceId); break; case 3 /* SPACE SLOT */: resourceId = findFreeTimeslot(); markTimeslotBusy(resourceId); break; default: System.err.println("Attempted to allocate resource\n"); break; } return resourceId; } public int free(int resourceType, int resourceId) { switch (resourceType) { case 2 /* TIME SPLT */: resourceId = markTimeslotFree(resourceId); break; case 3 /* SPACE SLOT */: resourceId = markTimeslotFree(resourceId); break; default: System.err.println("Attempted to free resource\n"); break; } return resourceId; } private int markTimeslotFree(int resourceId) { // TODO Auto-generated method stub return 0; } private void markTimeslotBusy(int resourceId) { // TODO Auto-generated method stub } private int findFreeTimeslot() { // TODO Auto-generated method stub return 0; } }
invalid
invalid
77
Din exemplul de mai sus se poate observa cu usurinta incalcarea principiului OCP. De cate ori se va adauga un nou tip de resurse (resourceType), codul de mai sus trebuie retestat. Programatorul ce doreste adaugarea unu nou tip de resurse trebuie sa inteleaga implementarea interna a clasei de alocare (ResourceAlocator). Codul de mai sus, se poate inlocui cu :
package ro.ulbs.ip.ocp; import java.util.Hashtable; public class ResourceAllocator { Hashtable /*<Integer, ResourcePool>*/ m_pResourcePool = new Hashtable(); int allocate(Integer resourceType) { int resourceId; ResourcePool resourcePool = resPoolByType(resourceType); resourceId = resourcePool.findFree(); resourcePool.markBusy(resourceId); return resourceId; } private ResourcePool resPoolByType(Integer resourceType) { return ((ResourcePool)m_pResourcePool.get(resourceType)); } int free(Integer resourceType, int resourceId) { resPoolByType(resourceType).markBusy(resourceId); return resourceId; } void addResourcePool(Integer resourceType, ResourcePool pPool) { m_pResourcePool.put(resourceType, pPool); } }
La adaugarea unui nou tip de resurse, codul nu mai trebuie testat nefacundu-se modificari acestuia in acest al doilea caz. Progarmatorul care doreste sa adauge noul
78
CLOSED la modificari. Cel ce adauga un nou tip de resursa trebuie doar sa cunoasca interfata ResourcePool. Care este interfata ResourcePool (se poate afla cu usurinta din codul de mai sus). Cum se implementeaza TimeslotResourcePool ? Dar SpaceslotResourcePool ? Creati codul java aferent.
Deci, conform schemei de mai sus, daca functia User accepta ca si parametru un
Base, ar trebui sa fie legala si pasarea unui parametru Derived.
Pare a fi normal sa se intample acest lucru dar sunt multe situatii care trebuiesc considerate.
79
Exemplul clasic este dilema cerc/elipsa. 7.6.2.1 Dilema cerc/elipsa In scoala, cei mai multi dintre noi au invatat ca cercul este doar o degenerare a unei elipse. Toate cercurile sunt de fapt elipse in care focarele coincid. Aceasta "teorie" ne face sa fim tentati sa alegm un model ca in figura.
Chiar daca acesta ne satisface dpdv model conceptual, sunt totusi mai multe impedimente. O privire mai atenta la declaratia elipsei incep sa faca lumina in aceasta dilema. Se observa ca elipsa are trei elmente (cele doua focare si axa mare) ceea ce pentru cerc este inacceptabil. Daca totusi nu ne-ar pasa de spatiul ocupat am putea face ca Circle sa se comporte corect, suprascriind metodele de setare a focarelor a.i. cele doua focare sa aiba aceeasi valoare. In aceste condiii am putea considera axa mare ca diametru. Totul pare sa mearga dar clientii ruineaza totul.
80
7.6.2.2 Clientii ruineaza totul Cercurile si elipsele cu sunt singure in univers. Intre entitatile existente existe niste interfete, iar aceste interfeta implica un contract. Acest contract poate sa nu fie prevazut explicit dar el pur si simplu exista. De exemplu este foarte probabil ca un user al clasei Ellipse sa se astepte ca sectiunea de cod ce urmeazaa sa functioneze.
void f(Ellipse& e){ Point a(-1,0); Point b(1,0); e.setFoci(a,b); e.setMajorAxis(3); assert(e.getFocusA() == a); assert(e.getFocusB() == b); .... }
81
Revenind la LSP, se poate spune ca pentru a fi substituabila, contractul clasei de baza trebuie sa fie onorat si de clasele derivate. Pentru exemplu dat, Circle nu onoreaza contractul pe care il implica coform extinderii lui Ellipse. Pentru a stbili contractul uni metode, trebuie sa se stie contextul in care ea va fi apelata (preconditii) si ce conditii vor fi adevarate la iesirea din metoda (postconditii). Eifel - limbaj de efinire de astfel de contracte. Deci conditiile pentru ca o clasa D sa poata inlocui clasa de baza B sunt: preconditiile lui D sa nu fie mai puternice ca le lui B postconditiile lui D sa nu fie mai slabe ca le lui B
Cu alte cuvinte clasele derivate trebuie sa nu se astepte la mai mult si s nu dea mai putin. 7.6.2.4 Repescursiuni ale violarii LSP Violarea de acest gen este greu de detectat inainte de a fi prea tarziu. (In exemplul cu cercul, ea a fost detectata cand totul parea sa mearga bine dar un client s-a plans). Uneori costurile de reparare corecta a violarii LSP sunt mult prea mari si se prfera utilizarea unor if-uri (daca e cerc sa nu mi verifice toate atributele). Se poate observa ca violarea LSP este o violare latenta OCP. 7.6.2.5 Exercitiu Se considera proiectarea unui sistem care regleaza temperatura in mai multe camere intr-un sistem. Sistemul software citeste periodic temperatura din fiecare camera si o ajusteaza la o temperatura de referinta. Comportamentul este modelat intr-o clasa de baza ControlerTemperatura. Interfata de programareeste diferita per camera (diferentele sunt manuite prin extinderea clasei de baza in cateva subclase).
82
Controller-ul suporta urmatoareale metode : set/get ReferenceTemperature : nu sunt metode virtuale deoarece
temperatura de referinta nu este dependenta de vreun dispozitiv de intrare iesire. Initialize : initializeaza adresa portului getTemperature : metoda pur virtuala, registri pentru citirea temperaturii
difera de la dispozitiv la dispozitiv. adjustTemperature : aplica ajustajul aplicat ca parametru. Metoda este pur
virtuala, implementarea acesteia difera de la dispozitiv la dispozitiv. Creati clasa ControlerTemperatura cu cele patru metode. Creati o clasa
ControlerTemperaturaFirmaX
care
implementeaza
ve realiza prin utilizarea io_write(253, temperatura). Implementati io_read si io_write ca doua metode goale in ControllerTemperatura. Se poate crea inca o clasa ControlerTemperaturaFirmaY pentru care portul de intrare iesire este altul. Sa presupunem acum ca departamentul de marketing decide sa suporte si controlerele de temperatura produse de firma Z. Din pacate, dispozitivele produce de firma Z nu sunt perfect compatibile cu ControllerTemperatura : Z produce dispozitive automate in care se programeaza temperatura de referinta. Intreg ciclul de ajustare este facut automat. Tentatia este de a realiza o clasa ControlerTemperaturaFirmaZ in care se face o implemetare blank pentru metoda
adjustTemperature
iar
83
Totul pare bine la prima vedere dar sa presupunem ca softul de management este realizat astfel:
ControlerTemperatura tempController = next() ; tempController.setReferenceTemperature(10) ; tempController.initialize() ; x = tempController.getTemperature() ;
Se observa ca dispozitivul de initializeaza dupa ce dj s-au apelat functii hardware din acesta, ceea ce poate duce la rezultate haotice (chiar la defectarea sistemului).
84
Arhitectura OO arata o alta structura de dependente unde clasele de nivel mai inalt depind de abstractiuni.
In acest caz detaliile depind de abstractiunile de nivel mai inalt ci nu invers dependenta a fost inversata. 7.6.3.1 Dependenta de abstractiuni Implicatia acestui principiu este simpla: dependentele trebuie sa aiba ca tinta clase abstracte nu concrete. Motivul este si el simplu: lucrurile concrete se schimba frecvent. Tehnologiile cum sunt COM ne obliga la respectarea acestui principiu (intre componente) din moment ce doar interfata abstracta a componentei este vizibila. 7.6.3.2 Crearea obiectelor Unul dintre locurile unde nu se pot face dependente la clase abstracte este crearea obiectelor (nu se pot crea obiecte abstracte). Aparent nu exista nici o posibilitate de
85
a iesi din acest impas dar totusi exista o solutie eleganta numita AbstractFactory - un pattern ce va fi studiat mai tarziu. 7.6.3.3 Exemplu Sa presupunem ca dorim sa realizam o stiva de protocoale RLC (radio link control). Cele trei nivele sunt : RLC Physical Layer, RLC Datalink Layer si RLC Network Layer. Fiecare nivel este modelat ca o clasa. Un scheleton al acestora (in C++) este prezentat mai jos :
86
class RLC_Physical_Layer { RLC_Datalink_Layer *m_p_RLC_Datalink_Layer; public: void Device_Transmit(Datagram *p_Datagram) { Apply_Physical_Layer_Headers(p_Datagram); Write_Message_To_Device(p_Datagram); } void Handle_Device_Receive(Datagram *p_Datagram) { Remove_Physical_Layer_Header(p_Datagram); m_p_RLC_Datalink_Layer->Handle_Physical_Layer_Receive(p_Datagram); } }; class RLC_Datalink_Layer { RLC_Physical_Layer *m_p_RLC_Physical_Layer; RLC_Network_Layer *m_p_RLC_Network_Layer; public: void Datalink_Transmit(Datagram *p_Datagram) { Process_Datalink_Transmit(p_Datagram); m_p_RLC_Physical_Layer->Device_Transmit(p_Datagram); } void Handle_Physical_Layer_Receive(Datagram *p_Datagram) { Process_Datalink_Receive(p_Datagram); m_p_RLC_Network_Layer->Handle_Network_Layer_Receive(p_Datagram); } }; class RLC_Network_Layer { RLC_Datalink_Layer *m_p_RLC_Datalink_Layer; Application_Layer *m_p_Application_Layer; public: void Network_Transmit(Datagram *p_Datagram) { Process_Network_Layer_Transmit(p_Datagram); m_p_RLC_Datalink_Layer->Datalink_Transmit(p_Datagram); } void Handle_Datalink_Layer_Receive(Datagram *p_Datagram) { Process_Network_Layer_Receive(p_Datagram); m_p_Application_Layer->Handle_Application_Receive(p_Datagram); } };
87
Problema majora cu acest cod este faptul ca cele trei clase depind unele de altele, unele avand nevoie de celelalte pentru a-si realiza taskul. Relatiile intre nivele sunt cablate. Nu se poat asambla protocoale noi prin asamblarea a noi seturi de nivele. Schimbari de proiectare intr-un nivel pot avea repercursiuni majore in celelalte. Modificati arhitectura prin introducerea unei clase Nivel Generic . Elementele acestei clase abstracte sunt : un constructor cu doi paramtetrii (nivelul de deasupra si cel de dedesupt), fiecare layer, cand primeste un mesaj de deasupra lui face ce trebuie sa faca iar apoi trimite nivelului de dedesupt mesajul pentru a fi procesat mai departe, cand se receptioneaza un mesaj de la nivelul de dedesupt se proceseaza mesajul si se trimite in sus. Realizati cele patru clase in java. Creati un alt nivel Datalink si observati ca se poate utiliza acelasi nivel fizic fara a modifica vreo linie de cod in acesta. Interpuneti un layer de debug intre nivelul fizic si cel de deasupra lui care nu face altceva decat paseaza mesajele mai departe si scrie niste scurte mesaje.
88
Daca mai multi clienti au nevoie de aceeasi interfata nu mai este necesara o interfata pentru fiecare client.
89
Un criteriu de grupare a claselor in pachete este dupa utilizarea lor, pachetul fiind unitatea de release, normal ca tot pachetul este si unitatea ce se refoloseste. Prinipiul inchiderii comune (CCP) Clasele care se schimba impreuna, apartin aceluiasi pachet Un proiet de dezvoltare mare este impartit intr-un numar mare de pahete interconectate. Cand un pachet se schimba, cantitatea de munca pentru recompilarea tuturor dependentelor poate fi foarte mare ceea ce ne duce cu gandul la o metoda de minimizare a numarului de pachete ce trebuiesc compilate/testate : grupam in acelasi pachet clasele care se schimba impreuna. Avantajul este mare dar si dificultatea de a detecta in avans clasele care sunt dependente si sufera modificari impreuna este mare. Principiul reutilizarii comune (CRP) Clasele care nu sunt foloite impreuna nu trebuisc grupate in acelasi pachet Tensiunea intre principiile de coeziune a pachtelor Problema cu cele trei principii de mai sus este ca ele sunt mutual exclusive (nu pot fi respectate in acelasi timp). REP, CRP fac viata mai usoara celor care reutilizeaza modulele in timp ce CCP se gandeste mai mult la creatorii pachetului (sau la cei ce se ocup de mentenanta). CCP - un singur pachet; CRP - foarte mici ... . Din fericire pachetele nu sunt "batute'n cuie" ele se pot schimba pe parcursul dezvoltarii softului fara mari probleme.
90
Din moment ce unitatea de release este pachetul, cei care lucreaza prefera sa o faca in interiorul unui singur pachet (sa nu lucreze in mai multe pachete deodata). Aceasta tendinta este amplificata de principiile de coeziune care incerca sa tina impreuna clasele care au legatura intre ele. Inainte de a face release unui pachet, ei trebuie sa il testeze si pentru acesta, ei trebuie sa compileze si sa testeze toate pachetele care depind de cel caruia tocmai iau facut release.
Considerand figura de mai sus, daca se doreste livrarea pachetului Protocol, el trebuie sa se bazeze pe ultima versiune a pachetului CommError. Protocol nu mai are alte dependente.
91
Problema apare daca o clasa din CommError dorete sa afieze ceva folosind o componenta din GUI => s-a realizat o dependenta de GUI.
In acest caz, cel ce livreaza Protocol, trebuie sa-si creeze suita de test cu pachetele: CommError, GUI, Comm, ModemControl, Analysis, Database, ceea ce este dezastruos. Pentru a se evita astfel de dezastre, trebuie sa existe cineva care sa asigure inexistenta ciclurilor de dependente intre pachete.
92
Pentru ruperea ciclurilor exista doua metode: prima este crearea unui nou pachet MessageManager, clasele din GUI de care avea nevoie ComError sunt mutate in MessageManager si astfel atat GUI cat si CommError depind de MessageMnager, a doua este prin utilizarea DIP si ISP.
93
Figura 35 Utilizare DIP si ISP. Daca A depinde de X si Y depinde de B se mai poate adauga o interfata BY care are toate metodele de care are neoie Y. Interfetele e bine s fie plasate in pachetel cu clasele care au nevoie de ele nu in clasele ce le implementeaza
7.7.2.2 Principiul dependentelor stabile (SDP) Depinde in directia stabilitatii Stabilitatea. Daca se pune un banut in dunga ... sta dar nu se poate spune ca este stabil. Stabilitatea se refera la cantitatea de efort ce trebuie depus pentru a se face o schimbare. Banutul nu e stabil pentru ca el se rastoarna la cea mai mica incercare. In contrast cu banutul, masa pe care sta, este stabila. Care este legatura cu programarea? Exista mai multi factori care fac un pachet greu de modificat (dimensiuea, complexitatea, claritatea ... dar cel mai important este multitudinea de pachete care depind de cel luat in analiza). Un pachet cu multe dependente de intrare este foarte stabil (X).
94
Pe de alta parte un pachet de care depind foarte putini este putin stabil (la limita ... iresponsabil) (Y). Daca X nu depinde de nimeni spunem ca este independent. Metrici de stabilitate. Stabilitatea unui pachet se poate determina folosind 3 metrici: Ca (Afferent Coupling) - Numarul de clase in afara pachetului care depind de clase din interiorul lui (ingoing dep) Ce (Efferent Coupling) - Numarul de clase din afara pachetului, pentru care clase din pachet depind de ele (outgoing dep) I (Instability) - I = Ce / (Ca + Ce) apartine [0..1]. Acum s-ar putea reformula SDP "Depinde de pachete al carui I este mai mic ca al tau".
95
Intrebarea este daca tot softul trebuie sa fie stabil ? Unul dintre cele mai importante semne designului bun este usurinta cu care acesta se poate modifica. Din aceasta cauza se doreste ca unele parti sa fie instabile.
Cosiderand figura de mai sus vom arata cum SDP poate fi violat. Flexible este un pachet care dorim sa fie usor de modificat. Sa presupunem ca dezvoltatori ai pachetului Stable, realizeaza o dependenta la Flexible. Acesta, violeaza SDP din
96
cauza ca un pachet care se doreste stabil depinde de unul cre se doreste instabil. Ca rezultat, Flexibile nu mai este usor de modificat, orice schimbare avand ca urmare schimbari in Stable si pachetele care depind de acesta. 7.7.2.3 Principiul stabilitatii abstracte (SAP) Pachetele stabile trebuie sa fie abstracte Putem sa ne imaginam sructura de pachete a aplicatiei noastra ca un set de pachete intrconectate, cu pachetele instabile in partea de sus iar cele stabile in partea de jos. In acest imagine, dependentele merg inspre cele stabile. Pachetele de sus sunt usor de modificat dar modificarea celor de jos are un impact foarte puternic. Intrebare care se pune este : Dorim in proiect sa existe pachete greu de modificat? In mod sigur, orice pachet greu de modifict duce la lipsa de flexibilitate. Exista totusi o gaura neagra pe care o putem specula: Pachetele stabile sunt greu de modificat dar daca au fost facute corect sunt usor de extins (utilizand OCP). De fapt SAP este doar o reformulare a DIP. Cele mai stabile pachete trebuie s fie cat mai abstracte. Dar cum se masoara gradul de abstractie? Metrici de abstractie.
Nc - numarul de clase din pachet Na - numarul de clase abstracte din pachet A - A=Na/Nc
SAP vazut prin prisma A si I: I trebuie sa cresca cand A scade. Deci pachetele concrete trebuie sa fie instabile iar cele abstracte stabile.
97
Din figura de mai sus rezulta ca daca A este mare si I este mare, nu are sens. Daca A mic si I mic (sub dreapta) pachetul se ncadreaz in "zona de suferita". Deci frumos ar fi I=1-A dar in realitate nu este posibil. Metrici de distanta D Distanta D=abs(A+I-1)/sqr(2) intre 0 si 0.707 D' Distanta normalizata. D=abs(A+I-1). variaz intre 0 si 1. Aceste metrici msoar arhitectura orientata obiect.
98
8 SABLOANE DE PROIECTARE
Ideea esentiala a sabloanelor de proiectare este reutilizarea unor solutii consecrate la probleme cu probabilitate mare de reaparitie. In general pattern-urile sunt descrise prin: Nume : descrie pe scurt problema ce o rezolva Scop: carui tip de probleme i se adreseaza Motivatie: Exemplu adecvat de problema rezolvata Problema: Desciere pe larg a problemei si a contextului de aparitie Solutia: Descrierea elementelor utilizate si a relatiilor dintre acestea. Consecinte: Orice solutie are implicatii (bune si rele). Acest capitol analizeaza impactul asupra flexibilitatii, extensibilitatii sau portabilitatii sistemului, dupa cum pot sa se refere la aspecte ale implementarii sau limbajului de programare utilizat. Compromisurile sunt de cele mai multe ori legate de spatiu si timp. Creationale Clasa Factory Method Structurale Adapter (class) Comportamentale Interpreter Template Method Chain of Responsibility Abstract Factory Adapter (object) Obiect Builder Prototype Singleton Bridge Composite Decorator Facade Flyweight Command Iterator Mediator Memento Observer State
99
Proxy
Strategy Visitor
100
primesc referinte ale altor obiecte; necesita ca obiectele sa-si respecte unul altuia interfata, ceea ce presupune ca interfetele sa fie proiectate astfel incat sa nu impiedice utilizarea unui obiect in combinatie cu mai multe tipuri de obiecte. Deoarece obiectele sunt accesate doar prin intermediul interfetelor, nu este incalcat principiul incapsularii. In decursul executiei orice obiect poate fi inlocuit cu altul, atata timp cat obiectele respective au acelasi tip. In plus, datorita faptului ca si implementarea unui obiect este scrisa tot in termenii interfetelor altor obiecte, dependentele de implementare vor fi substantial reduse; prin compunerea obiectelor se obtin urmatoarele efecte asupra unui proiect: clasele sunt incapsulate si "concentrate" asupra cate unui singur obiectiv, ceea ce face ca ele, ca si ierarhiile lor sa aiba dimensiuni mici si sa fie mai usor de gestionat. Un proiect bazat pe compunerea obiectelor se caracterizeaza printr-un numar mai mare de obiecte si un numar mai mic de clase, iar comportarea sistemului va depinde de relatiile dintre obiecte, in loc sa fie definita de o anumita clasa.
Experienta arata ca adesea proiectantii folosesc mostenirea in mod abuziv. De aceea se recomanda studiul si aplicarea sabloanelor de proiectare, acestea bazandu-se foarte mult pe compunerea obiectelor.
8.1.3 DELEGAREA
Reprezinta o cale de aplicare a principiului compunerii obiectelor. Intr-o relatie de delegare 2 obiecte sunt implicate in rezolvarea unei cereri, si anume: obiectul care recepteaza mesajul (delegatorul) deleaga executia operatiei corespunzatoare unui alt obiect - delegat. Acest lucru este oarecum similar cu situatia in care subclasele "paseaza" sarcina executiei unor operatii claselor parinte (este vorba despre operatiile mostenite si
101
neredefinite). Dar, in timp ce clasa parinte a unei subclase ramane aceeasi pe toata durata executiei, in cazul delegarii obiectele delegat pot fi schimbate, cu conditia sa aiba aceeasi interfata. Delegarea este considerata ca un sablon de proiectare fundamental, pe ea bazanduse foarte multe din celelalte sabloane (ex: State, Visitor, Strategy, Mediator, Chain of Responsibility, Bridge). Capitolul urmator, trateaza problema delegarii mai pe larg.
102
9 DELEGARE (DELEGATION)
Aceasta lucrare prezinta principiul delegarii, necesitatea acestuia si solutiile de implementare.
9.1 MOTIVATIE:
In dezvoltarea aplicatiilor software se intalnesc adesea utilizari inadecvate ale mostenirii, care sunt considerate "anti-sabloane". Printre acestea cele mai frecvente sunt: a) derivarea din clase de uz general (gen Vector, HashTable etc). Declararea unei clase care modeleaza o entitate dependenta de aplicatie ca subclasa a unei clase de uz general nu este recomandata, din urmatoarele motive:
o o
modificarea clasei generale poate afecta subclasele; de obicei derivarea se face pentru utilizarea functiilor din clasa de uz general. Acest lucru nu este neaparat rau dar poate distruge incapsularea: clientii clasei specifice pot miza pe faptul ca acea clasa extinde clasa de uz general (rezulta dependenta de clasa generala).
Un exemplu de folosire inadecvata a mostenirii il intilnim chiar in Java, unde clasa care implementeaza stiva (Stack) mosteneste clasa Vector. public class Stack extends Vector Datorita mostenirii un programator poate sa apeleze pentru o variabila de tip Stack metode care apartin clasei de baza Vector, ca in exemplul prezentat mai jos:
Stack stiva = new Stack(); stiva.push("X"); stiva.push("Y"); stiva.push("Z");
103
Object x = stack.get(1);
Dupa cum se observa aceasta stiva expune metode de acces ale vectorului ceea ce nu este tocmai dorit. (Conform contractului trebuie sa aiba doar push si pop). b) folosirea mostenirii pentru modelarea rolurilor. Ca sa ne dam seama cand suntem pe cale sa aplicam mostenirea in mod nepotrivit, iata doua dintre criteriile pe care le putem aplica:
Se utilizeaza mostenirea doar cand aceasta este cu predilectie statica. Daca se constata ca un anumit obiect, la momente de timp diferite, trebuie sa apartina unor subclase diferite ale aceleiasi clase parinte B, o asemenea situatie nu se va modela folosind mostenirea de clasa. Cu alte cuvinte, mostenirea nu este potrivita atunci cand identificam o situatie in care se schimba clasa de baza in timpul executiei. Daca se constata ca o anumita subclasa trebuie sa ascunda fata de clientii sai o parte din membrii mosteniti de la parintele ei avem un semn ca aplicarea mostenirii nu a fost cea mai inteligenta solutie.
O alta solutie de a afla daca trebuie sa folosim mostenire sau delegare este de a raspunde la urmatoarea intrebare A fi sau a avea?. In diagrama de mai jos este prezentat un exemplu in care clasele Programator si Director mostenesc clasa Angajat. Totul pare sa fie corect, insa exista situatii in care un angajat este programator iar mai apoi director. In aceste situatii modelul prezentat nu mai poate sa fie folosit.
104
Pentru a rezolva problema aparuta putem sa folosim in locul mostenirii delegare, ca in diagrama urmatoare, unde clasa Angajat poate sa contina membrii de tip
Programator, Director.
105
Figura 41 - Un angajat poate avea rolul de Programator, Director sau chiar ambele
9.2 APLICABILITATE:
in cazurile in care metode sau membrii mosteniti nu trebuie sa fie accesati in clasa derivata - in cazul in care comportamentul unei clase este determinat in timpul executiei (O fereastra poate fi in timpul executiei dreptunghiulara sau rodunda, deci nu ar fi bine ca clasa fereastra sa mosteneasca clasa dreptunghi). Delegarea este utilizata in alte pattern-uri cum ar fi State, Strategy si Visitor, pattern-uri ce vor fi analizate in lucrarile urmatoare.
9.3 STRUCTURA:
106
9.4 PARTICIPANTI:
Delegator clasa delegator care are membru de tipul Delegat; Delegat clasa (de obicei de uz general) a carei functionalitate va fi folosita de catre delegator si nu va fii neaparat expusa de Delegator clientilor; Clasa Delegator va folosi unele dintre metodele si membrii clasei delegate pentru a realize propriile obiective.
9.5 COLABORARE:
9.6 CONSECINTE:
Principalul dezavantaj al delegarii este gradul mai scazut de structurare fata de mostenire. Relatiile dintre clase modelate cu ajutorul delegarii sunt mai putin clare. Pentru rezolvarea acestor neajunsuri se pot utiliza reguli privind alegerea numelor pentru a referi obiectele aflate in diverse roluri si utilizarea comentariilor pentru clarificarea scopului delegarii. Delegarea este mai generala decat mostenirea. Orice extensie adusa unei clase prin mostenire se poate realiza si prin delegare. Delegarea este o buna alegere in proiectare, dar trebuie avut grija ca ea sa nu fie folosita inadecvat.
9.7 IMPLEMENTARE:
public class Delegator { Delegat d; . . . public type metodaDelegator() { . . . d.metodaDelegat(); . . . } };
107
return type; } };
108
10 FACTORY METHOD
10.1 SCOPUL
Definete o interfa pentru crearea unui obiect, dar las subclasele s decid care clas va fi instaniat.
10.3 MOTIVAREA
Un Framework utilizeaza clase abstracte pentru a defini i menine relaiile dintre obiecte si este adesea responsabil i de crearea acestor obiecte. Considerm un Framework pentru aplicaii care utilizeaza documente (*.doc, *.mp3, etc). Dou abstracii de baz sunt clasele Aplication i Document. Ambele clase sunt abstracte i clienii trebuie s le creeze subclase pentru a realiza implementri specifice aplicaiilor. Pentru a crea o aplicaie de desenare de exemplu, definim clasele DrawingAplication i DrawingDocument. Clasa aplicaie va fi responsabil de administrarea documentelor i le creaza la cerere cnd utilizatorul selecteaz Open sau New din meniu.
109
Datorit faptului c subclasa concreta (aici MyDocument dar pentru aplicatii reale are nume diferite) care trebuie instaniat este specific aplicaiei (aplicatie pe care inca nu am proiectat-o !!), clasa Application nu poate prediciona instanierea clasei documentului; aceasta tiind numai cand trebuie creat un nou document fr a cunoate de ce tip este acesta. Aceasta creaz o dilem: cadrul trebuie s instanieze clase dar el nu cunoate dect clase abstracte care nu pot fi instaniate. Pattern-ul Factory Method ofer o soluie. El ncapsuleaz cunotinele despre ce subclas a clase Document trebuie creat i mut aceste cunotine n afara cadrului.
10.4 APLICABILITATEA
Factory Method se folosete cnd: O clas nu poate anticipa clasa obiectelor care trebuie create; O clas dorete ca tipul exact al obiectelor care se instantiaza sa fie specificat de clasele descendente;
10.5 STRUCTURA
produs
10.5.1 PARTICIPANI
Product (Document) definete interfaa obiectelor pe care le creeaz metoda factory
110
Creator (Application) Declaraiile metodei factory, care returneaz un obiect de tip Product Poate apela metoda factory pentru a crea un obiect Product
10.6 CONSECINELE
Metodele Factory elimin hard-coding-ul. In cod se lucreaz numai cu interfaa clasei Product, ca urmare va lucra cu orice clas concret definit de utilizator care extinde Product. Un dezavantaj potenial: clienii trebuie s deriveze clasa Creator numai pentru a crea un obiect particular Concrte Product. Derivarea nu creaz probleme cnd clientul trebuies deriveze oricum clasa Creator, dar altfel sunt obligat sa-l am tata pe Creator si poate nu vreau. n continuare sunt prezentate nc dou consecine ale pattern-ului Factory Method: Furnizeaz hooks pentru subclase. Crearea obiectelor unei clase cu metoda factory este mai flexibil dect crearea obiectului direct. Daca Factory Metod nu este abstracta, ofera o implementare implicita pentru obecte de tip ConcreteProduct. Daca doresc sa modific, in clasele descendente rescriu FactoryMethod. Conecteaz n ierarhiile paralel de clase. n exemplul precedent metoda factory este apelat numai de constructor. Dar acesta nu este un exemplu tipic; clienii vor gsi acest metod folositoare n special n cazul ierarhiilor de clase paralele.
111
Ierarhiile de clase paralele se obin atunci cnd o clas transmite o parte din responsabiliti unei clase separate. De exemplu figurile grafice care pot fi manipulate interactiv, adic poate fi mutat sau rotit folosind mouse-ul. Implementarea unor astfel de interaciuni nu este ntotdeauna uoar. Adesea este necesar memorarea i modificarea informaiei care nregistreaz starea manipulrii la un moment dat. Aceast stare este necesar numai n timpul manipulrii, mai mult ea nu este necesar n obiectul figur. Diferite figuri se comport diferit cnd utilizatorul le manipuleaz. De exemplu, ntinderea unei linii ar putea avea ca efect mutare unui capt al acesteia n timp ce ntinderea unui text poate schimba distana dintre linii. Solutia : o ierarhie separata de manipulatoare. CreateManipulator() este FactoryMethod care creaza manipulatorul corespunzator figurii care are nevoie de el.
112
11 ABSTRACT FACTORY
11.1 SCOP
Furnizeaz o interfa pentru crearea unor familii de obiecte nrudite sau dependente, fr a specifica clasele lor concrete.
11.2 MOTIVAREA
S considerm o aplicaie care suport mai multe moduri de pr ezentare, cum ar fi Motif i Presentation Manager. Diferitele moduri de prezentare implic diferite imagini si comportamente pentru componentele interfeei utilizator, cum ar fi scrollbar-uri, ferestre i butoane.
113
Sistemul trebuie s fie configurat una sau mai multe familii de produse. Se doreste proiectarea unei familii de produse inrudite care s lucreze mpreun i aceast constrngere ar trebui pstrat.
Se dorete furnizarea unei librri de produse si dorim sa facem publica interfata acestora dar nu si implementarea.
114
11.5 STRUCTURA
11.5.1 PARTICIPANTS
AbstractFactory - declara o interfaa pentru operaii de creare a produselor abstracte; ConcreteFactory - implementeaza operatiile de creare a produselor concrete; AbstractProduct - declara o interfata pentru o categorie de produse; ConcreteProduct - definete un produs care va fi creat de un obiect ConcreteFactory corespunztor; - implementeaza interfaa AbstractProduct; Client - utilizeaz doar interfeele AbstractFactory si AbstractProduct.
11.6 CONSECINELE
Izoleaz clasele. Pattern-ul Abstract Factory ofer posibilitatea controlului claselor cnd o aplicaie este creat. Din cauz c o clas de tip factory ncapsuleaz responsabilitile i procesele de creare a obiectelor, se izoleaz clienii de implementarea claselor. Clienii manipuleaz instanele prin interfeele lor abstracte. Numele clasei este izolat n implementare, el nu apare n codul clientului.
115
Face mai uor schimbul ntre familiile de produse. Clasa concret factory apare o singur dat, numai la instaniere. Aceasta o face mai uor de schimbat clasa pe care o utilizeaz aplicaia. Se pot folosi diferite configuraii ale produsului simplu prin schimbarea clasei Concrete Factory. Datorit faptului c o clas factori abstract creaz o familie de produse, ntreaga familie se schimb simultan. n exemplul nostru de interfa putem schimba ntre Motif i Presentation Manager prin schimbarea obiectului factory corespunztor i recrearea interfeei. Asigura consistena dintre produse. Cnd se creeaz obiecte ntr-o familie acestea sunt proiectate s lucreze mpreun, este important ca acea aplicaie s foloseasc obiectele provenite de la o singur familie la un moment dat. Pattern-ul abstract factory face aceasta uor de aplicat. Suportarea noilor tipuri de produse este dificil. Extinznd pattern-ul i la producerea de noi tipuri de produse nu este uor. Aceasta din cauz c interfaa pattern-ului fixeaz un set de produse care pot fi create. Suportarea noilor tipuri de produse implic extinderea interfeei, care complic schimbarea clasei AbstractFactory i toate subclasele sale.
116
12 SINGLETON
12.1 SCOPUL
Asigur c o clas are o singur instan i furnizeaz un punct global de acces la ea.
12.2 MOTIVAREA
Exista situatii in care unele clase trebuie s aib exact o singur instan. De exemplu la pattern-ul AbstractFactory este necesar sa existe o singura fabrica concreta (altfel incurc produsele).
12.3 APLICABILITATEA
Patternul Singleton se folosete cnd: trebuie s existe o singur instan a unei clase i trebuie s fie accesibil clienilor din diferite puncte de acces; cnd o singur instan ar trebui extins n subclase i clienii ar trebui s foloseasc instana extins fr a se modifica codul.
12.4 STRUCTURA
117
Ambele solutii au o posibila problema: daca vreau sa extind clasa metoda getInstance() nu mai poate fi suprascrisa polimorfic (deoarece este statica = metoda de clasa) O alta varianta : cream o clasa SingletonFactory care mentine o tabla de dispersie, unde perechile <key, value> sunt <nume, singleton> (detalii in Design Patterns)
118
instanta respectiva este marcata ca libera si poate sa fie reutilizata (la cererea urmatoare). Cu alte cuvinte un client va face:
printer = PrintPooler.obtainPrinter(); printer.print();
2. Imbunatatiti clasa de mai sus astfel: a. Primul apel obtainPrinter creaza si un fir (Thread) care din 10 in 10 secunde verifica daca sunt printere obtinute si neutilizate. In acest caz le pune la dispozitie altor client. b. Realizati o interfata de monitorizare a PrintPooler-ului. In aceasta interfata se va vedea sta
119
13 BUILDER
13.1 INTENTIE (CU CE SCOP)
Acest sablon are ca scop separarea constructiei unui obiect de reprezentarea acestuia. Prin aceasta separare devine posibila folosirea aceluiasi process de constructie pentru mai multe reprezentari. Cu alte cuvinte, algoritmul ce construieste obiectul complex in cauza, nu tine cont de reprezentare (reprezentarea fiind pugin-abila).
120
ASCII
TeX
Intr-o abordare monolitica (nemodulara), ar trebui facute mai multe readere separate chiar daca procesul de creare a documentului sau produsului rezultat este acelasi. Cele trei readere vor avea presarate prin codul lor sursa elemente de formare/reprezentare ale produsului de iesire. Aceasta abordare are o multime de deficiente printre care: modificarea algoritmului de creare este foarte dificila. Pe cazul dat trebuiesc facute modificari in cel putin trei locuri. modificarea reprezentarii este deasemenea greoaie intrucat readerul este cuplat cu modulul de creare a partilor. O implementare bazate pe sablonul de proiectare Builder, rezolva ambele probleme mai sus mentionate. Fiecare conversie + generarea componentei complexe se realizeaza cu cate o implementare a interfetei AbstractBuilder. Acest pattern se bazeaza pe faptul ca intregul este construit intotdeauna din parti iar construirea partilor este un detaliu de implementare incapsulat in Builderul concret specific produsului. Crearea
121
intregului se face pas cu pas, fiecare metoda specificata in intefata abstracta Builder contrubuind cu ceva (cu o parte) la produsul finit. Pe exemplul dat in aceste randuri, RTFReaderul (cu rol de Director), este responsabil cu citirea/intelegerea documentului RTF si pe masura ce acesta gaseste instructiuni de formatare sau texte, le directioneaza ca apeluri builderului (setat de catre client sau aflat prin intermediul unui factory). Toate aceste apeluri sunt facute prin intermediul interfetei Builder, Directorul fiind astfel izolat de implementarile ASCIIBuilder, TeXBuilder, s.a.m.d.
13.4 STRUCTURA
122
13.5 PARTICIPANTI
13.5.1 BUILDER (TEXTCONVERTOR)
Specifica o interfata pentru crearea partilor componente ale unui produs.
SI COLABORATORI)
Directorul notifica builderul de cate ori o parte a produsului trbuie create. Builderul primeste cereri de la director si adauga partile specificate la produsul complex. Clientul are acum acces la prdusul finit. Toate aceste interactiuni sunt pezentate in diagrama de secventa de mai jos.
123
13.7 CONSECINTE
Reprezentarea interna a produsului poate sa varieze. Builderul este cunoscut de catre director doar prin intermediul interfetei Builderului abstract (TextBuilder sau HamburgerBuilder din sectiunea Implementare de mai jos). Daca reprezentarea interna a produsului se schimba se poate defini un alt builder extinzand builderul abstract. Se izoleaza codul de constructie de cel de reprezentare. Se imbunatateste modularitatea incapsulandu-se modul in care obiectul este construit si reprezentat. Builderul concret este singurul care stie cum se creaza / reprezinta un produs particular iar codul odata scris poate fi folosit de catre mai multi Directori. Pe exemplul cu RTFReaderul, s-ar putea scrie un alt director care sa cunoasca sintaxa HTML, rezultatul fiind un convertor HTML -> TeX sau chiar o componenta editor de HTML.
124
Da un grad mai mare de control procesului de constructie. Spre deosebire de paternurile creationale care construiesc produsele dintr-un singur foc, Builder construieste produsul pas cu pas sub privirea directorului (sub controlul acestuia).
13.8 IMPLEMENTARE
Clasa Builder trebuie sa defineasca cate o operatie pentru fiecare componenta pe care un director ar putea sa o creeze. Builderul abstract nu este de fapt o interfata ci o clasa oarba cu toate metodele implementate intr-un mod implicit (nu fac nimic) astfel fiind posibil ca un Builder concret sa suprascrie doar metodele ce-l intereseaza. Nu se poate gasi o clasa abstracta de produse intrucat spre deosebire de AbstractFactory produsele variaza foarte mult (de fapt pot chiar san nu aiba nimic in comun). Spre exemplificare, ce poate avea in comun un fisier ASCII text cu o componente grafica de editare de documente HTML? Dealtfel nici nu este necesar, in acest caz clientul stiind intotdeauna ce builder are directorul setat. Stie acest lucru pentru ca chiar el (cleintul) a creat Bulderul concret si i l-a furnizat Directorului inspre folosinta.
125
126
public class HamburgerBuilder extends CreatorMeniu { Hamburger h = new MeniuHamburger(); public void contribuieCuMeniuPrincipal() {
127
h.setCarne(porc); }
Un anumit tip specific de CreatorMeniu ar putea avea anumite metode lasate goale (contribuieCuJucarie) sau chiar inexistente (dar mostenite goale de la parinte), aceasta insemnand ca HamburgerBuilder nu ofera cele doua componente in oferta. Produsele create de implementari CreatorMeniu ar putea fi atat de diferite incat nu se pot pune sub aceeasi interfata. Un exemplu ar fi un restaurant in care clientul poate vedea o holograma a produsului ce doreste sa-l consume. Generatorul acestei holograme nu este decat o extensie a interfetei MeniuCreator de mai sus. Cod sursa RTFReader
128
public RTF_Reader( TextConverter aBuilder, String RTFtoConvert ) { builder = aBuilder; RTF_Text = RTFtoConvert; }
switch ( next.type() ) { case CHAR: break; case FONT: case PARA: etc. } builder.font( next.font() ); break; builder.newParagraph( ); break; builder.character( next.char() );
129
} } }
abstract class TextConverter { public void character( char nextChar ) { public void font( Font newFont ) public void newParagraph() {} } { } }
130
{ public void character( char nextChar ) public void font( Font newFont ) public void newParagraph() public TeX getText() } { blah }
{ blah } { blah }
myReader.parseRTF();
131
construireFundatie -> ridicareZiduri -> executieAcoperis -> instalatieElectrica -> instalatieSanitara -> executareTencuiala -> finisajCuRigips -> modernizareCurte. Creati toate clasele aferente problemei fi faceti o clasa de test ce instantiaza societatea comerciala ce va moderniza un apartament.
132
a) Adaptarea de clasa cu mostenire multipla (Java nu permite) b) Adaptarea obiect nu necesita mostenire multipla: se rezolva prin delegare
14.2 BRIDGE
14.2.1 SCOPUL
Separ abstractizarea de implementare astfel nct acestea se pot modifica independent.
133
14.3 COMPOSITE
dorii s reprezentai o ntreag ierarhie de obiecte. dorii ca clienii s ignore diferena dintre obiectele compuse i obiectele individuale. Clienii vor trata toate obiectele din structura compus uniform.
14.3.1 STRUCTURA
134
14.3.2 PARTICIPANI
Component (Grafic) Declar interfaa pentru obiectele compuse Declar o interfa pentru accesarea i maparea componentelor copil Definete o interfa pentru accesarea componentelor printe ntr-o structur recursiv Leaf (Rectangle, Line, Text, etc.) reprezint obiectele frunz.
Composite (Picture) Definete comportamentul pentru componentele care au copii Memoreaz componentele copil
135
14.4 DECORATOR
14.4.1 SCOPUL
Ataeaz dinamic responsabiliti unui obiect. Decorator furnizeaz o alternativ flexibil subclaselor pentru extinderea funcionaliti.
14.4.2 PARTICIPANI
Component: definete interfaa pentru obiectele crora li se pot aduga funciuni in mod dinamic; ConcreteComponent: definete un obiect concret cruia i se pot aduga funciuni; Decorator: conine o referina la Component (compref) si definete o interfaa conforma cu cea a lui Component; ConcreteDecorator: aduga funciuni noi unei componente.
136
15 BIBLIOGRAFIE
Alistair Cockburn - Writing Effective Use Cases, Addison-Wesley, 2001 David Eriksson - Designing an object-oriented decompiler. Decompilation support for Interactive Disassembler Pro, Master Thesis in Software Engineering, Thesis no: MSE-2002:17, June 2002 (source Internet) Kent Beck, Martin Fowler - Planning Extreme Programming, Addison Wesley, 2000 Anneke Kleppe, Jos Warmer, Wim Bast - MDA Explained: The Model Driven Architecture: Practice and Promise, Addison Wesley, 2003 Sherif M. Yacoub, Hany H. Ammar - Pattern-Oriented Analysis and Design: Composing Patterns to Design Software Systems, Addison Wesley, 2003 Martin Fowler UML Distilled Second Edition: A Brief Guide to the Standard Object Modeling Language, Addison Wesley, 1999 Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides Elements of Reusable Object-Oriented Software, Addison Wesley, 1995 Martin Robert Object-Oriented Design Principle - www.objectmentor.com, 2002 Dorin Sima Elemente de inginerie software, Ed. ULBS, 2003 http://hillside.net/patterns/ http://www.extremeprogramming.com/ http://martinfowler.com/ http://www.gentleware.com, Poseidon Site, 2007 http://www.eclipse.org, Eclipse - an open development platform, 2007