Curs 11 Curs 12

Puntea (Bridge)






Definitie
Este un sablon care realizeaza decuplarea unei abstractiuni de implementarea ei, astfel incat cele 2 pot varia independent.

Context
Cand o anumita abstractiune poate avea mai multe variante de implementare, de obicei se utilizeaza mostenirea: o clasa abstracta defineste interfata abstractiunii, iar subclasele concrete o implementeaza in diverse moduri.
Aceasta abordare nu este intotdeauna suficient de flexibila. Prin mostenire o implementare este legata permanent de abstractiune si acest lucru face foarte dificila modificarea independenta a abstractiunii si a implementarii.
Sa consideram ca abstractiunea in cauza se numeste Window si reprezinta o fereastra in cadrul unui sistem de interfete grafice. Aceasta abstractiune ar trebui sa permita scrierea de aplicatii care sa poata lucra cu diverse platforme grafice, X Window System si Presentation Manager de exemplu.
Utilizand mostenirea, s-ar putea defini 2 subclase, XWindow si PMWindow care sa implementeze interfata Window pentru cele 2 platforme. Apar aici 2 dezavantaje:

Sablonul Bridge isi propune sa solutioneze aceste probleme plasand abstractiunea Window si implementarea ei in ierarhii separate. Astfel, vom avea o ierarhie formata din clasa Window, cu subclasele IconWindow si TransientWindow, respectiv o ierarhie separata, formata din clase care depind de platforma: WindowImpl cu subclasele XWindowImpl si PMWindowImpl:
Operatiile care apar in clasele din prima ierarhie sunt operatii specifice diverselor tipuri de ferestre, in timp ce operatiile care apar in a 2-a ierarhie depind de platforma grafica. De Exemplu: Pe de alta parte, operatiile din clasele de tip Window se bazeaza pe operatiile interfaetei WindowImpl: Relatia care apare intre Window si WindowImpl se numeste punte (bridge). Se observa ca o asemenea organizare a claselor permite evolutia independenta a celor 2 ierarhii.

Motivatii

Sablonul Bridge se aplica in general in situatiile in care:

se doreste evitarea legarii unei abstractiuni de implementarea sa. Acest lucru se intampla de exemplu cand implementarea trebuie aleasa sau modificata pe durata executiei;
atat abstractiunea cat si implementarea trebuie sa poata fi extinse prin adaugarea de subclase. In aces caz sablonul bridge permite combinarea dupa dorinta a diferitelor abstractiuni si implementari.
schimbarile aduse implementarii nu trebuie sa afecteze clientii, adica nu trebuie sa necesite recompilarea codului acestora;
se doreste ascunderea completa a implementarii fata de clienti;
se constata ca extensiile care trebuie aduse unei abstractiuni duc la o crestere prea mare a numarului de subclase (asa cum s-a vazut in exemplul de mai sus). In acest caz se indica "spargerea" obiectelor in 2 parti, creindu-se 2 ierarhii separate. Asemenea ierarhii se mai numesc "generalizari incuibate";
se doreste partajarea unei implementari intre mai multe obiecte si acest fapt trebuie ascuns fata de client.


Solutie

In figura de mai jos este data structura de clase care constituie sablonul Bridge:

Obiectele de tip Abstraction redirecteaza cererile clientului spre obiectele de tip Implementor de care sunt asociate.
 

Consecinte

Sablonul Bridge prezinta urmatoarele implicatii:

Implementare

Implementor unic. In cazul in care avem o singura ramura descendenta din Implementor, practic nu mai este necesara definirea clasei abstracte Implementor. Acesta este un caz degenerat al sablonului Bridge. Totusi, separarea intre Abstraction si Implementor este recomandata, deoarece permite modificarea claselor de implementare fara a afecta clientii.

Crearea obiectului Implementor adecvat. Cum, cand si unde se decide ce obiect concret de tip Implementor sa se instantieze, atunci cand avem mai multe variante?
Daca Abstraction stie care sunt toate clasele ConcreteImplementor, ea poate instantia una dintre ele in constructor, bazandu-se pe parametrii de apel ai constructorului. Spre exemplu, daca o clasa colectie are mai multe implementari, decizia se poate baza pe dimensiunea colectiei: o lista inlantuita este potrivita pentru colectii mici, in timp ce o tabela hashing este buna pentru colectii mari.
O alta posibilitate este aceea de a alege initial o varianta de implementare implicita, urmand ca ea sa fie schimbata pe parcurs, dupa necesitati. De exemplu, daca se constata ca o colectie creste peste o anumita limita, se poate comuta la o implementare mai adecvata.
O alta varianta ar fi de a delega decizia spre un alt obiect. Referindu-ne la exemplul cu Window, s-ar putea introduce un obiect producator abstract (v. Abstract Factory), a carui unica sarcina ar fi aceea de a incapsula informatii specifice platformei. In felul acesta, obiectul producator stie ce obiect WindowImpl trebuie creat pentru platforma curenta. Obiectul Window ar cere doar producatorului sa i se furnizeze un obiect WindowImpl. Un avantaj al acestei variante este acela ca Abstraction nu este legat direct de Implementor.


Mediatorul






Definitie
Acest sablon supervizeaza interactiunea intre mai multe obiecte, permitand ca obiectele respective sa nu se refere unul pe altul in mod explicit. In felul acesta, la nevoie se poate modifica mai usor modul in care interactioneaza obiectele.

Context
Proiectarea orientata pe obiecte incurajeaza distribuirea responsabilitatilor intre obiecte. Aceasta poate sa duca uneori la colaborari complexe intre obiecte. In cel mai rau caz se ajunge ca fiecare obiect sa intre in relatie cu toate celelalte.
Desi partitionarea unui sistem in mai multe obiecte este menita sa duca la cresterea reutilizabilitatii, proliferarea conexiunilor intre obiecte tinde sa reduca aceasta caracteristica. Numarul mare de interconexiuni face dificila modificarea comportamentului sistemului, deoarece acest comportament este distribuit pe multe obiecte.
Ca exemplu, sa consideram implementarea unei ferestre de dialog dintr-o interfata grafica. Intr-o asemenea fereastra ar putea sa apara o multitudine de elemnete grafice de control (widgets): butoane, comutatoare, linii de editare, liste derulante etc. intre care pot exista dependente. Astfel, introducerea unei anumite secvente intr-un camp de editare poate determina selectarea/deselectarea unor comutatoare sau selectia unui element dintr-o lista derulanta poate determina afisarea unor anumite valori in diverse linii de editare.
In plus, pentru ferestre de dialog diferite dependentele intre elementele de control vor fi diferite. Ca urmare, chiar daca diversele ferestre de dialog lucreaza cu aceleasi tipuri de componente, ele trebuie particularizate pentru a reflecta dependentele de comunicare intre componente.
Rezolvarea acestei probleme consta in definirea unei clase separate care sa incapsuleze comportamentul colectiv al elementelor de control, jucand rolul unui mediator al acestora. Mediatorul este responsabil pentru coordonarea intarctiunilor unui grup de obiecte. El este un intermediar care permite ca obiectele din grup sa nu se refere unele la altele in mod explicit. Aceste obiecte trebuie doar sa stie cine este mediatorul lor, in felul acesta reducandu-se numarul asocierilor.
Sa presupunem ca in fereastra de dialog se afla, printre altele, o lista derulanta, o linie de editare si o caseta de maracare a optiunilor formata din 3 comutatoare care interactioneaza in felul urmator: initial, pana nu a fost selectata nici o intrare din lista, comutatoarele sunt dezactivate (nu pot fi pozitionate pe on/off); cand utilizatorul selecteaza o intrare din lista, secventa de caractere respectiva apare afisata in campul de editare, iar cele 3 comutatoare sunt activate, astfel incat utilizatorul sa poata sa marcheze o anumita combinatie de optiuni. Admitand ca lista este reprezentata printr-un obiect aListBox, iar campul de editare printr-un obiect anEntryField, aplicarea sablonului mediator in acest caz ar determina urmatoarea inlantuire de operatii dupa selectia unei intrari din lista:

  1. aListBox anunta mediatorul ca a avut loc evenimentul selectie intrare,
  2. mediatorul preia secventa selectata,
  3. mediatorul paseaza secventa spre obiectul anEntryField, determinand afisarea ei,
  4. mediatorul activeaza comutatoarele.
Tema: se propune studentilor sa reprezinte in notatie UML diagrama de secventa care ilustreaza interactiunea de mai sus.
In concluzie, se poate spune ca elementele ferestrei comunica intre ele indirect, prin intermediul mediatorului. Practic, elementele respective nici nu se "cunosc" intre ele. Deoarece comportamentul colectiv este localizat intr-o singura clasa, ea poate fi modificata sau inlocuita cu o subclasa a ei.
In figura de mai jos se poate vedea diagrama care reda structura de clase corespunzatoare exemplului discutat mai sus:

DialogDirector este o clasa abstracta care defineste comportarea de ansamblu a grupului de obiecte ce participa la dialog. FontDialogDirector este o clasa concreta care modeleaza o fereastra prin care utilizatorul stabileste caracteristicile de font ale unui text. Clientii apeleaza operatia ShowDialog pentru a afisa fereastra de dialog pe ecran. Cu ajutorul operatiei CreateWidgets fereastra este populata cu elementele componente, obiecte ale subclaselor de tip Widget.
Fiecare obiect de tip Widget anunta mediatorul in momentul in care a suferit o anumita modificare de stare prin operatia Changed(): Subclasele lui DialogDirector trebuie sa redefineasca operatiile CreateWidgets pentru a-si crea componente specifice unei anumite ferestre de dialog, precum si WidgetChanged pentru a coordona modificarile de stare ale componentelor.

Motivatii

Sablonul Mediator se aplica in situatiile in care:

o multime de obiecte comunica intr-un mod bine definit, dar complex, rezultand o interdependenta nestructurata si dificil de inteles;
reutilizarea unui obiect este dificila din cauza ca el refera si comunica cu multe alte obiecte;
un anumit comportament distribuit pe mai multe clase trebuie sa poata fi adaptat fara a implica o multime de subclase.


Solutie

In figura de mai jos este data structura de clase care constituie sablonul Mediator:

Obiectele Colleague trimit si primesc cereri spre/de la un obiect Mediator. Mediatorul implementeaza comportamentul interactiv prin dirijarea cererilor intre obiectele Colleague implicate.
 

Consecinte

Sablonul Mediator prezinta urmatoarele avantaje si dezavantaje:

Limitarea derivarii. Un mediator localizeaza actiuni care altfel ar trebui distribuite la mai multe obiecte. Schimbarea actiunilor necesita doar schimbarea mediatorului, nu si a obiectelor Colleague.
Decuplarea obiectelor. Un mediator promoveaza cuplajul slab intre obiectele Colleague. Clasele de tip Mediator si Colleague pot fi schimbate si reutilizate independent unele de altele.
Simplificarea protocolului obiectelor. Un mediator este menit sa inlocuiasca interactiunile de tip many-to-many cu interactiuni de tip one-to-many care sunt mai usor de inteles, implementat si adaptat.
Abstractizarea cooperarii obiectelor. Definind interactiunea intre obiecte ca un concept separat, incapsulat intr-un obiect, se permite proiectantului sa se concentreze (sau sa se focuseze, daca ar fi sa adoptam o expresie la moda) asupra modului in care obiectele coopereaza intre ele, independent de comportamentul lor individual. Cu alte cuvinte, se face o separare intre comportamentul specific unui obiect (care individualizeaza obiectul) si comunicarea obiectului cu alte obiecte.
Centralizarea controlului. Mediatorul introduce un compromis intre complexitatea interactiunilor si complexitatea obiectului mediator, in sensul ca acest obiect, prin faptul ca incapsuleaza protocoale, poate sa devina mai complex decat oricare dintre obiectele Colleague, ajungand un monolit dificil de intretinut.
Implementare In cele ce urmeaza vom prezenta o posibila codificare a exemplului discutat in paragraful Context: Obs: daca se proiecteaza o aplicatie cu interfata grafica in limbajul Java, folosind de exemplu pachetul java.awt, sablonul mediator se poate aplica definind ca obiect mediator chiar receptorul de evenimente (listener). El va fi responsabil cu crearea componentelor grafice si amplasarea lor in fereastra, iar metodele de tratare a evenimentelor (care au nume predefinite) vor fi omoloagele operatiei WidgetChanged, cu observatia ca in loc sa avem o singura asemenea operatie, vom avea atatea cate tipuri de surse de evenimente sunt in fereastra.

Curs 11 Curs 12