Platfrmă de e learning și curriculă e cntent pentru învățământul superir tehnic Instrumente pentru Dezvltarea Prgramelr 17. Practici si instrumente pentru dezvltarea prgramelr. Design patterns si cncepte avansate Swing.
Obiective Scpul acestui labratr este experimentarea unr tehnici utile in priectarea interfetelr grafice. Vm flsi Swing drept suprt. Subiecte atinse: Mediatr Pattern State Pattern SwingWrker Mediatr Pattern De bicei, lgica unui prgram este distribuita in diferite clase. Pe masura ce numarul acestra creste, cmunicarea devine tt mai cmplexa. Se pate ajunge, astfel, la structura incalcita de clase, avand drept cnsecinta un cd greu de citit si de intretinut. Mai mult, schimbarile survenite la mmente ulteriare pt aduce cu sine mdificari ale mai multr clase. De asemenea, capacitatea de reutilizare se diminueaza, intrucat biectele devin, din punct de vedere cmprtamental, puternic legate de celelalte. Design pattern-ul Mediatr se adreseaza prblemei de mai sus, prmvand un cuplaj redus intre cmpnente. Acest deziderat se btine prin intrducerea unei ni entitati, mediatrul, singura care pseda cunstinte despre tate celelalte cmpnente (numite clegi), in timp ce acestea nu cunsc decat mediatrul. Cum cmpnentele nu se mai refera explicit, interactiunile dintre cmpnente pt fi mdelate independent. Din punctul de vedere al interactiunilr intre entitati, intrebuintarea mediatrului pate fi privita ca trecere de la un graf arecare: la unul cu tplgie de stea:
In figura de mai sus muchiile grafului reprezinta interactiuni explicite intre cmpnente (de exemplu, apeluri de metde). Mediatrul este, de fapt, un intermediar al cmunicarii si, pentru claritatea arhitecturii, ar trebui sa se limiteze la acest rl. Diagrama de clase: Entitati: Mediatr defineste interfata de cmunicare intre cmpnente CncreteMediatr implementeaza cmprtamentul prpriu-zis pastreaza referinte la elementele pe care le crdneaza Clleague defineste cmpnenta cntrlata de Mediatr In urmatrul exemplu este vrba despre un event handler pentru apasarea unui butn: buttn.addactinlistener(new ActinListener() { public vid actinperfrmed(actinevent e) {
}); } mediatr.buttnpressed(); Se bserva ca handler-ul cntine pur si simplu apelul unei metde a mediatrului. Presupunem, bineinteles, ca mediatrul a aflat in prealabil de tate celelalte cmpnente si pate asigura interactiunea. Metda apelata mai sus pate avea frma: public vid buttnpressed() { } buttn1.setenabled(true); buttn2.setenabled(false);... Astfel, cmunicarea este gestinata direct de catre mediatr. In acelasi timp, se pate sesiza prezenta design pattern-ului Cmmand, care intrduce un nivel de separatie intre mecanismul de declansare a unei cmenzi (in cazul nstru, apasarea butnului) si entitatile afectate de cmanda (in cazul de fata, buttn1 si buttn2). Daca survin mdificari, acestea vr afecta dar cdul mediatrului, care implementeaza cmanda insasi. In acelasi timp, s-a btinut un cuplaj redus, buttn nestiind de buttn1 si buttn2, si viceversa. In cncluzie, design pattern-ul Mediatr are urmatarele avantaje: cuplajul redus intre cmpnente: se evita necesitatea ca fiecare cmpnenta sa le cunasca pe tate celelalte, btinandu-se simplificare a interactiunilr izlarea interactiunilr dintre biecte de cmprtamentele individuale ale acestra lcalizarea cmprtamentului, care altfel s-ar distribui intre mai multe entitati. De asemenea, imbgatirea acestuia se pate realiza strict prin extinderea mediatrului, nefiind necesara reprducerea acestei peratii pentru tate celelalte cmpnente, ce vr putea fi reutilizate ca atare usurinta adaugarii de ni cmpnente: dar mediatrul trebuie mdificat pentru actualizarea dependentelr State Pattern Acest pattern este util atunci cand un biect pune la dispzitie serie de actiuni, al carr efect depinde de starea interna a biectului. De exemplu, intr- aplicatie grafica, actiunile de apasare a butnului muse-ului si de deplasare a acestuia pt avea rezultate diferite in functie de tl-ul selectat in prealabil.
O prima mdalitate de a implementa acest cmprtament este prin memrarea starii curente si intergarea acesteia in mmentul executarii actiunii. Aceasta abrdare se cncretizeaza cel mai adesea in instructiuni if-else, respectiv switch. Daca numarul de stari este mare si/sau infrmatia de stare este cmplexa, cdul astfel rezultat devine greu de inteles, iar prbabilitatea incnsistentelr la tranzitia intre stari creste. Pattern-ul State prpune izlarea cmprtamentului specific unei stari in cate clasa separata. Astfel, in lcul unr metde unice pentru actiunile psibile, in care urmam pasi diferiti in functie de starea curenta, definim cate clasa pentru fiecare stare psibila, implementand in fiecare din acestea actiunile specifice. Adesea, se defineste clasa (eventual abstracta) cu tate actiunile psibile, urmand sa se deriveze din aceasta starile cncrete. Diagrama de clase: Entitati: Cntext defineste interfata accesibila clientilr pastreaza referinta la biectul stare curenta ascunde clientilr existenta biectelr stare State defineste peratiile psibile, al carr cmprtament este dependent de stare CncreteState implementare prpriu-zisa a unei stari Sa luam exemplul aplicatiei grafice. Definim clasa State: public class State { public vid musedwn(int x, int y) {} public vid musemve(int x, int y) {} }
Din aceasta am putea defini 2 stari cncrete, crespunzatare tl-urilr Circle si Rectangle: public class CircleState extends State { } public vid musedwn(int x, int y) {... } public vid musemve(int x, int y) {... } Dupa care putem memra starea curenta in frma unui biect: State currentstate = new CircleState(); In mmentul executarii unei actiuni, vm delega starii curente. Plimrfismul va asigura executia metdei crecte: addmuselistener(new MuseListener() { }); public vid museclicked(museevent e) { } currentstate.musedwn(e.getx(), e.gety()); Tranzitia de stare se pate cdifica prin reatribuirea biectului currentstate. Dupa cum veti vedea in exercitii, cele 2 pattern-uri, Mediatr si State, pt fi imbinate. Avantajele pattern-ului State: incapsularea starilr in clase: cmprtament si infrmatie de stare eliminarea secventelr cnditinale (if) explicitarea si siguranta tranzitiilr: in varianta initiala, in care infrmatia de stare era retinuta in exterir, riscul aparitiei errilr la efectuarea tranzitiilr si la determinarea starii curente era mult mai mare, dearece acestea nu se realizau atmic, prin atribuirea unei singure variabile. SwingWrker Asa cum am precizat in labratrul trecut, Swing se bazeaza pe un mecanism single-threaded. De fapt, experienta demnstreaza prbleme farte mari in realizarea unei interfete grafice esentialmente multi-threaded. Abrdarea single-threaded are ca principal avantaj absenta prblemelr de cncurenta, intrucat tate handler-ele se executa succesiv. Dezavantajul se manifesta in mmentul in care este necesara rularea unr rutine de lunga durata (de exemplu, descarcarea unui fisier mare din retea). Daca aceasta sarcina ar rula pe acel unic fir, interfata ar inceta sa mai raspunda actiunilr utilizatrului pana la finalizarea rutinei. Astfel transpare necesitatea intrebuintarii unr fire de executie separate. Prblemele se intetesc daca este necesara pastrarea legaturii cu interfata grafica, de exemplul in cazul in care se dreste prezentarea
evlutiei dwnlad-ului. Acest lucru ar slicita din partea prgramatrului un efrt de sincrnizare intre firele de executie. Firul de executie pe care ruleaza tate handler-ele de eveniment se numeste Event Dispatch Thread (EDT). Pentru evitarea prblemelr de cncurenta, se recmanda ca accesarea cmpnentelr vizuale sa se faca inttdeauna de pe EDT. Metda SwingUtilities.invkeLater(Runnable) permite rularea rutinei date ca parametru pe EDT. Swing pune la dispzitie clasa SwingWrker, care fera psibilitatea rularii de task-uri in fundal, pe fire de executie diferite, asigurand, intern, sincrnizarea cu EDT. Prgramatrul va trebui sa extinda clasa si sa supradefineasca cel putin prima metda de mai js: dinbackgrund dne prcess cntine secventa de lunga durata se executa pe un fir diferit trebuie supradefinita, fiind abstracta pate face apeluri publish pentru a publica rezultate intermediare din timpul prelucrarii, ce vr fi receptinate de prcess executata pe EDT la terminarea prelucrarii: rezulta ca se pt referi cmpnentele grafice din interirul ei capteaza biectele ferite de publish ruleaza pe EDT Dupa instantierea clasei, prnirea executiei task-ului se face prin apelul metdei execute. Clasa SwingWrker este generica si pate fi parametrizata astfel incat metda dinbackgrund sa intarca un rezultat. Acesta pate fi btinut, dupa terminarea prelucrarilr, cu metda get. Aveti la dispzitie un tutrial (http://java.sun.cm/dcs/bks/tutrial/uiswing/cncurrency/ wrker.html) despre SwingWrker. Exercitii Prezentare Urmariti prezentarea (http://elf.cs.pub.r/idp/_media/labratare/l5/lab5.pdf) pentru intelegerea cnceptelr utilizate in labratr. Enunturi Observatii: Utilizati scheletul de labratr, care reprezinta slutia labratrului trecut. Imprtati priectul in Eclipse.
punctele marcate cu (T) au asciat un cmentariu TODO in cd. Exemplu: pentru exercitiul 1 gasiti cmentariul // TODO 1 Rulati aplicatia pentru a bserva dispunerea cmpnentelr. Aruncati privire asupra claselr din priect. Atentie: Rulati Eclipse-ul de pe fep.grid.pub.r. Daca rulati Eclipse-ul aflat pe masinile din labratr, ultimul exercitiu nu va functina (OpenJDK are incmpatibilitate cu UISpec4j) 1. (1p) Mediatr. Se urmareste decuplarea cntralelr. 1. In clasa Main, bservati membrul med de tipul Mediatr si succesiunea de apeluri register* in metda init. Aceste metde fac cntralele cunscute mediatrului. 2. (T) Observati apelul metdei add a mediatrului in handler-ul butnului Add. Mutati cdul cmentat din handler in cadrul metdei addlist a mediatrului. Pentru a btine mdelul unei liste flsiti list.getmdel(). In general veti face cast-ul valrii intarse la DefaultListMdel. 3. (T) Repetati actiunea pentru handler-ul butnului Remve (metda remvelist a mediatrului). 2. (5p) State. Se urmareste implementarea unr efecte diferite ale apasarii butanelr Add si Remve, in functie de butnul radi selectat, List sau Message: daca List e selectat, cmprtamentul este cel din labratrul trecut: Add adauga in lista textul intrdus Remve inlatura din lista elementul selectat daca Message este selectat, cmprtamentul este urmatrul: Add afiseaza un mesaj arecare (message bx sau la cnsla) Remve nu face nimic 1. Observati clasa State, cu cele 2 metde ale sale. Clasa este extinsa de ListState si MessageState. 2. (1p) (T) Observati clasa ListState, care se refera la starea crespunzatare butnului radi List. Implementati cele 2 metde, flsindu-va de mediatr (vezi prblema 1). 3. (1p) (T) Efectuati acelasi lucru pentru clasa MessageState, crespunzatare butnului radi Message. 4. (1p) (T) Observati clasa StateManager, care gestineaza starea curenta a aplicatiei si tranzitiile intre stari. Manager-ul de stare fera actiunile pe care le drim executate (add/remve), bazandu-se pe starea curenta pentru btinerea efectului drit. Implementati cele 4 metde, urmarind cmentariile.
5. (1p) (T) In clasa Mediatr bservati membrul de tipul StateManager. Implementati metdele add si remve, care vr pasa apelul manager-ului de stare. 6. (0.5p) (T) In clasa Main, in handler-ele butanelr Add si Remve, inlcuiti apelurile metdelr addlist, respectiv remvelist cu apelurile add, respectiv remve, implementate la subpunctul anterir. Acest lucru este necesar pentru btinerea cmprtamentului dependent de stare. (Metdele addlist si remvelist implementau cmprtamentul starii List) 7. (0.5p) (T) In clasa Main, bservati handler-ele butanelr radi List si Message. Se cnstata ca ele apeleaza metdele list, respectiv message ale mediatrului. Implementatile astfel incat sa realizeze tranzitiile crespunzatare de stare (vezi subpunctul 2.4). 3. (3p) SwingWrker. Se urmareste flsirea clasei SwingWrker pentru rularea, in fundal, a unui task de lunga durata. 1. (0.5p) In clasa Mediatr, pentru a simula prelucrare de lunga durata, adaugati la inceputul metdei addlist aceasta secventa: int i = 3; while (i-- > 0) try { Thread.sleep(1000); } catch (InterruptedExceptin e) {} Rulati aplicatia, intrduceti un text si incercati sa actinati rapid, de mai multe ri, butnul Add. Ce bservati? Care este cauza acestui cmprtament? 2. (1p) (T) Cmpletati clasa ListWrker, definita in clasa Mediatr, pentru a rezlva prblema. Mutati prelucrarea de lunga durata in metda dinbackgrund, si cdul de actualizare a mdelului de lista in metda dne. Utilizati instanta ListWrker in metda addlist (apelati execute). 3. (0.5p) Adaugati, dupa apelul Thread.sleep, apelul publish(i). Ce bservati? (In metda prcess iterati prin lista de intregi primita ca argument) 4. (0.5p) (T) In metda prcess, inlcuiti bucla fr cu un apel println(list). Reduceti timpul de asteptare la 1 ms. Ce bservati? Din ce cauza prcess primeste un argument de tip lista? 5. (0.5p) Afisati, in fiecare metda din ListWrker, thread-ul curent (Thread.currentThread), si cercetati utput-ul. 4. (2p) UISpec4j. Se urmareste flsirea framewrk-ului UISpec4j pentru testarea interfetei. Framewrk-ul pate fi dwnladat de aici. 1. (0.5p) Adaugati jarul de UISpec4j la priect, si de asemenea biblitecta de JUnit. Eliminati prelucrarea de lunga durata din metda dinbackgrund
2. (0.5p) Creati clasa, GUITest, care extinde UISpecTestCase. Aceasta clasa extinde clasa TestCase din JUnit, asadar metdele setup() si teardwn() au aceeasi semnificatie Setati clasa GuiTest sa apeleze metda main(), din clasa Main, fara parameteri (flsiti metda setadapter()) Pentru prnirea aplicatiei, setadapter primeste urmatrii parametrii: setadapter(new MainClassAdapter(Main.class, new String[0])); 3. (0.5p) Creati metda de test, testadd, pentru a verifica functinalitatea butnului Add din interfata. Flsiti metda getmainwindw(), ferita de UISpecTestCase, pentru a btine fereastra aplicatiei si pentru a accesa cmpnentele individuale ale interfetei Verificati ca, la apasarea butnului Add, lista cntine elementul crespunzatr 4. (0.5p) Creati metda similara de test, testremve, pentru a verifica functinalitatea butnului Remve din interfata. Resurse utile Mediatr Pattern (http://en.wikipedia.rg/wiki/mediatr_pattern) State Pattern (http://en.wikipedia.rg/wiki/state_pattern) Tutrial SwingWrker (http://java.sun.cm/dcs/bks/tutrial/uiswing/cncurrency/ wrker.html)