Tehnici de proiectare software - Tipografia
Tehnici de proiectare software - Tipografia
Tehnici de proiectare software - Tipografia
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
TEHNICI DE PROIECTARE SOFTWARE<br />
Proiectare orientată pe şabloane<br />
Prof. univ. Dr. Tudor Bălănescu
Cuprins<br />
I. Introducere...........................................................................................1<br />
1.1. De ce şabloane?............................................................................................ 1<br />
1.2. Scurt istoric ................................................................................................. 1<br />
1.3. Concepte <strong>de</strong> bază în şabloane..................................................................... 2<br />
II. Şabloane creaŃionale ..........................................................................3<br />
II. 1. Abstract Factory........................................................................................3<br />
11.2. Buil<strong>de</strong>r............."...........................................................................................7<br />
11.3. Factory Mcthod ........................................................................................11<br />
11.4. Prototype...................................................................................................14<br />
11.5. Singleton................................................................................................... 17<br />
III. Şabloane comportamentale ............................................................ 20<br />
111.1. Chain Of Responsibility..........................................................................20<br />
111.2. Command ............................................................................................... 24<br />
111.3. Interpreter ...............................................................................................28<br />
111.4. Iterator....................................................................................................31<br />
111.5. Mediator ................................................................................................. 35<br />
111.6. Memento ................................................................................................. 38<br />
111.7. State ........................................................................................................ 41<br />
111.8. Strategy...................................................................................................45<br />
111.9. Visitor ..................................................................................................... 49<br />
111.10. Template Method ................................................................................. 54<br />
IV. Şabloane structurale ....................................................................... 58<br />
IV.l. Adapter ................................................................................................... 58<br />
IV.2. Bridge.......................................................................................................62<br />
IV.3. Composite ................................................................................................65<br />
IV.4. Decorator .................................................................................................69<br />
IV.5. Faca<strong>de</strong> ..................................................................................................... 73<br />
IV.6. Flyvveight ................................................................................................ 76<br />
IV.7. Half-Objcct Plus Protocol (HOPP)........................................................ 79<br />
IV.8. Proxy ........................................................................................................83<br />
V. Concluzii .........................................................................................87<br />
Bibliografie ............................................................................................88
I. Introducere<br />
1.1. De ce şabloane?<br />
De multe ori realizarea unui proiect <strong>software</strong>, asemănător construcŃiei unei<br />
^ase. nu conduce la rezultatul scontat. De aceea avem nevoie <strong>de</strong> îndrumare, <strong>de</strong><br />
echivalentul unui arhitect, <strong>de</strong> cineva care posedă cunoştinŃele şi experienŃa necesare<br />
pentru <strong>de</strong>sign-ul unui <strong>software</strong>: avem nevoie <strong>de</strong> un guru în domeniul proiectării.<br />
Din păcate, astfel <strong>de</strong> guru nu sunt prea mulŃi în lume, prin urmare companiile<br />
trebuie să-şi formeze proprii experŃi <strong>software</strong>. Deci dorim crearea unui <strong>software</strong> <strong>de</strong><br />
calitate, dar nu ştim cum să luăm <strong>de</strong>ciziile corecte, <strong>de</strong>cizii care să ne conducă în final<br />
'a realizarea unui produs <strong>de</strong> calitate.<br />
Dacă totuşi există o modalitate <strong>de</strong> a colecŃiona cunoştinŃele <strong>de</strong> care avem<br />
r.evoie tară prea mari dificultăŃi? Dacă putem totuşi să înregistrăm conceptele <strong>de</strong><br />
.iesign <strong>de</strong> <strong>software</strong>, construind o bază pentru generaŃiile viitoare? Există o astfel <strong>de</strong><br />
cale - şabloanele <strong>de</strong> <strong>proiectare</strong>.<br />
Se ştie că experŃii recurg <strong>de</strong>seori la aplicarea unor soluŃii pe care le-au mai<br />
rolosit atunci când rezolvă probleme noi. Apelează la vechile soluŃii pe care le<br />
generalizează, adaptând apoi soluŃia generală Ia contextul noii probleme.<br />
I<strong>de</strong>ea din spatele şabloanele <strong>de</strong> <strong>proiectare</strong> este realizarea unei căi standard <strong>de</strong><br />
reprezentare a soluŃiilor generale ale problemelor <strong>de</strong>s întâlnite în <strong>de</strong>zvoltarea <strong>de</strong> -<br />
oftware. Există câteva avantaje ale acestui lucru:<br />
- cu timpul se pot construi cataloage <strong>de</strong> şabloane<br />
- începătorii în <strong>de</strong>zvoltarea <strong>de</strong> <strong>software</strong> pot beneficia <strong>de</strong> pe urma<br />
experienŃei acumulate în trecut<br />
- şabloanele standard îi ajuta atât pe începători cât şi pe experŃi să înŃeleagă<br />
implicaŃiile <strong>de</strong>ciziilor pe care le iau<br />
- şabloanele <strong>de</strong> <strong>proiectare</strong> au un vocabular comun, acest lucru făcând<br />
comunicarea <strong>de</strong>ciziilor către cei care <strong>de</strong>zvolta <strong>software</strong>-ul mult mai uşoară; <strong>de</strong>cât să<br />
r.escriem un <strong>de</strong>sign în <strong>de</strong>taliu, putem folosi un nume <strong>de</strong> şablon pentru a explica ceea<br />
.e dorim să realizăm; putem <strong>de</strong> asemenea face o legătură între şabloane, putându-se<br />
.-.cutând <strong>de</strong>spre un set <strong>de</strong> şabloane <strong>de</strong> <strong>proiectare</strong> pentru Smalltalk într-o prezentare la<br />
. nferinŃa OOPSLA din 1987. James Coplien a promovat <strong>de</strong> asemenea şabloanele,<br />
^r;md o carte <strong>de</strong>spre <strong>de</strong>zvoltarea şabloanelor pentru C++ la începutul anilor '90.<br />
OOPSLA a oferit mediul excelent pentru schimbul <strong>de</strong> i<strong>de</strong>i între creatorii<br />
^Hoanelor <strong>de</strong> <strong>proiectare</strong>. Un alt forum important care a ajutat la evoluŃia şabloanelor<br />
.: rost Hillsi<strong>de</strong> Group, creat <strong>de</strong> Kent Beck şi Grady Booch.
Probabil contribuŃia cea mai importantă în popularizarea şabloanelor <strong>de</strong><br />
<strong>proiectare</strong> a avut-o cartea din 1995 "Şabloane <strong>de</strong> <strong>proiectare</strong>: Elemente <strong>de</strong> re utilizare a<br />
<strong>software</strong>-ului orientat pe obiecte", scrisa <strong>de</strong> Erich Gamma, Richard Helm, Ralph<br />
Johnson şi John Vlissi<strong>de</strong>s.<br />
După publicarea acestor două cărŃi, şabloanele <strong>de</strong> <strong>proiectare</strong> au început să se<br />
bucure <strong>de</strong> un mare interes în rândul comunităŃii <strong>software</strong>. Limbajul Java s-a <strong>de</strong>zvoltat<br />
in acelaşi timp în care şabloanele câştigau popularitate, <strong>de</strong>ci cei care foloseau Java au<br />
început să aplice şabloanele în proiectele lor. Continua creştere a popularităŃii<br />
şabloanele <strong>de</strong> <strong>proiectare</strong> în Java s-a manifestat prin prezentări în conferinŃe ca<br />
JavaOne. precum şi prin articole privind şabloanele în revistele <strong>de</strong>dicate limbajului<br />
Java.<br />
1.3. Concepte <strong>de</strong> baza în şabloane<br />
La baza i<strong>de</strong>ii <strong>de</strong> şablon stă conceptul <strong>de</strong> standardizare a informaŃiilor <strong>de</strong>spre o<br />
problemă comună şi soluŃia ei. Unul din cele mai folositoare rezultate ale muncii lui<br />
Alexan<strong>de</strong>r a fost <strong>de</strong>zvoltarea formei sau formatului în care e reprezentat şablonul.<br />
Este important ca un şablon să aibă un nume sugestiv şi să răspundă la<br />
întrebarea "Ce face aceste şablon?". în plus, trebuie să mai includă o discuŃie asupra<br />
problemei, o explicaŃie asupra modului în care şablonul rezolvă problema şi o<br />
indicaŃie privind beneficiile şi importanŃa folosirii şablonului respectiv.<br />
Evi<strong>de</strong>nt, <strong>de</strong>-a lungul timpului au apărut variaŃii ale formei Alexandriene,<br />
<strong>de</strong>zvoltate conform necesităŃiilor apărute în crearea unui anumit <strong>software</strong>. în cele ce<br />
urmează se va folosi forma formată din următoarele template-uri:<br />
a) Nume - un nume <strong>de</strong>scriptiv al şablonului<br />
b) Cunoscut şi ca - un nume alternativ, dacă există<br />
c) ProprietăŃile şablonului - clasificarea şablonului. Şablonul va fi <strong>de</strong>finit prin:<br />
obiecte<br />
l.Tip:<br />
- creationale - şabloane pentru crearea obiectelor<br />
- comportamentale - şabloane ce coordonează interacŃiunea obiectelor<br />
- structurale - şabloane care asigură relaŃiile statice şi structurale dintre<br />
- <strong>de</strong> sistem - şabloane care asigură interacŃiune la nivel a sistemului<br />
2. Nivel:<br />
- clasă - şablonul aplicat unei singure clase<br />
- componentă - şablonul ce cuprin<strong>de</strong> un grup <strong>de</strong> clase<br />
- arhitectural - şablonul e folosit pentru a coordona acŃiunile sistemului<br />
sau ale subsistemului<br />
d) Scop - o scurtă explicaŃie privind ceea ce face şablonul<br />
e) Introducere - o scurtă <strong>de</strong>scriere a unei probleme un<strong>de</strong> poate fi folosit şablonul<br />
f) Utilizare - un<strong>de</strong> şi cum se poate folosi şablonul <strong>de</strong> <strong>proiectare</strong><br />
L T ) Descriere - discuŃie <strong>de</strong>taliată asupra şablonului - ce face şi cum se comportă<br />
h) Implementare - o discuŃie <strong>de</strong>spre ce trebuie făcut pentru a implementa şablonul<br />
î) Beneficii şi inconveniente - consecinŃele utilizării şablonului<br />
I) Variante ale şablonului - implementări alternative posibile<br />
k) Şabloane asociate - alte şabloane care sunt asociate sau apropiate <strong>de</strong> şablon
II. Şabloane creaŃionale<br />
Aceste şabloane realizează unul dintre cele mai comune lucruri în programarea<br />
iernată pe obiecte - crearea obiectelor într-un sistem. Majoritatea sistemelor<br />
ientate pe obiecte necesită instanŃierea în timp a multor obiecte, iar aceste şabloane<br />
: iiină crearea obiectelor prin:<br />
- instanŃiere generică - aceasta face ca obiectele să fie create în sistem fără a<br />
:ntifica o clasă specifică în cod<br />
- simplitate - unele şabloane fac uşoara crearea unui obiect, astfel că nu mai<br />
■nuie scris un cod mare şi complex pentru instanŃierea obiectului<br />
- constructori - unele şabloane forŃează constructorii <strong>de</strong> un anumit tip pentru a<br />
:a obiectele în cadrul sistemului<br />
II.l. Abstract Factory<br />
• Cunoscut şi ca Kit sau Toolkit<br />
ProprietăŃi<br />
■ Scop<br />
- Tip: creaŃional<br />
- Nivel: componentă<br />
Să asigure o modalitate <strong>de</strong> creare a familiilor <strong>de</strong> obiecte <strong>de</strong>pen<strong>de</strong>nte sau<br />
■r.rudite fără a specifica clasele lor concrete.<br />
• Introducere<br />
Presupunem că <strong>de</strong> doreşte crearea unei aplicaŃii care să fie un manager<br />
personal <strong>de</strong> informaŃii, cuprizând adrese şi numere <strong>de</strong> telefoane. AplicaŃia va fi o<br />
combinaŃie între o carte <strong>de</strong> telefoane şi un planificator personal, urmând a utiliza<br />
intensiv datele ce conŃin adresele şi numerele <strong>de</strong> telefoane. Se pot crea iniŃial clase -<br />
are să reprezinte adresa şi numărul <strong>de</strong> telefon. Se scrie apoi codul acestor clase astfel<br />
încât ele să reŃină informaŃiile relevante. De exemplu, toate numerele <strong>de</strong> telefon din<br />
America <strong>de</strong> Nord sunt formate din maxim zece cifre iar codul poştal are un format<br />
specific.<br />
Imediat ce codul claselor a fost scris corespunzător acestor condiŃii, ne dăm<br />
seama că trebuie să facem acelaşi lucru şi pentru adrese şi numere <strong>de</strong> telefon din altă<br />
;ară. <strong>de</strong> exemplu Olanda. Dar aici sunt alte reguli privind numerele <strong>de</strong> telefon şi<br />
adresele, prin urmare clasele Address şi PhoneNumber trebuie modificate pentru a lua<br />
in consi<strong>de</strong>rare regulile din noua Ńară.
Urmează apoi introducerea informaŃiilor <strong>de</strong>spre altă Ńară şi aşa mai <strong>de</strong>parte. Cu<br />
fiecare nou set <strong>de</strong> reguli, clasele Address şi PhoncNumber <strong>de</strong>vine din ce în ce mai<br />
încărcate cu cod şi mai greu <strong>de</strong> lucrat cu ele. necesitând mereu şi o noua compilare.<br />
Şablonul Abstract Factory rezolvă această problemă. Folosind acest şablon, se<br />
poate <strong>de</strong>fini o clasă generică AddressFactory care produce obiecte ce urmează<br />
şablonul general dat <strong>de</strong> Address şi PhoneNumber.<br />
Astfel fiecare Ńară va avea propria versiune a claselor Address şi<br />
PhoneNumber. In loc <strong>de</strong> a adaugă mereu informaŃie claselor, se extin<strong>de</strong> Address la<br />
AddressHolland şi PhoneNumber la PhoneNumberHolland. InstanŃe ale ambelor clase<br />
sunt create printr-o clasa AddressFactoryOlanda. Acest lucru oferă o mare libertate <strong>de</strong><br />
extin<strong>de</strong>re a codului iară a face modificări structurale majore în restul sistemului.<br />
• Utilizare<br />
Şablonul Abstract Factory se foloseşte când:<br />
- clientul e in<strong>de</strong>pen<strong>de</strong>nt <strong>de</strong> crearea produselor<br />
- aplicaŃia e configurată folosind una din multele familii <strong>de</strong> produse<br />
- obiectele trebuie create ca un set pentru a fi compatibile<br />
- se doreşte relevarea unei colecŃii <strong>de</strong> clase doar prin legăturile şi relaŃiile<br />
dintre clase şi nu prin implementarea lor<br />
■ Descriere<br />
Câteodată o aplicaŃie necesită folosirea unei game diferite <strong>de</strong> resurse sau medii<br />
operative. Exemple ar fi lucrul în Windows, fişierele sistem sau comunicarea cu alte<br />
aplicaŃii sau sisteme.<br />
Astfel că se doreşte realizarea unei aplicaŃii <strong>de</strong>stul <strong>de</strong> flexibile încât să<br />
folosească o gama larga <strong>de</strong> resurse fără a fi nevoie să se rescrie aplicaŃia <strong>de</strong> fiecare<br />
dată când o nouă resursă e introdusă. O modalitate <strong>de</strong> a rezolva această problemă este<br />
<strong>de</strong> a introduce un creator generic <strong>de</strong> resurse şi anume Abstract Factory. Acesta are una<br />
sau mai multe meto<strong>de</strong> <strong>de</strong> creare, care pot fî apelate pentru a produce resurse generice<br />
sau produse abstracte.<br />
Tehnologia Java rulează pe mai multe platforme, <strong>de</strong> fiecare dată existând o<br />
altă implementare a fişierelor sistem. SoluŃia pe care Java a adoptat-o este <strong>de</strong> a<br />
abstractiza conceptele <strong>de</strong> fişiere şi <strong>de</strong> a nu arată implementarea lor propriu-zisă.<br />
AplicaŃia poate fi <strong>de</strong>zvoltată folosind capacităŃile generice ale resurselor care prezintă<br />
o funcŃionalitate reală.<br />
în timpul rulării, clasele ConcreteFactories şi ConcreteProducts sunt create şi<br />
folosite <strong>de</strong> aplicaŃie. Clasele concrete sunt conforme cu <strong>de</strong>finirile lor din clasele<br />
AbstractFactory şi AbstractProducts, astfel că ele pot fi utilizate direct fără a fi<br />
rescrise sau recompilate.
Implementare<br />
Diagrama <strong>de</strong> clase a şablonului Abstract Factory este următoarea:<br />
De regulă pentru implementarea şablonului Abstract Factory <strong>de</strong> folosesc:<br />
- AbstractFactory - o clasă abstractă sau o interfaŃă care <strong>de</strong>fineşte meto<strong>de</strong>le <strong>de</strong><br />
. _/.:e ale produselor abstracte<br />
- AbstractProduct - o clasă abstractă sau o interfaŃă care <strong>de</strong>scrie<br />
- ".vM-iamentul general al resurselor care vor 1! folosite <strong>de</strong> aplicaŃie<br />
- ConcreteFactory - o clasă <strong>de</strong>rivată din clasa AbstractFactory şi care<br />
■ .mentează meto<strong>de</strong> <strong>de</strong> creare pentru unul sau mai multe produse concrete<br />
- ConcreteProduct - o clasă <strong>de</strong>rivată din clasa AbstractProduct şi care conŃine<br />
■ j:nentarea unei resurse sau a unui mediu <strong>de</strong> operare specific<br />
5
Beneficii şi inconveniente<br />
In şablon Abstract Factory ajută la creşterea pe ansamblu a flexibilităŃii unei<br />
i caŃii. Această flexibilitate se manifestă atât în etapa <strong>de</strong> <strong>de</strong>sign cât şi în cea <strong>de</strong><br />
.;ve a aplicaŃiei. în timpul <strong>de</strong>sign-ului. trebuie să se <strong>de</strong>cidă viitoarele utilizări ale<br />
:caŃiei. La rulare, aplicaŃiei îi pot fi uşor integrate noi caracteristici şi resurse.<br />
Un alt beneficiu al acestui şablon este acela că el poate simplifica testarea<br />
:ului aplicaŃiei. Implementarea unor clase <strong>de</strong> test şi anume TestConcreteFactory şi -<br />
tConcretcProduct este simplă, putându-se astfel simula comportamentul aşteptat al<br />
.:rselor.<br />
Pentru a putea beneficia <strong>de</strong> acest şablon, trebuie <strong>de</strong>finită cu atenŃie o interfaŃă<br />
.crică corespunzătoare pentru produsul abstract. Daca produsul abstract e <strong>de</strong>finit -<br />
rect. realizarea unor produse concrete poate fi dificilă sau chiar imposibilă.<br />
Variante ale şablonului<br />
După cum s-a menŃionat anterior, se pot <strong>de</strong>fini AbstractFactory şi<br />
stractProduct ca interfeŃe sau clase abstracte, în funcŃie <strong>de</strong> necesităŃile aplicaŃiei sau<br />
^referinŃe. Unele variante ale şablonului Abstract Factory permit producerea mai<br />
.'.tor obiecte ConcreteFactory. rezultând o aplicaŃie care poate folosi simultan<br />
tiiii multiple <strong>de</strong> ConcreteProducts.<br />
Şabloane asociate<br />
Printre şabloanele asociate lui Abstract Factory se numără:<br />
- Factory Method<br />
- Singleton<br />
- Data Access Object<br />
Exemplu<br />
Dacă se doreşte ca un program să afişeze date în două locaŃii diferite, folosind<br />
\;ză <strong>de</strong> date locală sau la distanŃă, se poate implementa o interfaŃă cu ajutorul<br />
.-iiului Abstract Factorv astfel:<br />
Bcneficii §'\ inconveniente<br />
l.'n sablon Abstract Factory ajuta la crestcrca pe ansamblu a flexibilitatii unei<br />
:catii. Aceasta flexibilitate se manifests atat in etapa <strong>de</strong> <strong>de</strong>sign cat si in cea <strong>de</strong><br />
^rc a aplicatiei. In timpul <strong>de</strong>sign-ului. trebuie sa se <strong>de</strong>cida viitoarele utilizari ale<br />
■.catiei. La rulare. aplicatiei fi pot fi usor integrate noi caracteristici si resurse.<br />
Un alt beneficiu al acestui sablon este acela ca el poate simplifica testarea<br />
v.ilui aplicatiei. Implementarea unor clase <strong>de</strong> test si anume TestConcreteFactory §i -<br />
:ConcreteProduct este simpla, putandu-se astfel simula comportamentul asteptat al<br />
.:rselor.<br />
Pentru a putea beneficia <strong>de</strong> acest sablon, trebuie <strong>de</strong>fmita cu atentie o interfata<br />
.erica corespunzatoare pentru produsul abstract. Daca produsul abstract e <strong>de</strong>finit<br />
Meet, realizarea unor produse concrete poate fi dificila sau chiar imposibila.<br />
\ ariante ale §ablonului<br />
Dupa cum s-a mentionat anterior, se pot <strong>de</strong>fini AbstractFactory si<br />
straetProduct ca interfete sau clase abstracte, in functie <strong>de</strong> necesitatile aplicatiei sau<br />
: v eferinte. Unele variante ale sablonului Abstract Factory permit producerea mai
.'.tor obiecte ConcreteFactory. rezultand o aplicatie care poate folosi simultan<br />
::!ii multiple <strong>de</strong> ConcreteProducts.<br />
Sabloane asociate<br />
Printre sabloanele asociate lui Abstract Factory se numara:<br />
- Factory Method<br />
- Singleton<br />
- Data Access Object<br />
Exemplu<br />
Daca se doreste ca un program sa afiseze date in doua locatii diferite, folosind<br />
\;za <strong>de</strong> date locala sau la distanta, se poate implementa o interfata cu ajutorul<br />
.-nului Abstract Factory astfel:<br />
r.~-;:rface ConnectionFactory {<br />
Local qetLocalConnection(); Remote<br />
getRemcteCor.nection ( ) ;<br />
\.;=s DataKanager implements ConnectionFactcry { boolean local = false;<br />
Datalnfo[] data; public Local ger.LocalConnection() {<br />
return new Local Mo<strong>de</strong>(); } public Remote<br />
getRemoteConnection() {<br />
return new RemoteMo<strong>de</strong>();<br />
public void loadData { ) { if(local){<br />
Local conn = getLccalConnection ( ) ; data = conn. ioadDB (<br />
"dlo.db"; ;<br />
.. r.z-:-,rface ConnectionFactory {<br />
Local qetLocalConnection(); Remote<br />
getRemcteCor.nection ( ) ;<br />
r-. ass DataKanager implements ConnectionFactcry { boolean local = false;<br />
Datalnfo[] data; public Local ger.LocalConnection() {<br />
return new Local Mo<strong>de</strong>(); } public Remote<br />
getRemoteConnection() {<br />
return new RemoteMo<strong>de</strong>();<br />
public void loadData { ) { if(local){<br />
Local conn = getLccalConnection ( ) ; data = conn. ioadDB (<br />
"dlo.db"; ;
II.2. Buil<strong>de</strong>r<br />
ProprietăŃi<br />
Scop<br />
telse {<br />
Rerr.ote conn = getRenoieConnecti ori (} ;<br />
conn . connect2WWW ( "v«w . sorr.e . vihere . con:'<br />
data = conn.loadDB("db.db");<br />
i<br />
)lic void setConnection(boolean b) { local = b;<br />
- Tip: creaŃional<br />
- Nivel: componentă<br />
Sa simplifice crearea obiectelor complexe prin <strong>de</strong>finirea unei clase al cărui<br />
-:e <strong>de</strong> a construi instanŃe ale altei clase. Şablonul Buil<strong>de</strong>r realizează un produs<br />
'dl în care. chiar dacă există mai multe clase, conŃine întot<strong>de</strong>auna o clasă<br />
Introducere<br />
în managerul personal <strong>de</strong> informaŃii, utilizatorii pot dori a lucra cu un calendar<br />
'.. Pentru aceasta trebuie <strong>de</strong>finită o clasă Appointment conŃinând informaŃii<br />
■J un singur eveniment, urmărind informaŃii precum:<br />
- date <strong>de</strong> început şi sfârşit ale întâlnirilor<br />
- o <strong>de</strong>scriere a întâlnirii<br />
- locaŃia întâlnirii<br />
- participanŃii la întâlnire<br />
Evi<strong>de</strong>nt, aceste informaŃii sunt introduse <strong>de</strong> un utilizator atunci când îşi<br />
jj./.ă o întâlnire, <strong>de</strong>ci trebuie <strong>de</strong>finit un constructor care să permită crearea unui<br />
'Mect <strong>de</strong> tip Appointment (întâlnire). Diferite informaŃii sunt necesare pentru<br />
JJ unui obiect întâlnire, <strong>de</strong>pinzând <strong>de</strong> tipul acesteia. Unele întâlniri presupun<br />
.Tiirea unei liste <strong>de</strong> participanŃi. Unele pot avea o dată <strong>de</strong> început şi una <strong>de</strong> sfârşit.<br />
pot avea o singură dată. Când sunt luate în consi<strong>de</strong>rare aceste opŃiuni, crearea<br />
:elor Appointment nu este trivială.<br />
Sunt doua posibilităŃi <strong>de</strong> creare a obiectelor, insa nici una din ele atractivă. Se<br />
rea constructori pentru fiecare tip <strong>de</strong> întâlnire în parte sau se poate scrie un<br />
: ..etor enorm cu o mare funcŃionalitate logică. Fiecare dintre aceste modalităŃi<br />
".convenienŃe - constructori mulŃi, logica <strong>de</strong>venind mult mai complexă. Mai rău<br />
.-*.. ambele modalităŃi creează probleme atunci când se încearcă implementarea -<br />
uhelase a clasei Appointment.
Daca însă responsabilitatea creării unei întâlniri e cedată unei clase speciale<br />
AppointmentBuil<strong>de</strong>r, atunci are loc simplificarea codului clasei Appointment.<br />
AppoinlmentBuil<strong>de</strong>r conŃine meto<strong>de</strong> <strong>de</strong> creare a părŃilor unei întâlniri, având nume<br />
sugestive. In plus. clasa AppointmentBuil<strong>de</strong>r asigură că informaŃiile introduse la<br />
crearea unei întâlniri sunt vali<strong>de</strong>.<br />
• Utilizare<br />
Şablonul Buil<strong>de</strong>r se foloseşte când o clasă:<br />
- are o structură internă complexă, în special una cu un set variabil <strong>de</strong> obiecte<br />
asociate<br />
- are atribute care <strong>de</strong>pind unele <strong>de</strong> altele. Unul din lucrurile pe care şablonul<br />
Buil<strong>de</strong>r îl poate face este să forŃeze construirea unui obiect complex<br />
- foloseşte alte obiecte ale sistemului care pot fi dificil <strong>de</strong> obŃinut în timpul<br />
creării<br />
• Descriere<br />
Deoarece şablonul se ocupă <strong>de</strong> construirea unui obiect complex din mai multe<br />
surse posibile şi diferite, el se numeşte Buil<strong>de</strong>r - constructor. Pe măsura ce crearea<br />
obiectelor creşte în complexitate, crearea obiectelor cu ajutorul constructorului poate<br />
<strong>de</strong>veni dificilă. Acest lucru se întâmplă <strong>de</strong> regulă când obiectul nu <strong>de</strong>pin<strong>de</strong> exclusiv<br />
<strong>de</strong> resursele care se află sub controlul său.<br />
Obiectele <strong>de</strong> tip afacere fac parte din această categorie. Ele necesită frecvent<br />
date dintr-o bază <strong>de</strong> date pentru a se iniŃializa, precum şi asocierea cu alte obiecte <strong>de</strong><br />
tip afacere pentru a reprezenta cât mai fi<strong>de</strong>l mo<strong>de</strong>lul <strong>de</strong> afacere. Un alt exemplu ar fi<br />
un obiect compus din sistem, cum ar fi obiectul care reprezintă un <strong>de</strong>sen într-un<br />
program vizual <strong>de</strong> editare. Un astfel <strong>de</strong> obiect trebuie asociat cu un număr arbitrar <strong>de</strong><br />
alte obiecte imediat ce este creat.<br />
In astfel <strong>de</strong> cazuri, este convenabilă <strong>de</strong>finirea unei alte clase care să fie<br />
responsabilă cu crearea. Şablonul Buil<strong>de</strong>r coordonează asamblarea obiectului produs:<br />
crează resurse, reŃine rezultate intermediare şi pune la dispoziŃie structura funcŃională<br />
a creării. In plus. el poate obŃine resurse <strong>de</strong> sistem necesare pentru construirea<br />
obiectului produs.<br />
• Implementare<br />
Diagrama <strong>de</strong> clase a şablonului Buil<strong>de</strong>r este următoarea:<br />
Director<br />
-AbstractBuil<strong>de</strong>rf] buil<strong>de</strong>r<br />
+Product buildProductQ<br />
pentru fiecare parte a<br />
produsului buil<strong>de</strong>r.buildPartQ<br />
0..*<br />
ConcreteBuil<strong>de</strong>r<br />
+void buildPartQ<br />
+Product getProductQ<br />
inter face<br />
AbstractBuil<strong>de</strong>r<br />
+void buildPartQ<br />
creează. Product
Pentru implementarea şablonului Builer este nevoie <strong>de</strong>:<br />
- Director - are o referinŃă către o instanŃa AbstractBuil<strong>de</strong>r. Clasa Director<br />
apelează meto<strong>de</strong>le creaŃtionale pentru a construi diferite părŃi ale produsului, precum<br />
şi Buil<strong>de</strong>r-ul<br />
- AbstractBuil<strong>de</strong>r - interfaŃa care <strong>de</strong>fineşte meto<strong>de</strong>le <strong>de</strong> creare disponibile<br />
pentru crearea părŃilor diferite ale produsului<br />
- ConcreteBuil<strong>de</strong>r - implementează interfaŃa AbstractBuil<strong>de</strong>r. Implementează<br />
toate meto<strong>de</strong>le necesare creării unui obiect real <strong>de</strong> tip Product. Meto<strong>de</strong>le acestei clase<br />
ştiu cum să proceseze informaŃiile primite <strong>de</strong> la clasa Director şi să construiască<br />
părŃile respective ale obiectului Product. Clasa ConcreteBuil<strong>de</strong>r are <strong>de</strong> asemenea ori o<br />
metodă getProduct sau o metodă creatională care returneazâ instanŃa produsului<br />
- Product - obiectul rezultat. El poate fi <strong>de</strong>finit fie ca o clasă, fie ca o interfaŃă<br />
■ Beneficii şi inconveniente<br />
Pentru obiectele care necesită o creare în mai mulŃi paşi, Buil<strong>de</strong>r se comportă<br />
ca un obiect <strong>de</strong> nivel înalt care supervizează procesul. F,I poate coordona şi valida<br />
crearea tuturor resurselor şi dacă este nevoie pune Ia dispoziŃie o strategie <strong>de</strong> reluare a<br />
procesului în cazul apariŃiei unei erori. Pentru obiectele care au nevoie <strong>de</strong> resurse <strong>de</strong><br />
sistem în timpul creării, cum ar fi conexiuni cu baza <strong>de</strong> date sau obiecte <strong>de</strong> tip afacere<br />
<strong>de</strong>ja existente, Buil<strong>de</strong>r are un o modalitate convenabilă <strong>de</strong> control al acestor resurse.<br />
Principalul inconvenient al acestui şablon este acela că există o legătură<br />
strânsă între produse şi toate obiectele create în timpul construirii acestora. Prin<br />
urmare, schimbări care apar în produsul creat <strong>de</strong> Buil<strong>de</strong>r conduc <strong>de</strong> regulă şi la<br />
modificări ale Buil<strong>de</strong>r-ului.<br />
■ Variante ale şablonului<br />
Este posibilă implementarea mai multor şabloane Buil<strong>de</strong>r în jurul unei singure<br />
clase Buil<strong>de</strong>r. Pentru o mai mare flexibilitate, se poate extin<strong>de</strong> acest şablon <strong>de</strong> bază<br />
prin una sau mai multe din următoarele posibilităŃi:<br />
- se creează un şablon Buil<strong>de</strong>r abstract. Prin <strong>de</strong>finirea unei clase sau interfeŃe<br />
care să specifice meto<strong>de</strong>le <strong>de</strong> creare, se poate produce un sistem mult mai generic care<br />
poate găzdui diferite tipuri <strong>de</strong> Buil<strong>de</strong>re<br />
- se <strong>de</strong>finesc meto<strong>de</strong> ereaŃionalc multiple pentru Buil<strong>de</strong>r. Unele Buil<strong>de</strong>re<br />
<strong>de</strong>finesc meto<strong>de</strong> multiple pentru a pune la dispoziŃie o mai marc varietate <strong>de</strong> moduri<br />
<strong>de</strong> iniŃializare a resurselor necesare<br />
- se <strong>de</strong>zvoltă <strong>de</strong>legaŃi creaŃionali. Astfel, un obiect <strong>de</strong> tipul Director conŃine<br />
metoda <strong>de</strong> creare ale produsul general şi apelează o serie <strong>de</strong> meto<strong>de</strong> crcaŃionale ale<br />
obiectului Buil<strong>de</strong>r. In acest caz, Obiectul Director se comportă ca manager al<br />
:esului <strong>de</strong> creare al Buil<strong>de</strong>r-ului<br />
■ Şabloane asociate<br />
Printre şabloanele asociate se numără şablonul Composite. De multe<br />
ori . donul Buil<strong>de</strong>r este folosit pentru a produce obiecte <strong>de</strong> tip Composite care au o .<br />
jetură foarte complexă.<br />
• Exemplu<br />
Se poate folosi §ablonul Buil<strong>de</strong>r pentru crearea unui produs complex, <strong>de</strong><br />
.'.xemplu o mtalnire:
. ■ - - 2 class /ippoir tmGnt Bui 1 <strong>de</strong> r {<br />
public static final int START_DATE_REQUIRED =■ 1; public static final<br />
int END_ DATE_RF,QU1RED = 2; public static final int<br />
DESCRTPTION__REQUIRED = 4; public static final int<br />
ATTENDEE_REQUIRED = 8; public static final int<br />
LOCATION_REQUIRED = 16; protected Appointment appointment;<br />
protected int requiredElements; public void buildAppointment () {<br />
appointment = new Appointment();<br />
public void buildDates(Date starcDate, Dare endDate) { Date currentDate = new<br />
Date(); if ( (startDate i= null) && (startDate.after (currentDate))) {<br />
appointment.setstartDate(startDate); }<br />
if ((endDate != null) && (endDate.after (startDate))) {<br />
appointment.setEndDate(endDate); }<br />
public void buildDescriution(String newDescription) {<br />
appointment.setDescription(newDescription);<br />
public void build-Atten<strong>de</strong>es (ArrayList atten<strong>de</strong>es) {<br />
if ((atten<strong>de</strong>es != null) && (!atten<strong>de</strong>es.isEmpty())) {<br />
appointment.setAtten<strong>de</strong>es(atten<strong>de</strong>es); }<br />
public void buildLocation(Location newLocation) { if (newLocation !=<br />
null) {<br />
appointment.setLocation(newLocation); }<br />
}<br />
public Appointment getAppointment() throws<br />
Inf orrriationRequiredException { requiredElements = 0; rf<br />
(appointment.getStartDate() == null) {<br />
requiredElements += START_DATE_REQUIRED; } if<br />
(appointment.getLocation() == null) {<br />
requiredElements += LOCATION_REQUIRED; } if<br />
(appointment.getAtten<strong>de</strong>es().isEmpty()) {<br />
requiredElements += ATTENDEE_REQUIRED; } if<br />
(requiredElements > 0) {<br />
throw new InformationRequiredException<br />
(requiredElements); } return<br />
appointment;<br />
public int getRequiredElements() { return<br />
requiredElements;<br />
II.3. Factory Method<br />
• Cunoscut şi ca Virtual Constructor<br />
• ProprietăŃi<br />
Scop<br />
- Tip: creaŃional<br />
- Nivel: clasă<br />
Să <strong>de</strong>finească o metodă standard <strong>de</strong> creare a unui obiect, diferită <strong>de</strong> un<br />
constructor, lăsând însă subclasei alegerea tipului obiectului ce urmează a fi creat.<br />
• Introducere<br />
Presupunem că se lucrează cu un manager personal <strong>de</strong> informaŃii. Acesta va<br />
conŃine multe bucăŃi <strong>de</strong> informaŃii esenŃiale vieŃii <strong>de</strong> zi cu zi: adrese, întâlniri, date,<br />
cărŃi citite şi altele. Aceste informaŃii nu sunt statice; <strong>de</strong> exemplu trebuie să se poată<br />
modifica adresa a unei persoane atunci când aceasta se mută sau să se modifice<br />
<strong>de</strong>taliile unei întâlniri dacă persoana <strong>de</strong> contact întârzie.
Managerul personal <strong>de</strong> informaŃii e responsabil <strong>de</strong> modificarea fiecărui câmp.<br />
Marele <strong>de</strong>zavantaj este acela că el trebuie să cunoască toate tipurile <strong>de</strong> întâlniri<br />
precum şi modificările care se pot face asupra lor. Fiecare articol conŃine diferite<br />
câmpuri şi utilizatorul trebuie să vadă un ecran <strong>de</strong> intrare apropiat acestor câmpuri.<br />
Devine foarte greu <strong>de</strong> introdus informaŃii <strong>de</strong>spre noile tipuri <strong>de</strong> sarcini pentru că<br />
trebuie adăugată managerului personal <strong>de</strong> informaŃii o noua capacitate <strong>de</strong> editare <strong>de</strong><br />
fiecare dată. potrivită pentru modificarea noului tip <strong>de</strong> articol. Mai mult. fiecare<br />
schimbare apărută într-o sarcina specifică, cum ar fi adăugarea unui câmp nou unei<br />
întâlniri, înseamnă modificarea managerului astfel încât el să recunoască noul câmp.<br />
Se ajunge la un manager personal <strong>de</strong> informaŃii foarte încâlcit şi greu <strong>de</strong> întreŃinut.<br />
SoluŃie este lăsarea articolelor, ca întâlnirile, să fie responsabile cu punerea la<br />
dispoziŃie a propriilor editoare pentru administrarea adăugărilor şi modificărilor.<br />
Managerul <strong>de</strong> informaŃii trebuie doar să ştie cum să ceară un anumit editor folosind<br />
metoda getEditor. care se afla în fiecare articol ce poate fi editat. Metoda returnează<br />
un obiect care implementează interfaŃa ItemEditor. managerul urmând a utiliza acest<br />
obiect pentru a cere o componenta JComponent sau un editor GUI. Utilizatorii pot<br />
modifica informaŃiile articolului pe care doresc sa-1 editeze şi editorul asigură că<br />
schimbările sunt aplicate corect.<br />
Toate informaŃiile <strong>de</strong>spre cum trebuie editat un anumit articol sunt conŃinute în<br />
editor, care este pus la dispoziŃie <strong>de</strong> articolul însăşi. Reprezentarea grafică a editorului<br />
este <strong>de</strong> asemenea creată <strong>de</strong> editor. Acum se pot introduce noi tipuri <strong>de</strong> articole fără a<br />
fi nevoie <strong>de</strong> modificarea managerului personal <strong>de</strong> informaŃii.<br />
• Utilizare<br />
Şablonul Factory Method se foloseşte când:<br />
- se doreşte o flexibilitate mai mare prin lăsarea anumitor <strong>de</strong>cizii, cum ar fi<br />
cele privind tipul obiectului ce trebuie creat, pe mai târziu<br />
11
- se doreşte ca o subclasa şi nu supraclasa ei. să <strong>de</strong>cidă ce tip <strong>de</strong> obiect trebuie<br />
creat<br />
- se ştie când trebuie creat un obiect dar nu se ştie lipul acestuia<br />
- este nevoie <strong>de</strong> câŃiva constructori suprascrişi care au aceeaşi listă <strong>de</strong><br />
parametri, lucru nepermis în Java. în schimb, se folosesc câteva şabloane Factory<br />
Method cu nume diferite<br />
■ Descriere<br />
Acest şablon este numit Factory Method pentru că el creează obiecte atunci<br />
când se doreşte acest lucru. Când se începe scrierea unei aplicaŃii, <strong>de</strong> multe ori nu este<br />
clar ce tipuri <strong>de</strong> componente o să se utilizeze. în mod normal există o i<strong>de</strong>e generală<br />
<strong>de</strong>spre operaŃiile pe care urmează să le facă anumite componente, dar implementarea<br />
se face ulterior şi nu reprezintă consecinŃa acelui moment.<br />
Flexibilitatea poate fi obŃinută utilizând interfeŃe pentru aceste componente.<br />
Dar problema programării cu interfeŃe este aceea că nu se pot crea obiecte dintr-o<br />
interfaŃă. Trebuie să existe o clasă implementată pentru a obŃine un obiect. In loc <strong>de</strong> a<br />
codifica o clasă specifică implementată în aplicaŃie, se extrage funcŃionalitatea<br />
constructorului şi se introduce într-o metodă. Aceasta metodă este Factory Method. Se<br />
creează astfel o clasa ConcreteCreator a cărei responsabilitate este <strong>de</strong> a crea obiectele<br />
corecte. Clasa ConcreteCreator creează instanŃe ale implementării - ConcreteProduct -<br />
unei interfeŃe - Product.<br />
• Implementare<br />
Diagrama <strong>de</strong> clase a şablonului Factory Method este următoarea:<br />
interface Creator<br />
+Product facioryMethodQ<br />
ConcreteCreator<br />
+Product factoryMethodQ<br />
return nevv ConcreteProductQ<br />
instanŃia/.ă<br />
interface Product<br />
ConcreteProduct<br />
Pentru implementarea şablonului Factory Method este nevoie <strong>de</strong>:<br />
- Product - interfaŃa obiectelor create <strong>de</strong> şablon<br />
- ConcreteProduct - clasa care implementează interfaŃa Product. Obiectele<br />
acestei clase sunt create <strong>de</strong> clasa ConcreteCreator
- Creator - interfaŃa ce <strong>de</strong>fineşte metoda iactorvMethod<br />
- ConcreteCreator - clasa ce extin<strong>de</strong> clasa Creator şi conŃine implementarea<br />
meto<strong>de</strong>i factoryMethod. Ea poate returna orice obiect care implementează interfaŃa<br />
Product<br />
Beneficii şi inconveniente<br />
Un beneficiu major este acela că managerul personal <strong>de</strong> informaŃii poate fi<br />
foarte generic. Trebuie doar să ştie cum să ceară un editor pentru un articol.<br />
InformaŃia privind modul <strong>de</strong> editare al unui articol specific este conŃinută în editor.<br />
Editorul poate <strong>de</strong> asemenea crea interfaŃa grafică cu utilizatorul pentru editare. Acest<br />
lucru face managerul <strong>de</strong> informaŃii mult mai uşor <strong>de</strong> utilizat, putându-se introduce noi<br />
tipuri <strong>de</strong> informaŃii fără a fi nevoie <strong>de</strong> modificarea programului central.<br />
Inconvenientul acestui şablon este acela că pentru a adăuga un nou tip <strong>de</strong><br />
produs, trebuie adăugată şi implementată o nouă clasă şi trebuie ori modificată o clasă<br />
ConcreteCreator <strong>de</strong>ja existentă ori creată o nouă clasă care să implementeze interfaŃa<br />
Product.<br />
• Variante ale şablonului<br />
Există mai multe variante ale şablonului Factory Method:<br />
- clasa Creator poate avea o implementare standard pentru meto<strong>de</strong>le <strong>de</strong> tip<br />
factoryMethod. In acest fel, clasa Creator nu trebuie să fie o clasă abstractă sau o<br />
interfaŃă. Beneficiul este acela că nu trebuie implementată o subclasa a clasei Creator<br />
- interfaŃa Product poate fi implementată ca o clasă abstractă, astfel putând fi<br />
adăugate implementări pentru alte meto<strong>de</strong><br />
- metoda factoryMethod poate avea un parametru. Poate apoi crea mai multe<br />
tipuri <strong>de</strong> obiecte Product pe baza parametrului dat. Acest lucru duce la scă<strong>de</strong>rea<br />
meto<strong>de</strong>lor <strong>de</strong> tip factoryMethod necesare.<br />
• Şabloane asociate<br />
Printre şabloanele asociate şablonului Factory Method se află:<br />
- Prototype - evită implementarea unei subclase a clasei Creator<br />
- Template Method - meto<strong>de</strong>le <strong>de</strong> tip Template apelează <strong>de</strong> regulă meto<strong>de</strong> <strong>de</strong><br />
tip factoryMethod<br />
- Data Access Object - foloseşte şablonul Factory Method pentru a putea crea<br />
instanŃe specifice ale obiectelor <strong>de</strong> tip Data Access Objects fără a necesita cunoştinŃe<br />
<strong>de</strong>spre baza <strong>de</strong> date specifică<br />
■ Exemplu<br />
Şablonul Factory Method poate fi folosit pentru realizarea unei picturi, după<br />
cum urmează:<br />
13
î n t e r r ci L.. e o h d o e \<br />
public voia craw(;; }<br />
dass Line impiements Shape {<br />
Point x, y;<br />
Line(Point a, Foint b; { x = a; y = b; }<br />
public voj.d drawŃ; { //se <strong>de</strong>senează o linie } }<br />
dass Square implemerits Shape {<br />
Point start;<br />
int width, height;<br />
Square (Point s, irit w, int. h) {<br />
start = s ; w i d t h = w ; height<br />
= h;<br />
}<br />
public void drawŃ) { //se <strong>de</strong>senează un pătrat }<br />
}<br />
d a s s P a i n t in g {<br />
Point x, y;<br />
int width, height, radius; Painting(Point a,<br />
Point b, int w, int h, int r)<br />
x = a;<br />
y = b;<br />
width = w;<br />
neight = h;<br />
radius = r;<br />
Shape pic;<br />
Painting pt;<br />
Shape drawLine Ń) {<br />
return new Line(x,y);<br />
Shace drawSquare ( ) {<br />
return new Square(x, width, height);<br />
ii (line) pic = pt.drawLine(); if<br />
(square) pic = pt.drawSquare()<br />
II.4. Prototype •<br />
ProprietăŃi<br />
• Scop<br />
- Tip: creaŃional<br />
- Nivel: clasă unică<br />
Să facă crearea dinamică mai uşoară prin <strong>de</strong>finirea <strong>de</strong> clase ale căror obiecte se<br />
pot duplica.<br />
14
• Introducere<br />
In managerul personal <strong>de</strong> informaŃii, se doreşte copierea unei adrese astfel<br />
încât utilizatorul să nu fie nevoit să introducă manual toate informaŃiile atunci când<br />
creează un nou contact. O modalitate <strong>de</strong> rezolvare a acestei situaŃii presupune<br />
următorii paşi:<br />
- crearea unui nou obiect <strong>de</strong> tip Address<br />
- copierea valorilor apropiate din obiectul <strong>de</strong> tip Address existent<br />
Cu toate că acesta abordare rezolvă problema, ea are un mare inconvenient -<br />
încalcă principiul încapsulării. Pentru a aplica soluŃia <strong>de</strong> mai sus, trebuie introduse<br />
apelări ale meto<strong>de</strong>lor pentru a copia informaŃiile din clasa Address în afara ei. Acest<br />
lucru duce la îngreunarea întreŃinerii codului clasei Address. Este <strong>de</strong> asemenea dificilă<br />
reutilizarea clasei Address în alte proiecte.<br />
Deoarece codul ce se doreşte copiat aparŃine clasei Address, se pot evita<br />
greutăŃile întâmpinate prin <strong>de</strong>finirea unei meto<strong>de</strong> <strong>de</strong> copiere în clasă. Această metodă<br />
produce un duplicat al obiectului <strong>de</strong> tip Address cu aceleaşi date ca şi obiectul<br />
original. Apelarea meto<strong>de</strong>i printr-un obiect <strong>de</strong> tip Address existent rezolvă problema<br />
într-o manieră mai bună şi mai apropiată <strong>de</strong> regulile programării orientate pe obiecte.<br />
• Utilizare<br />
Şablonul Prototype se foloseşte pentru crearea unui obiect care este copia unui<br />
obiect <strong>de</strong>ja existent.<br />
• Descriere<br />
Numele acestui şablon este sugestiv; el foloseşte un obiect drept prototip<br />
pentru a crea o nouă instanŃă a lui cu aceleaşi valori. El permite programelor să<br />
execute operaŃii cu copia unui obiect şi să iniŃializeze obiectele într-o stare care a fost<br />
stabilită prin utilizarea sistemului. Acest lucru este <strong>de</strong> multe ori preferabil iniŃializerii<br />
obiectului cu un set generic <strong>de</strong> valori.<br />
Exemple clasice pentru acest şablon se regăsesc în editoarele grafice şi <strong>de</strong> text,<br />
un<strong>de</strong> funcŃiile <strong>de</strong> copiere şi lipire pot îmbunătăŃi productivitatea utilizatorilor. Unele<br />
sisteme <strong>de</strong> afaceri folosesc aceeaşi modalitate, producând un mo<strong>de</strong>l iniŃial pe baza<br />
unui obiect existent, mo<strong>de</strong>l ce poate fi apoi modificat în funcŃie <strong>de</strong> noile cerinŃe.<br />
• Implementare<br />
Diagrama <strong>de</strong> clase a şablonului Prototype este următoarea:<br />
Client<br />
+void operationQ<br />
Prototype p = new prototype.copy()<br />
Prototype<br />
+Prottotype copy()<br />
15
Pentru implementarea şablonului Prototype este nevoie <strong>de</strong>:<br />
- Prototype - conŃine metoda <strong>de</strong> copiere. Metoda întoarce o instanŃă a aceleaşi<br />
clase cu aceleaşi valori ca şi instanŃa originală. Noua instanŃă poate fi o copie fi<strong>de</strong>lă<br />
sau superficială a celei originale<br />
• Beneficii şi inconveniente<br />
Şablonul Prototype este folositor <strong>de</strong>oarece permite sistemelor să producă o<br />
copie a unui obiect utilizat, cu variabile <strong>de</strong>ja setate la anumite valori, iară a <strong>de</strong>pin<strong>de</strong><br />
<strong>de</strong> cele <strong>de</strong>finite în constructor.<br />
O copie superficiala face duplicatul doar a elementelor principale ale clasei;<br />
astfel se obŃine o copie mai rapidă dar care poate să nu acopere toate necesităŃile.<br />
Deoarece caracteristicile sunt copiate din original, copia indică tot obiectul iniŃial.<br />
Prin urmare modificarea unui astfel <strong>de</strong> obiect afectează toate copiile.<br />
OperaŃiile <strong>de</strong> copiere în adâncime fac duplicatul tuturor caracteristicilor unui<br />
obiect. Acestea durează <strong>de</strong> regulă mai mult <strong>de</strong>cât copierile superficiale şi pot fi foarte<br />
costisitoare pentru obiectele care au o structură complexă. Acest lucru asigură faptul<br />
că modificările într-o copie sunt izolate <strong>de</strong> alte copii.<br />
• Variante ale şablonului<br />
Variantele şablonului Prototype includ:<br />
- Copy Constructor - acest constructor <strong>de</strong> copiere ia o instanŃă a aceleaşi clase<br />
ca argument şi returnează o nouă copie cu aceleaşi valori ca şi argumentul. Beneficiul<br />
acestei variante este aceea că intenŃia <strong>de</strong> a crea o noua instanŃă este clară dar o<br />
singură modalitate <strong>de</strong> copiere poate fi executată.<br />
- Clone Method - limbajul Java <strong>de</strong>fineşte o metodă <strong>de</strong> donare în clasa<br />
java.lang.Object - supraclasa tuturor claselor Java. Pentru ca metoda să poată fi<br />
utilizată într-o instanŃă, clasa obiectului respectiv trebuie să implementeze interfaŃa<br />
java.lang.Clonable pentru a indica că o instanŃă a acestei clase poate fi copiată<br />
■ Şabloane asociate<br />
Printre şabloanele asociate şablonului Prototype se afla:<br />
- Abstract Factory - poate folosi şablonul Prototype pentru a crea noi obiecte<br />
bazate pe cele aflate în uz<br />
- Factory Method - poate folosi şablonul Prototype ca un template pentru noile<br />
obiecte<br />
• Exemplu<br />
Un exemplu <strong>de</strong> şablon Prototype este chiar suprascrierea unei meto<strong>de</strong>:<br />
interface Shape {<br />
public void draw();<br />
i<br />
class Line implements Shape {<br />
public void draw() { System.out.println("line"); } }<br />
16
c_i_ass bquare : nplenenus Sna". e ■,<br />
pudic voi ci draw j ; { "yc .:-::.. : u~. . p r .ir.t In ( "squar e" ) ; }<br />
L<br />
J<br />
class Circle irr.plerrents Shapc- {<br />
public void draw() ; Systcr,.cur.priritInŃ"circle"); }<br />
class Paintina {<br />
public sr_a~ic void .tain ( Suriri g [ J args; {<br />
Shape si ~- nev» L i n e ( ) ;<br />
Shape s2 = nes Square();<br />
Shape s3 = new Circle ( ) ;<br />
oaint; ( si} ;<br />
painr(s2);<br />
paint ( s3} ; } static<br />
void paint(Shape s) {<br />
if (s instanceof Line) s.drawŃ)<br />
if (s instanceof Square) s.draw<br />
if îs insranceof Circle) s.draw<br />
II.5. Singleton<br />
• ProprietăŃi<br />
• Scop<br />
- Tip: creaŃional<br />
- Nivel: obiect<br />
Să existe doar o singură instanŃă a acestei clase în sistem, permiŃând altor clase<br />
acces la această instanŃă.<br />
■ Introducere<br />
Din când în când este nevoie <strong>de</strong> un obiect global, unul care să fie accesibil <strong>de</strong><br />
oriun<strong>de</strong> dar care să fie creat o singură dată. Se doreşte ca toate părŃile aplicaŃiei să<br />
poată utiliza obiectul folosind aceeaşi instanŃă. Un exemplu ar fi un istoric - o listă <strong>de</strong><br />
acŃiuni pe care le-a tăcut utilizatorul în timpul lucrului cu aplicaŃia. PărŃi multiple ale<br />
aplicaŃiei folosesc acelaşi obiect HistoryList, fie pentru a adăuga o acŃiune a<br />
utilizatorului fie pentru a şterge acŃiuni anterioare. O modalitate <strong>de</strong> a face acest lucru<br />
este crearea unui obiect global <strong>de</strong> către aplicaŃie, urmând ca fiecare obiect care are<br />
nevoie <strong>de</strong> el să aibă o referinŃă către obiectul global. Totuşi, poate fi foarte dificil <strong>de</strong><br />
<strong>de</strong>terminat cum să se trimită această referinŃă şi ce părŃi ale aplicaŃiei au nevoie <strong>de</strong><br />
acest obiect.<br />
O altă cale este <strong>de</strong> a crea valori globale folosind variabile statice. AplicaŃia ar<br />
avea câteva obiecte statice în interiorul unei clase pe care le va accesa direct. Dar şi<br />
această abordare are câteva inconveniente. Un obiect static nu este suficient <strong>de</strong>oarece<br />
el va fi creat atunci când clasa se încarcă, nelăsând astfel nici o oportunitate <strong>de</strong> a<br />
introduce date înainte <strong>de</strong> instanŃiere. Aici intra în joc şablonul Singleton. El permite<br />
accesul uşor al întregii aplicaŃii la obiectul global.<br />
17
• Utilizare<br />
Şablonul Singleton se foloseşte când se doreşte o singură instanŃa a unei clase<br />
care sa fie disponibilă peste tot.<br />
■ Descriere<br />
Şablonul Singleton asigură crearea unei singure instanŃe <strong>de</strong> către JVM. Pentru<br />
a fi siguri că <strong>de</strong>Ńinem controlul asupra instanŃtierii, constructorul trebuie <strong>de</strong>clarat<br />
privat. Apare însă o problemă: este imposibil <strong>de</strong> creat o instanŃă, astfel că o metoda <strong>de</strong><br />
acces este pusă la dispoziŃie <strong>de</strong> o metodă statică getlnstanceQ. Această metodă<br />
creează o singură instanŃă, dacă nu există <strong>de</strong>ja, şi returnează referinŃa singleton-ului<br />
celui care apelează metoda. ReferinŃa către singleton este stocată ca un atribut static<br />
privat al clasei singleton-ului pentru viitoare apelări.<br />
Cu toate că metoda <strong>de</strong> acces poate crea singleton-ul. <strong>de</strong> cele mai multe ori el<br />
este creat atunci când clasa este încărcată. întârzierea construcŃiei este necesară doar<br />
dacă o anumită formă <strong>de</strong> iniŃializare trebuie făcuta înainte ca singleton-ul sa fie<br />
instanŃiat.<br />
• Implementare<br />
Diagrama <strong>de</strong> clase a şablonului Singleton este următoarea:<br />
Singleton<br />
-Sinaleton instance = new SinaJetonO<br />
-SingletonŃ) +Singleton<br />
gctlnstanceQ<br />
return instance<br />
Pentru implementarea şablonului Singleton este nevoie <strong>de</strong>:<br />
- Singleton - clasa ce conŃine un constructor privat, menŃine o referinŃă statică<br />
privată către singura instanŃă a acestei clase şi pune la dispoziŃie o metodă <strong>de</strong> acces<br />
statică care returnează referinŃa către singura instanŃă<br />
• Beneficii şi inconveniente<br />
Şablonul Singleton este singura clasă ce-şi poate crea o instanŃă. Nu se poate<br />
crea o astfel <strong>de</strong> instanŃa fără utilizarea meto<strong>de</strong>i statice puse la dispoziŃie. Nu este<br />
nevoie a se trimite referinŃa către toate obiectele care folosesc acest singleton.<br />
Totuşi. în funcŃie <strong>de</strong> implementare, pot apărea probleme ale firelor <strong>de</strong> execuŃie.<br />
Trebuie avut mare grijă atunci când se iniŃializează un singleton într-o aplicaŃie cu<br />
multiple fire <strong>de</strong> execuŃie. Fără un control a<strong>de</strong>cvat, aplicaŃia poate conduce la un<br />
"război" al firelor <strong>de</strong> execuŃie.
• Variante ale şablonului<br />
Una dintre opŃiunile şablonului Singleton. <strong>de</strong> multe ori nebăgată în seama, este<br />
aceea <strong>de</strong> a avea mai mult <strong>de</strong> o instanŃă într-o clasă. Beneficiul este că restul aplicaŃiei<br />
poate rămâne la fel. în timp ce acelea care ştiu <strong>de</strong> aceste instanŃe multiple pot utiliza<br />
alte meto<strong>de</strong> pentru o obŃine alte instanŃe.<br />
Metoda <strong>de</strong> acces a singleton-ului poate fi punctul <strong>de</strong> intrare către un set <strong>de</strong><br />
instanŃe, toate <strong>de</strong> un subtip diferit. Metoda <strong>de</strong> acces poate <strong>de</strong>termina la rulare ce subtip<br />
specific <strong>de</strong> instanŃă să returneze. Acest lucru poate părea ciudat, dar este foarte<br />
folositor atunci când se utilizează clase dinamice. Sistemul care foloseşte singleton-ul<br />
rămâne neschimbat, în timp ce implementarea specifică a singleton-ului poate fi<br />
diferită.<br />
• Şabloane asociate<br />
Printre şabloanele asociate Singleton-ului se număra:<br />
- Abstract Factory<br />
- Buil<strong>de</strong>r<br />
- Prototype<br />
• Exemplu<br />
unica:<br />
Şablonul Singleton se poate utiliza pentru crearea unei conexiuni la distanŃă<br />
final class KemoteConnection {<br />
private Connect con;<br />
privare static RemoteConnecticn re = nev;<br />
RemoteConnection(connection) ; private<br />
RemoteConnection(Connect c) { con = c;<br />
public static RemoteConnection getRemoteConnection ( ) {<br />
return re; } public void<br />
setConnection(Connect c) f<br />
this(c);<br />
\<br />
19
III. Şabloane comportamentale<br />
Şabloanele comportamentale se concentrează pe controlul dintr-un sistem.<br />
Unele căi <strong>de</strong> organizare a controlului într-un sistem pot aduce beneficii mari atât în<br />
ceea ce priveşte eficienŃa cât şi în mentenanŃa sistemului.<br />
III.l. Chain Of Responsibility<br />
• ProprietăŃi<br />
• Scop<br />
- Tip: comportamental<br />
- Nivel: componentă<br />
Să stabilească un lanŃ în sistem astfel încât un mesaj să poată fi utilizat la<br />
nivelul la care a fost primit sau să fie direcŃionat către un obiect care îl poate folosi.<br />
• Introducere<br />
Managerul personal <strong>de</strong> informaŃii poate fi folosit şi pentru managementul<br />
proiectelor. El poate fi văzut ca o structură arborescentă <strong>de</strong> sarcini. O sarcină este<br />
rădăcina arborelui, reprezentând proiectul însuşi. Sarcina <strong>de</strong> bază are un set <strong>de</strong><br />
subsarcini, fiecare din acestea având la rândul lor un set <strong>de</strong> subsarcini, şi aşa mai<br />
<strong>de</strong>parte. In acest fel, proiectul poate fi împărŃit într-un set <strong>de</strong> obiective legate între ele.<br />
Cum se lucrează cu informaŃiile dintr-o astfel <strong>de</strong> structură? De exemplu, ar fi<br />
<strong>de</strong> ajutor să se poată ve<strong>de</strong>a cine e responsabil pentru un anumit set <strong>de</strong> sarcini. Cum se<br />
atribuie grupuri <strong>de</strong> sarcini cuiva sau cum se trec sarcini altcuiva?<br />
O modalitate este <strong>de</strong>finirea unui atribut pentru fiecare sarcină care să<br />
reprezinte persoana care o în<strong>de</strong>plineşte. Când aceasta se schimbă, toate sarcinile şi<br />
subsarcinile sunt modificate încât să conŃină numele noii persoane care le execută.<br />
Insă această cale <strong>de</strong> reŃinere a persoanei răspunzătoare <strong>de</strong> o anumită sarcină este<br />
ineficientă, necesitând înregistrarea unei cantităci mari <strong>de</strong> informaŃii. O alternativă<br />
este referinŃa către unul sau mai multe obiecte centrale care reŃin numele<br />
responsabililor <strong>de</strong> sarcini. Cu toate că această abordare utilizează mult mai eficient<br />
memoria, ea necesită multă muncă pentru a realiza legăturile dintre sarcini şi obiectele<br />
centrale folosite pentru reŃinerea datelor.<br />
Ce se întâmplă însă atunci când se foloseşte arborele <strong>de</strong> sarcini însuşi pentru<br />
managementul responsabililor <strong>de</strong> sarcini? Se <strong>de</strong>fineşte o metodă în clasa sarcinii<br />
numită getOwner, care atunci când este apelată, verifică dacă responsabilul a fost<br />
specificat şi-i returnează numele în caz afirmativ. In caz contrar, sarcina apelează<br />
metoda getOwner a sarcinii părinte a sa. Această soluŃie necesită mai puŃină muncă<br />
<strong>de</strong>cât în ambele cazuri <strong>de</strong> mai sus şi rămâne eficientă şi în ceea ce priveşte utilizarea<br />
memoriei. Responsabilul <strong>de</strong> sarcini trebuie specificat doar într-o singură locaŃie a<br />
arborelui. Obiectele <strong>de</strong> tip Task fac restul muncii, predând apelul meto<strong>de</strong>i getOwner<br />
sarcinilor părinte ale lor până când este găsită una ce conŃine informaŃii. Acesta e un<br />
exemplu al şablonului Chain Of Responsibility.<br />
20
• Utilizare<br />
Şablonul Chain Of Responsibility se foloseşte când:<br />
- există un grup <strong>de</strong> obiecte în sistem care pot răspun<strong>de</strong> la acelaşi tip <strong>de</strong> mesaj<br />
- mesajele trebuie folosite <strong>de</strong> unul din obiectele din sistem<br />
- mesajele folosesc mo<strong>de</strong>lul "folosire sau trimitere mai <strong>de</strong>parte", adică unele<br />
mesaje pot fi utilizate la nivelul un<strong>de</strong> au fost recepŃionate sau produse, în timp ce<br />
altele trebuie trimise mai <strong>de</strong>parte către alte obiecte<br />
■ Descriere<br />
Când unele acŃiuni au loc într-un sistem orientat pe obiecte, acestea sunt <strong>de</strong><br />
regulă reprezentate printr-un eveniment sau mesaj. Un astfel <strong>de</strong> mesaj poate lua forma<br />
unei meto<strong>de</strong> care e apelată sau poate fi un obiect al sistemului. Mesajul este apoi<br />
direcŃionat către un alt obiect care poate răspun<strong>de</strong> sau utiliza mesajul. In cele mai<br />
simple cazuri, acelaşi obiect care produce mesajul, răspun<strong>de</strong> la el. De exemplu, un<br />
câmp un<strong>de</strong> se introduce un text poate produce un eveniment ca răspuns la acŃiunea<br />
utilizatorului - cum ar fi tastarea.<br />
In cele mai complexe cazuri, răspunsul la mesaje implică mai multe lucruri.<br />
Un mesaj care cere o modificare în apariŃia interfeŃei grafice cu utilizatorul poate fi<br />
analizat la mai multe niveluri. Dacă cererea este aceea <strong>de</strong> a se modifica aliniamentul<br />
textului dintr-un câmp. componenta însăşi poate răspun<strong>de</strong>. însă o cerere <strong>de</strong><br />
schimbarea a aliniamentului întregului text va fi probabil direcŃionată către nişte<br />
obiecte <strong>de</strong> nivel înalt.<br />
Şablonul Chain Of Responsibility reprezintă un lanŃ pentru mesaje. Dacă un<br />
obiect nu poate răspun<strong>de</strong> la un mesaj primit, el trimite mesajul mai <strong>de</strong>parte către alt<br />
obiect. în mod frecvent, şablonul Chain Of Responsibility este implementat după un<br />
mo<strong>de</strong>l părinte-fiu. Astfel, mesajele ce nu pot fi utilizate <strong>de</strong> către fiu sunt trimise<br />
părintelui, şi aşa mai <strong>de</strong>parte dacă este cazul, până când este întâlnit un obiect ce<br />
poate răspun<strong>de</strong> mesajului. Şablonul Chain Of Responsibility este potrivit pentru o<br />
varietate mare <strong>de</strong> activităŃi efectuate cu o interfaŃă grafică cu utilizatorul orientată pe<br />
obiecte. FuncŃiile <strong>de</strong> ajutor, structura componentelor, formatare şi poziŃionare<br />
cuprinse în interfaŃa grafica pot folosi acest şablon.<br />
■ Implementare<br />
Diagrama <strong>de</strong> clase a şablonului Chain Of Responsability este următoarea:<br />
Client<br />
interface Handler<br />
void handleMessage ()<br />
ConcreteHandler<br />
-Handler handler<br />
+void handleMessageQ<br />
+void handlerMethod()<br />
if(can handle)<br />
handlerMethodQ<br />
else<br />
succesor.handleMessage()<br />
21
Pentru implementarea şablonului Chain Of Responsibility este nevoie <strong>de</strong>:<br />
- Handler - interfaŃa ce <strong>de</strong>fineşte metoda folosită pentru trimiterea mesajului<br />
către următorul Handler. Acest mesaj reprezintă <strong>de</strong> regulă doar apelarea meto<strong>de</strong>i, dar<br />
în cazul în care o cantitate mai mare <strong>de</strong> date trebuie încapsulată, poate fi trimis <strong>de</strong><br />
asemenea şi un obiect<br />
- ConcreteHandler - clasa care implemetează interfaŃa Handler. Ea păstrează o<br />
referinŃă către următoarea instanŃă către Handler. Această referinŃă este fie conŃinută<br />
în constructorul clasei sau într-o metodă proprie. Implementarea meto<strong>de</strong>i<br />
handleMessage poate <strong>de</strong>termina cum să se folosească metoda, cum să se apeleze<br />
metoda handleMethod şi cum să se trimită mesajul către următorul Handler<br />
■ Beneficii şi inconveniente<br />
Şablonul Chain Of Responsibility oferă flexibilitate mare în procesarea<br />
evenimentelor pentru o aplicaŃie, <strong>de</strong>oarece oferă posibilitatea <strong>de</strong> management a unor<br />
evenimente complexe prin împărŃirea responsabilităŃilor între un număr <strong>de</strong> elemente<br />
simple. Permite ca un set <strong>de</strong> clase să se comporte ca un întreg, astfel ca evenimentele<br />
produse într-o clasă pot fi trimise către celelalte clase. Insă flexibilitatea acestui<br />
şablon are un preŃ, el <strong>de</strong>venind dificil <strong>de</strong> <strong>de</strong>zvoltat şi testat. Pe măsură ce lanŃul <strong>de</strong>vine<br />
mai complex, trebuie monitorizată cu atenŃie propagarea evenimentelor. Eşecul în<br />
trimiterea evenimentelor poate avea ca rezultat pier<strong>de</strong>rea mesajelor, acestea rămânând<br />
fără răspuns.<br />
Daca mai multe mesaje sunt produse într-o perioadă scurtă <strong>de</strong> timp, ele fiind<br />
apoi trimise <strong>de</strong> mai multe ori până sunt utilizate, are loc încetinirea sistemului.<br />
• Variante ale şablonului<br />
Exista mai multe căi <strong>de</strong> adaptare a şablonului Chain Of Responsibility încât el<br />
sa răspundă cerinŃelor diverselor aplicaŃii. Cele două strategii luate în consi<strong>de</strong>rare sunt<br />
cele <strong>de</strong> utilizare - handling - şi cele <strong>de</strong> trimitere mai <strong>de</strong>parte - forwarding - a<br />
mesajelor.<br />
La cele <strong>de</strong> utilizare important este modul în care a fost implementat handler-ul.<br />
Printre variantele acestor strategii se număra:<br />
- Default Handler - unele implementări pornesc <strong>de</strong> la un handler <strong>de</strong> bază.<br />
Acesta este <strong>de</strong> regulă folosit atunci când nu este <strong>de</strong>finită explicit o clasă care să<br />
trimită mesajul mai <strong>de</strong>parte. Este <strong>de</strong> mare ajutor în evitarea pier<strong>de</strong>rilor <strong>de</strong> mesaje<br />
- Handle And Extend - în cazul acestei variante, utilizarea evenimentelor<br />
implică adăugarea lor într-un comportament <strong>de</strong> bază pe măsura ce ele sunt propagate<br />
<strong>de</strong>-a lungul lanŃului. Acesta este <strong>de</strong> ajutor în activităŃile <strong>de</strong> logare<br />
- Dynamic Handler - unele implementări ale şablonului Chain Of<br />
Responsibility permit ca structura <strong>de</strong> trimitere a mesajelor să fie modificată la rulare.<br />
Prin <strong>de</strong>finirea unei meto<strong>de</strong> <strong>de</strong> setare pentru fiecare clasă a lanŃului, se poate <strong>de</strong>fini şi<br />
modifica lanŃul pe măsură ce este folosit în cadrul aplicaŃiei<br />
Strategiile <strong>de</strong> trimitere mai <strong>de</strong>parte a mesajelor <strong>de</strong>finesc modalităŃi <strong>de</strong> utilizare<br />
sau trimitere a mesajelor produse <strong>de</strong> o componentă:
- Handle By Default - utilizează orice mesaj care nu e trimis mai <strong>de</strong>parte în<br />
mod implicit<br />
- Propagate By Default - trimite mai <strong>de</strong>parte un mesaj care nu a fost utilizat<br />
implicit<br />
- Forward To Default Handler - mult mai complexă <strong>de</strong>cât şablonul <strong>de</strong> baza,<br />
aceasta abordare utilizează un handler implicit <strong>de</strong> mesaje. Orice mesaj care nu este<br />
utilizat la nivel <strong>de</strong> componentă sau care nu este trimis mei <strong>de</strong>parte către un alt handler,<br />
va fi trimis automat handler-ului implicit<br />
- Ignore By Default - orice mesaj care care nu e utilizat sau trimis mai <strong>de</strong>parte,<br />
este aruncat. Daca clasele din lanŃ produc evenimente care nu sunt folosite în<br />
aplicaŃie, această variantă este o metodă acceptabilă <strong>de</strong> a le elimina. Totuşi, ea trebuie<br />
utilizată cu atenŃie pentru a nu se elimina mesaje <strong>de</strong> care sistemul are nevoie<br />
• Şabloane asociate<br />
Printre şabloanele asociate se număra şablonul Composite. Şablonul<br />
Composite pune la dispoziŃie suport pentru o structură arborescentă şi pentru<br />
propagarea mesajelor iar Chain Of Responsibility conŃine reguli privind modul <strong>de</strong><br />
propagare. în plus, şablonul Composite tin<strong>de</strong> să trimită mesaje în jos - <strong>de</strong> la rădăcină<br />
către ramuri - în timp ce şablonul Chain Of Responsibility trimite <strong>de</strong> obicei mesaje în<br />
sus - <strong>de</strong> la ramuri către rădăcină.<br />
• Exemplu<br />
Se presupune că atunci când se cheltuiesc banii unei companii, trebuie cerută<br />
aprobarea şefului sau a şefului şefului:<br />
abstract class PurchasePcwer {<br />
pretecred final double base = 500; protected<br />
PurchasePower successor; public voia<br />
setSuccessor(PurchasePower successor){<br />
this.successor = successor; } abstract public<br />
void processRequest(PurchaseRequest<br />
requesr); i<br />
ciass Manager extenas PurchasePower {<br />
private final double ALLOWA3LE = 10 * base; public<br />
void processRequest(PurchaseRequest request ) {<br />
if(requesr.getAmount() < ALLOWABLE)<br />
System.out.println("Manager will<br />
approve Ş"+ request: . getAmount ());<br />
el.se if (successor != nuli)<br />
successor.processRequest(request);<br />
class Director extenas PurchasePower {<br />
private final double ALL0WA3LE = 20 * base; public void<br />
processRequest(PurchaseRequest request ) { if (request.<br />
getAmouni () < ALLOWABLE )<br />
System, out . println ( "Director will approve $"■+-<br />
request. gecAiriourit ( ) ) ;<br />
else if(successor !— nuli)<br />
successor.processRequest(request); }<br />
23
class PurcnaseKequest {<br />
privare irit r.unbei;<br />
private dcubie artiourit;<br />
private String purpose;<br />
public PurchaseRecuest ( i:vo runcer, dcubie amount, String<br />
puroose){<br />
this.num.ber = riun;cer;<br />
this . arr.ount -■ arnount;<br />
this.purpose = purpose;<br />
public aoubie getAmount { ) { return amounr.; } public void<br />
setAmount(double amt) { amourit = amt; } public String<br />
geiPurpose() { return purpose; } public void setPurpose(String<br />
reason) {purpose = reason;} public irit getNurr.ber () { return<br />
number; } public void setNumber (int nun;) { number = num; } }<br />
class CheckAuthority {<br />
public static void n;ain (String'] args) throws Exception{<br />
Manager manager = new Manager(); Director director =<br />
new Director(); manager.setSuccessor(director);<br />
while (true) {<br />
System.out.prinŃ In("IntroduceŃi suma:") ;<br />
System.out.prinŃ(">");<br />
double d = Double.parseDouble(new<br />
BufferedRea<strong>de</strong>r(new inputStreamRea<strong>de</strong>r<br />
(System.in)).readLine());<br />
manager.crocessRequest(new PurchaseRequest(0,<br />
d, "General"));<br />
III.2. Command<br />
• Cunoscut şi ca Action, Transaction<br />
• ProprietăŃi<br />
• Scop<br />
- Tip: comportamental<br />
- Nivel: obiect<br />
Să învăluie o comandă într-un obiect astfel încât să poată fi stocată, trimisă<br />
către diferite meto<strong>de</strong> şi returnată ca oricare alt obiect.<br />
• Introducere<br />
Când un utilizator selectează o acŃiune ce urmeazc a fi executată, aplicaŃia<br />
trebuie să ştie <strong>de</strong> un<strong>de</strong> să extragă datele relevante şi comportamentul acestora. în mod<br />
normal, aplicaŃia ştie numărul opŃiunilor pe care le are un utilizator şi va reŃine acest<br />
lucru într-un loc central. Când o opŃiune e selectată, aplicaŃia află ce trebuie să facă,<br />
asamblează datele necesare şi apelează meto<strong>de</strong>le corespunzătoare.<br />
24
Multe din aplicaŃiile curente permit anularea fiecărei comenzi până la un<br />
anumit punct, cum ar fi ultima salvare efectuată. Daca se doreşte introducerea acestui<br />
lucru în aplicaŃia curentă, este nevoie <strong>de</strong> crearea unui istoric - o listă cu toate acŃiunile<br />
pe care utilizatorul le-a efectuat, cu toate datele utilizate şi cu starea prece<strong>de</strong>ntă. După<br />
vre-o trei sau patru acŃiuni, istoricul <strong>de</strong>vine mai mare <strong>de</strong>cât întreaga aplicaŃie şi asta<br />
din cauza redundanŃei datelor.<br />
Are mai mult sens combinarea acŃiunii utilizatorul într-un singur obiect -<br />
obiectul Command. Acesta conŃine datele necesare pentru o acŃiune specifică, precum<br />
şi comportamentul lor. Astfel, aplicaŃia doar invocă metoda <strong>de</strong> execuŃie a obiectului<br />
Command care execută comanda. AplicaŃia nu mai trebuie să cunoască toate opŃiunile<br />
disponibile şi poate fi uşor modificată încât să includă mai multe acŃiuni ale<br />
utilizatorului.<br />
• Utilizare<br />
Şablonul Command se foloseşte pentru:<br />
- anularea comenzilor, logare şi / sau tranzacŃii<br />
- executarea comenzilor în momente diferite<br />
• Descriere<br />
O aplicaŃie care nu foloseşte şablonul Command trebuie să conŃină meto<strong>de</strong><br />
care să se ocupe <strong>de</strong> fiecare eveniment care poate apărea. Prin urmare handler-ul<br />
trebuie să aibă toate informaŃiile pentru a putea executa acŃiunea. Introducerea unor<br />
noi acŃiuni necesită adăugarea <strong>de</strong> noi meto<strong>de</strong> clasei handler-ului.<br />
Şablonul Command încapsulează atât datele cât şi funcŃionalitatea necesară<br />
pentru a executa o acŃiune sau cerinŃă specifică. El pune la dispoziŃie o separare între<br />
cum trebuie executată o acŃiune şi momentul când trebuie executată. O aplicaŃie care<br />
foloseşte şablonul Command creează o sursă, un receptor şi o comandă. Comanda<br />
primeşte referinŃa către receptor şi sursa primeşte o referinŃă către comandă.<br />
Obiectul comandă este trimis către cel care 1-a invocat, care implementează<br />
interfaŃa Command. în forma ei cea mai simplă, interfaŃa are o metodă <strong>de</strong> executare.<br />
Clasele care o implementează reŃin receptorul ca o variabilă <strong>de</strong> instanŃă. Când metoda<br />
<strong>de</strong> executare e apelată, Command apelează metoda doAction din clasa Receiver.<br />
Command poate apela mai multe meto<strong>de</strong> ale clasei Receiver.<br />
• Implementare<br />
Diagrama <strong>de</strong> clase a şablonului Command este următoarea:<br />
Invoker<br />
interface Command<br />
+void execulef)<br />
ConcreteCommand<br />
-Receiver target<br />
----<br />
►<br />
Receiver<br />
+void execute()<br />
+void action()<br />
25
Pentru implementarea şablonului Command este nevoie <strong>de</strong>:<br />
- Command - interfaŃa ce <strong>de</strong>fineşte meto<strong>de</strong>le pe care le foloseşte clasa Invoker<br />
- Invoker - clasa care apelează meto<strong>de</strong>le <strong>de</strong> executare ale obiectului Command<br />
- Receiver - Ńinta obiectului Command şi obiectul care în<strong>de</strong>plineşte cerinŃa<br />
- ConcreteCommand - clasa ce implementează interfaŃa Command<br />
Atunci când metoda <strong>de</strong> executare e apelată, clasa ConcreteCommand apelează<br />
una sau mai multe meto<strong>de</strong> ale clasei Receiver. Când se implementează şablonul<br />
Command, trebuie tăcute anumite alegeri privind utilizarea apelurilor. Se poate face<br />
unul din următoarele lucruri:<br />
- clasa care implementează interfaŃa Command se poate obŃine combinând<br />
clasele Invoker şi Receiver. trimiŃând astfel direct toate apelurile<br />
- clasa ConcreteCommand poate fi ia receptorul, rezolvând ea însăşi cerinŃele<br />
• Beneficii şi inconveniente<br />
Şablonul Command oferă flexibilitate în mai multe moduri:<br />
- <strong>de</strong>cuplarea sursei evenimentului <strong>de</strong> la obiectul care are cunoştinŃele necesare<br />
pentru a efectua sarcina<br />
- împărŃirea instanŃelor către Command între mai multe obiecte<br />
- permite înlocuirea comenzilor şi receptorilor la rulare<br />
- adăugarea uşoară <strong>de</strong> noi comenzi; se scrie o alta implementare a interfeŃei şi<br />
se adaugă aplicaŃiei<br />
• Variante ale şablonului<br />
Printre variantele şablonului Command se numără:<br />
- Undo - când se extin<strong>de</strong> interfaŃa Command cu o metodă <strong>de</strong> anularea a<br />
comenzii, povara implementării acestei meto<strong>de</strong> revine clasei care implementează<br />
interfaŃa<br />
Pentru a realiza anularea doar a ultimei comenzi, aplicaŃia trebuie să reŃină o<br />
referinŃă doar către ultima comandă. Când se execută o anulare, aplicaŃia apelează<br />
metoda <strong>de</strong> anulare doar a ultimei comenzi. Insă. utilizatorii pot să nu fie satisfăcuŃi<br />
doar cu anularea ultimei comenzi. Pentru a realiza anulări multiple, aplicaŃia trebuie<br />
să păstreze toate comenzile într-un istoric. Pentru a putea anula o comandă, interfaŃa<br />
Command trebuie să salveze toate informaŃiile necesare reparării unui obiect<br />
modificat. Aceste informaŃii includ receptorul şi orice argumente şi valori vechi.<br />
Receptorul trebuie schimbat astfel încât comanda să poată restaura valorile originale.<br />
Comenzile pot fi folosite <strong>de</strong> mai multe ori în contexte diferite. Prin urmare,<br />
comanda trebuie copiată înainte <strong>de</strong> a fi introdusă în istoric. Copierea comenzii previne<br />
erorile ce pot apărea în urma anulării şi executării repetate a ei. Mergând înapoi şi<br />
înainte în istoric nu ar trebui să fie o problemă, dar în cazul unei implementări<br />
incorecte, apar erori. Pentru a preveni asta. comanda ar trebui să reŃină câtă informaŃie<br />
este necesară pentru inversarea acŃiunii. Dacă o parte din informaŃii sunt stocate în<br />
receptor, şablonul Memento este cel mai indicat să reŃină starea receptorului.<br />
26
- MacroCommand - o macrocomandă este o colecŃie <strong>de</strong> alte comenzi.<br />
Macrocomenzile se pot crea folosind şablonul Composite. O macrocomandă conŃine o<br />
listă <strong>de</strong> subcomenzi. Când metoda <strong>de</strong> executare e apelată, macrocomandă trimite<br />
subcomenzi. Dacă macrocomandă suporta anulare, atunci toate comenzile interne<br />
trebuie să suporte anulări<br />
istoric<br />
• Şabloane asociate<br />
Printre şabloanele asociate se află:<br />
- Composite - folosit pentru implementarea macrocomenzilor<br />
- Memento - reŃine starea receptorului în ve<strong>de</strong>rea anularii unei comenzi<br />
- Prototype - poate fi folosit pentru a copia comanda înainte <strong>de</strong> a o plasa în<br />
- Singleton - în multe aplicaŃii, istoricul este implementat ca un Singleton<br />
• Exemplu<br />
Un exemplu al şablonului Command este următoarea clasă ce realizează<br />
începerea şi terminarea unei acŃiuni:<br />
class SubmitAction extends AbstractAction {<br />
private Component target;<br />
public SubmitAction (String name, Icon icon, Component: t){<br />
putValue(Action.NAME, name);<br />
putVaiue(Action.SMALL_ICON, icon);<br />
putValue(Action.SHORT_DESCRIPTION, name + " the<br />
program");<br />
target = t;<br />
public void actionPerformed(ActionEvent evt) {<br />
JOptionPane.showMessageDialog(target, "click pentru<br />
începere") ,-<br />
class ExitAction extends AbstractAction {<br />
private Component target;<br />
public ExitAction(String name, Icon icon, Component t){<br />
putValue(Action.NAME, name); putValue(Action.SMALL<br />
ICON, icon); putValue(Action.SHORT_DESCRIPTION,<br />
name + " the<br />
program"); target - t; } public<br />
void actionPerformed(ActionEvent evt) {<br />
int answer = JOptionPane.showConfirmDialog(target,<br />
"DoriŃi să ieşiŃi? ",<br />
"Confirmare", JOptionPane.YES_NO_OPTION); if<br />
(answer == JOptionPane.YES OPTION) {<br />
System.exit(0);<br />
27
III.3. Interpreter<br />
• ProprietăŃi<br />
• Scop<br />
- Tip: comportamental<br />
- Nivel: clasă<br />
Să <strong>de</strong>finească un interpretor pentru un limbaj.<br />
• Introducere<br />
Cum se rezolvă un puzzle? O persoană înzestrată poate primi cele 5000 <strong>de</strong><br />
piese şi după nişte calcule, ştie un<strong>de</strong> trebuie plasată fiecare piesă. O altă modalitate<br />
este aceea <strong>de</strong> a alege piesele care aparŃin unei părŃi a puzzle-ului, încercând apoi<br />
rezolvarea părŃii mai mici. Se încearcă piesele până când doua dintre ele se potrivesc,<br />
repetându-se apoi proce<strong>de</strong>ul până când o parte mai mica e terminată. Se combină după<br />
aceea părŃile mai mici completându-se tot puzzle-ul.<br />
De multe ori rezolvarea unei probleme se face în acest mod. împărŃindu-se<br />
problema în subprobleme recursiv. Urmează apoi rezolvarea subproblemelor. Dar<br />
când problemele sunt inter<strong>de</strong>pen<strong>de</strong>nŃe, rezolvarea lor este dificilă.<br />
Cea mai bună soluŃie este crearea unui limbaj simplu care să mo<strong>de</strong>leze o<br />
problemă complexă şi să rezolve fraza ce o <strong>de</strong>scrie. Se poate simplifica astfel sarcina<br />
obŃinerii soluŃiei.<br />
• Utilizare<br />
Şablonul Interpreter se foloseşte când:<br />
- trebuie interpretat un limbaj simplu<br />
- probleme recursive pot fi exprimate în acel limbaj<br />
- eficienŃa nu este o problemă importantă<br />
• Descriere<br />
Şablonul Interpreter împarte o problemă în părŃi mai mici, apoi le reuneşte<br />
într-o frază aparŃinând unui limbaj simplu. Se interpretează apoi fraza şi se rezolvă<br />
problema pas cu pas. Acest lucru se face creând un arbore <strong>de</strong> sintaxă abstract.<br />
O exemplu matematic simplu este: rezultat = (a + b) / c; rezultatul <strong>de</strong>pin<strong>de</strong> <strong>de</strong><br />
valorile lui a, b şi c. Presupunând că valorile sunt 4, 2 şi 3. rezultatul va fi 2. Cum se<br />
ştie acest lucru? Mai întâi, i se asociază mental lui a valoarea 4. lui b 2 şi lui c 3. Apoi<br />
se adună a cu b, rezultând valoarea 6 care se împarte apoi la c. Rezolvând problema<br />
cu şablonul Interpreter, se parcurge un set <strong>de</strong> paşi similar. Fiecare din variabile este un<br />
operand, la fel cum sunt şi valorile intermediare. Regulile gramaticale (+ pentru<br />
adunare şi / pentru împărŃire) sunt operaŃii sau operatori. Fiecare regulă gramaticală<br />
este implementată într-o clasă separată şi fiecare valoare din dreapta regulii <strong>de</strong>vine o<br />
variabilă instanŃă.<br />
28
• Implementare<br />
Diagrama <strong>de</strong> clase a şablonului Interpretei" este următoarea:<br />
Client<br />
Context<br />
Şablonul Interpretei" necesită:<br />
interface Exprcssion<br />
+void interpreUContext c)<br />
TerminalExpression NonterminalExpression<br />
+void interpret(Context c) +void interpret(Context c)<br />
- Expression - interfaŃa prin intermediul căreia utilizatorul interacŃionează cu<br />
expresiile<br />
- TerminalExpression - clasa care implementează interfaŃa Expression,<br />
utilizată pentru nodurile terminale ale arborelui <strong>de</strong> sintaxă<br />
- NonterminalExpression - cealaltă implementare a interfeŃei Expression.<br />
utilizată pentru nodurile neterminale ale arborelui <strong>de</strong> sintaxă. ConŃine o referinŃă către<br />
următoarea expresie şi apelează metoda <strong>de</strong> interpretare pentru fiecare dintre copiii săi<br />
- Context - conŃine informaŃiile necesare în mai multe locuri ale<br />
interpretorului. Serveşte drept un canal <strong>de</strong> comunicare între instanŃele mai multor<br />
expresii<br />
- Client - construieşte sau primeşte o instanŃă a unui arbore <strong>de</strong> sintaxă abstract.<br />
Acest arbore <strong>de</strong> sintaxă este compus din instanŃe ale obiectelor <strong>de</strong> tip<br />
TerminalExpressions şi NonterminalExpressions<br />
• Beneficii şi inconveniente<br />
Şablonul Interpreter poate fi uşor modificat pentru a reflecta schimbările<br />
produse în gramatică. Pentru adăugarea unei noi reguli se creează o altă clasă care<br />
implementează interfaŃa Expression. O regulă se poate modifica uşor prin extin<strong>de</strong>rea<br />
clasei vechi şi suprascrierea meto<strong>de</strong>i <strong>de</strong> interpretare.<br />
Şablonul Interpreter nu este potrivit atunci când gramatica este mare. El poate<br />
conduce la un număr mare <strong>de</strong> clase în cazul în care limbajul conŃine multe reguli.<br />
Fiecare regulă adăugata limbajului necesită crearea unei noi clase în interpretor. Acest<br />
lucru poate duce în final la probleme <strong>de</strong> testare şi mentenanŃă.<br />
Se pot adăuga meto<strong>de</strong> interfeŃei Expression pentru a mari funcŃionalitatea<br />
expresiilor. Pentru o mai mare flexibilitate, se foloseşte şablonul Visitor care permite<br />
schimbarea dinamică a meto<strong>de</strong>i <strong>de</strong> interpretare.<br />
29
Crearea arborelui <strong>de</strong> sintaxă abstract poate pune probleme, el nefiind <strong>de</strong>finit <strong>de</strong><br />
şablonul Interpreter. Acesta presupune că arborele a fost creat altun<strong>de</strong>va.<br />
• Şabloane asociate<br />
Printre şabloanele asociate se află:<br />
- Composite - structura pentru interpretarea expresiilor se bazează pe şablonul<br />
Composite, folosind expresii terminale şi neterminale<br />
- Flyweight - pentru reducerea numărului <strong>de</strong> obiecte redundante sau similare<br />
- Iterator - pentru iterarea în cadrul arborelui <strong>de</strong> sintaxă abstract şi a nodurilor<br />
sale<br />
- Visitor - prin folosirea acestui şablon. Interpreter câştigă flexibilitate<br />
• Exemplu<br />
Consi<strong>de</strong>rând o expresie dată. cu ajutorul şablonului Interpreter se poate filtra<br />
informaŃia care se doreşte:<br />
public void myParser() {<br />
StringTokenizer hol<strong>de</strong>r = new StringTokenizer<br />
(expression, token); String[]<br />
toBeMatched = r.ew Strir.g<br />
[hol<strong>de</strong>r.countTokens()];<br />
int idx == 0;<br />
while(hol<strong>de</strong>r.hasMoreTokens () ) {<br />
String item = hol<strong>de</strong>r.nextToken(); int start =<br />
item.in<strong>de</strong>xOf(","); if (start=--C) { iten = item.<br />
substring ( 2 ) ; } toBeMatched[idx] = item; idx +4;<br />
}<br />
result = Arrays.asList(toBeMatched); }<br />
public List getParseResult() { return result; }<br />
public void interpret() {<br />
StringBuffer buffer = new StringBuffer();<br />
Listlterator iist = result.listlterator();<br />
while(Iist.hasNext()){<br />
Stri.ng tcken = (String)1 ist.next(); if<br />
(token.equais("SFO")) {<br />
token = "San Francisco";<br />
}else if(token.equais("CA")) {<br />
token = "Canada"; }<br />
buffer.append(" " ^ token); }<br />
interpreted = buffer.toString();<br />
}<br />
public String getInterpretedResult() {<br />
return interoreted;<br />
30
III.4. Iterator<br />
• Cunoscut şi ca Cursor<br />
• ProprietăŃi<br />
• Scop<br />
- Tip: comportamental -<br />
Nivel: componentă<br />
Să pună la dispoziŃie o cale <strong>de</strong> acces secvenŃial la articolele dintr-o colecŃie<br />
care este in<strong>de</strong>pen<strong>de</strong>ntă sau separată <strong>de</strong> colecŃia <strong>de</strong> bază.<br />
• Introducere<br />
Managerul personal <strong>de</strong> informaŃii utilizează multe colecŃii <strong>de</strong> vreme ce Ńine<br />
evi<strong>de</strong>nŃa unei cantităŃi mari <strong>de</strong> date. Adrese, contacte, proiecte, întâlniri, notiŃe, liste<br />
cu lucruri ce trebuie făcute - toate aceste presupun abilitatea <strong>de</strong> a se reŃine grupuri <strong>de</strong><br />
obiecte legate între ele. Pentru a se putea salva toate aceste tipuri <strong>de</strong> informaŃii, se pot<br />
crea clase care sa reŃină fiecare grup <strong>de</strong> articole în parte. în acest fel. se pot <strong>de</strong>zvolta<br />
colecŃii care să în<strong>de</strong>plinească nevoile specifice fiecărui grup <strong>de</strong> obiecte.<br />
Apare însă o problemă atunci când se doreşte parcurgerea fiecărei colecŃii.<br />
Dacă se creează clase care să în<strong>de</strong>plinească nevoile obiectelor stocate, nu există nici o<br />
garanŃie că elementele pot fi retrase şi folosite într-o modalitate uniformă. Întâlnirile<br />
pot fi organizate în subgrupuri în funcŃie <strong>de</strong> date. în timp ce contactele pot fi stocate<br />
alfabetic iar notiŃele pot fi ordonate secvenŃial.<br />
Acest lucru conduce la scrierea <strong>de</strong> cod specific fiecărei colecŃii în parte pentru<br />
a se putea naviga printre articolele fiecărui grup. precum şi copierea codului respectiv<br />
în fiecare parte a sistemului un<strong>de</strong> se foloseşte un grup. Rezultă un cod foarte<br />
complicat şi greu <strong>de</strong> întreŃinut. Mai mult. trebuie cunoscute fiecare tipuri <strong>de</strong> colecŃii<br />
folosite în <strong>de</strong>taliu pentru a menŃine obiectele în managerul personal.<br />
Şablonul Iterator rezolvă aceste probleme prin <strong>de</strong>finirea unei interfeŃe<br />
uniforme pentru parcurgerea oricărei colecŃii. Când se folosesc iteratori în sistem, se<br />
pot utiliza aceleaşi meto<strong>de</strong> <strong>de</strong> apel atunci când se navighează printr-o listă <strong>de</strong><br />
contacte, precum şi atunci când se printează o listă cu lucrurile ce trebuie făcute.<br />
• Utilizare<br />
Şablonul Iterator se foloseşte pentru:<br />
- a pune la dispoziŃie o cale uniformă şi consistentă <strong>de</strong> a parcurge elementele<br />
unei colecŃii care nu este legată <strong>de</strong> implementarea colecŃiei<br />
- a permite parcurgerea colecŃiilor multiple, lăsând mai mulŃi clienŃi să<br />
navigheze simultan în cadrul aceleaşi colecŃii <strong>de</strong> bază
■ Descriere<br />
Şablonul Iterator permite standardizarea şi simplificarea codului ce trebuie<br />
scris pentru parcurgerea unei colecŃii. Clasele ce implementează colecŃiile tind să fie<br />
create pe baza a ceea ce se stochează şi nu pe baza cerinŃelor <strong>de</strong> parcurgere. Avantajul<br />
oferit <strong>de</strong> Iterator este acela că prezintă o cale consistentă <strong>de</strong> a naviga în cadrul<br />
colecŃiilor indiferent <strong>de</strong> structura <strong>de</strong> bază.<br />
Un Iterator în Java foloseşte <strong>de</strong> regulă o interfaŃă care <strong>de</strong>fineşte operaŃiile lui<br />
principale, punând apoi la dispoziŃie una sau mai multe implementări care fac legătura<br />
cu colecŃia <strong>de</strong> bază. OperaŃiile fundamentale ale Iterator-ului sunt:<br />
- First<br />
-Next<br />
- IsDone<br />
- Currentltem<br />
Aceste operaŃii <strong>de</strong>finesc serviciile <strong>de</strong> bază pe care un Iterator le oferă. în<br />
termeni mai generali, un Iterator ar trebui să ofere următoarele capacităŃi:<br />
- Navigation - mişcarea în faŃă şi în spate în cadrul colecŃiei<br />
- Retrieval - obŃinerea elementului curent la care se face referinŃă<br />
- Validation - <strong>de</strong>ci<strong>de</strong>rea dacă mai sunt elemente în colecŃie, Ńinându-se cont <strong>de</strong><br />
poziŃia curentă a Iterator-ului<br />
Iteratorii pot conŃine şi operaŃii extinse, cum ar fi meto<strong>de</strong> <strong>de</strong> mişcare către<br />
primul sau ultimul element al colecŃiei.<br />
• Implementare<br />
Diagrama <strong>de</strong> clase a şablonului Iterator este următoarea:<br />
interface Iterator<br />
+Object fir st ()<br />
+ Objecl nexJf)<br />
+boolean hasMoreElement'()<br />
+Object getCurrentElement'()<br />
Concretelterator<br />
+Object first()<br />
+Object next()<br />
+boolean hasMoreElement()<br />
+Object getCurrentElement()<br />
interface Aggregate<br />
+Iterator gel'Iterator•()<br />
ConereteAggregate<br />
+Iterator getlteratorQ<br />
return new ConcretelteratorQ<br />
32
Pentru implementarea şablonului Iterator este nevoie <strong>de</strong>:<br />
- Iterator - interfaŃa care <strong>de</strong>fineşte meto<strong>de</strong>le standard ale Iterator-ului. Minim,<br />
ea trebuie să conŃină meto<strong>de</strong> pentru parcurgerea, obŃinerea şi validarea elementelor<br />
(first, next, hasMoreElements şi getCurrentltem)<br />
- Concretelterator - clase care implementează interfaŃa Iterator. Aceste clase<br />
conŃin o referinŃă către colecŃia <strong>de</strong> bază. în mod normal, instanŃele sunt create <strong>de</strong> clasa<br />
ConcreteAggregate. Deoarece există o strânsa legătură între ConcreteAggregate şi<br />
Concretelterator, aceasta din urmă face <strong>de</strong> obicei parte din prima<br />
- Aggregate - interfaŃa ce <strong>de</strong>fineşte o metodă pentru producerea Iterator-ului<br />
- ConcreteAggregate - clasa ce implementează interfaŃa Aggregate, construind<br />
la comanda un obiect <strong>de</strong> tip Concretelterator. Ea execută acŃiuni în funcŃie <strong>de</strong><br />
responsabilitatea fundamentală a sa <strong>de</strong> reprezentare a colecŃiilor <strong>de</strong> obiecte din sistem<br />
• Beneficii şi inconveniente<br />
Multe din beneficiile şablonului Iterator sunt date <strong>de</strong> avantajele <strong>de</strong>finirii unei<br />
interfeŃe uniforme pentru parcurgerea colecŃiilor. Se simplifică astfel consi<strong>de</strong>rabil<br />
utilizarea colecŃiilor şi se permite folosirea polimorfismului în lucrul cu colecŃiile.<br />
Pentru printarea elementelor dintr-o colecŃie, <strong>de</strong> exemplu, se poate folosi un Iterator,<br />
apelându-se metoda toString pentru orice obiect, indiferent <strong>de</strong> colecŃia <strong>de</strong> bază.<br />
în plus, Iteratorii permit clienŃilor navigarea multiplă în cadrul aceleiaşi<br />
colecŃii. Iterator-ul poate fi privit ca un cursor în interiorul colecŃiei.<br />
Un inconvenient al acestui şablon este acela că oferă iluzia <strong>de</strong> ordine în<br />
structurile neordonate. De exemplu, pentru un set ce nu suportă ordonare, Iterator-ul<br />
va pune la dispoziŃie elementele într-o secvenŃă arbitrară care se poate schimba <strong>de</strong>-a<br />
lungul timpului. Dacă nu se observă acest lucru, se poate scrie un cod pentru<br />
asigurarea consistenŃei în structura <strong>de</strong> bază, apărând astfel probleme.<br />
• Variante ale şablonului<br />
Şablonul Iterator prezintă mai multe posibilităŃi <strong>de</strong> implementare. Obiectele <strong>de</strong><br />
tip Concretelterator pot fi interne sau externe. Iteratorii externi au meto<strong>de</strong> specifice<br />
pentru navigarea în cadrul colecŃiei, în timp ce Iteratorii interni permit navigarea în<br />
ciclu printre elemente.<br />
Obiectele <strong>de</strong> tip Concretelterator pot fi dinamice sau statice. Un obiect<br />
dinamic face o referire directă către colecŃia <strong>de</strong> bază, <strong>de</strong>ci va reflecta mereu starea<br />
acesteia. Pe <strong>de</strong> alta parte, un obiect static creează un instantaneu al colecŃiei,<br />
referindu-se la această copie pe durata utilizării.<br />
Iteratorii nuli pot fi <strong>de</strong>finiŃi pentru parcurgerea structurilor complexe, cum ar fi<br />
arborii. Folosind un iterator ce reprezintă un "nod final", este posibilă scrierea unui<br />
cod recursiv simplu pentru vizitarea tuturor nodurilor arborelui.<br />
• Şabloane asociate<br />
Printre şabloanele asociate se află:<br />
- Factory Method - clasele ce implementează colecŃiile conŃin <strong>de</strong> regulă o<br />
metodă <strong>de</strong> producere a Iterator-ului<br />
33
- Visitor - atunci când şablonul Yisitor este folosit pentru un grup <strong>de</strong> obiecte,<br />
un Itcrator este <strong>de</strong> asemenea folosit pentru parcurgerea în ciclu a elementelor<br />
- Value List Handler - acest şablon se bazează pe Iterator în sensul că permite<br />
clientului să sară peste o colecŃie<br />
• Exemplu<br />
Avem interfaŃa Emplovee şi clasele Manager şi Worker care o implementează.<br />
Clasa EmployeeTcst va crea o listă şi va folosi un iterator pentru a o parcurge:<br />
interface Employee { public abstracr. double earr.ings ( ) ; )<br />
class Manager inpîements Err.ployee { private dcuble<br />
weeklySalary; private String name; public Manager (String<br />
narne, double s) {<br />
this.nane = name; setWeeklySalary ( s) ;<br />
} void setWeeklySalary(double s) {<br />
if (s > 0) { weeklySalary = s; }<br />
else weeklySalary = 0;<br />
}<br />
public double earnings() { return weeklySaiary; }<br />
public String get Name () { return narr.e; }<br />
public Strinq toStringO {return "Manager:" + getName();}<br />
}<br />
class Worker impienents Employee { private doubie<br />
wagePerPiece; private int quantity; private<br />
String name; public Worker (Strmg narne, douoi e<br />
w, mt q) {<br />
this.name = name;<br />
setWagePerPiece Ńw) ;<br />
setQuantity(q);<br />
i<br />
void setWagePerPiece(double w) {<br />
if (w > 0) wagePerPiece = w;<br />
else wagePerPiece = 0;<br />
}<br />
void setQuantity(int q) {<br />
if ( q > 0) quantity = q; else<br />
quantity = 0; }<br />
public String getName() { return name; }<br />
public double earnings() {return quantity*wagePerPiece;} public<br />
String toString() {return "Worker:" + getName(); } ) class<br />
EmployeeTest {<br />
public static void main(String[] args) {<br />
iava.util.List list = new ArrayList();<br />
list.add(new Manager("Bill", 800.00));<br />
list.add(new Worker("Al", 2.5, 200));<br />
list.add{new Manager("Peter", 1200.00));<br />
list.add(new Worker("Mark", 4.5, 333));<br />
Iterator iterator = list.iterator();<br />
while(iterator.hasNext()) {<br />
Employee em == (Employee ) iterator . next ();<br />
System.out.prinŃ(ei + " câştigă $"); System,<br />
out .println (ora. earnings ( ) } ;<br />
34
III.5. Mediator<br />
• ProprietăŃi<br />
• Scop<br />
- Tip: comportamental<br />
- Nivel: componentă<br />
Să simplifice comunicarea între obiectele unui sistem prin introducerea unui<br />
singur obiect care să se ocupe <strong>de</strong> transmiterea mesajelor între celelalte.<br />
• Introducere<br />
O caracteristică folositoare a managerului personal <strong>de</strong> informaŃii este aceea <strong>de</strong><br />
distribuire a informaŃiilor între mai mulŃi utilizatori, astfel încât cineva să poată aranja<br />
o întâlnire la care un alt utilizator să participe. în acest fel, toŃi participanŃii vor fi<br />
ŃinuŃi la curent cu planurile <strong>de</strong> întâlniri.<br />
Cum se pot însă administra întâlnirile, presupunând că mai multe managere <strong>de</strong><br />
informaŃii rulează? O modalitate este <strong>de</strong> a da fiecăruia o copie a unui obiect <strong>de</strong> tip<br />
Appointment, asigurându-se că ele au acces local la date. Apare o problemă: cum se<br />
menŃine informaŃia consistentă între toŃi utilizatorii? De exemplu, dacă un utilizator<br />
creează o întâlnire iar mai târziu îi schimbă data. cum află ceilalŃi participanŃi la<br />
întâlnire acest lucru? Se poate face aplicaŃia utilizatorului responsabilă şi cu<br />
administrarea modificărilor, Totuşi, dacă unuia dintre participanŃii la întâlnire i se<br />
permite să facă modificări, asta înseamnă că fiecare manager <strong>de</strong> informaŃii trebuie să<br />
urmărească activitatea celorlalte managere. Administrarea comunicării între un număr<br />
mare <strong>de</strong> participanŃi <strong>de</strong>vine foarte dificilă. în cel mai bun caz, este ineficient şi<br />
costisitor în ceea ce priveşte reŃeaua: în cel mai rău caz. planificarea unei întâlniri se<br />
transformă în haos.<br />
Având în ve<strong>de</strong>re posibila complexitate a sistemului, este preferabilă<br />
trimiterea/primirea cerinŃelor către/<strong>de</strong> la un obiect central care să ia <strong>de</strong>cizii privind<br />
meto<strong>de</strong>le <strong>de</strong> apelare. Aceasta este esenŃa şablonului Mediator. în loc <strong>de</strong> a face<br />
obiectul <strong>de</strong> tip Appointment responsabil <strong>de</strong> trimiterea modificărilor, se creează un<br />
obiect AppointmenfMediator. De fiecare dată când obiectul Appointment se modifică,<br />
se apelează o metoda a Mediator-ului care poate <strong>de</strong>ci<strong>de</strong> apelarea unor meto<strong>de</strong> pentru a<br />
confirma modificările. în funcŃie <strong>de</strong> rezultat, obiectul AppointmentManager transmite<br />
mesajul original sau o versiune revizuită a lui care conŃine datele privind modificarea<br />
orei întâlnirii sau anularea acesteia.<br />
Utilizare<br />
Şablonul Mediator se foloseşte când:<br />
- există reguli complexe ale comunicării între obiectele sistemului<br />
- se doreşte ca obiectele să fie păstrate simple şi uşor <strong>de</strong> administrat<br />
35
• Descriere<br />
Pe măsură ce comunicarea între obiectele unei aplicaŃii <strong>de</strong>vine din ce în ce mai<br />
complexă, administrarea acesteia este mult mai dificilă. Manipularea evenimentelor<br />
pentru controlul unei simple foi <strong>de</strong> calcul tabelar poate implica scrierea unui cod<br />
pentru fiecare componentă reŃea. Totuşi, dacă interfaŃa grafică cu utilizatorul este<br />
estinsă pentru a inclu<strong>de</strong> o reŃea, un grafic şi pentru a înregistra câmpurile afişate, ea<br />
<strong>de</strong>vine mult mai dificilă în ceea ce priveşte administrarea codului. O schimbarea a<br />
uneia dintre componente poate produce modificări în alte componente.<br />
Şablonul Mediator permite rezolvarea acestei probleme, <strong>de</strong>finind o clasă a<br />
cărei responsabilităŃi este comunicarea în general. Se simplifică mult celelalte clase<br />
ale sistemului, nemaifiind necesară administrarea comunicărilor propriu-zise.<br />
Obiectul mediator are rolul unui drum al sistemului, centralizând logica privind<br />
trimiterea şi primirea mesajelor. Componentele trimit mesaje către mediator şi se<br />
bazează pe acestea să le trimită notificări ale modificărilor apărute. Implementarea<br />
Mediator-ului se face atunci când interfaŃa grafică cu utilizatorul trebuie să se<br />
comporte ca un întreg.<br />
Pentru a ilustra utilitatea reală a şablonului Mediator, se pot consi<strong>de</strong>ra<br />
conferinŃele telefonice. Multe companii <strong>de</strong> telefonic oferă teleconferinŃe iar interfaŃa<br />
<strong>de</strong> comutare a interlocutorilor este un fel <strong>de</strong> mediator. Ea permite transmiterea<br />
mesajelor între indivizii care participa la conferinŃa.<br />
• Implementare<br />
Diagrama <strong>de</strong> clase a şablonului Mediator este următoarea:<br />
interface Mediator interface Client<br />
+void broadcastEventf)<br />
+void broadcastEventQ<br />
-Client[] clients<br />
ConcreteMediator<br />
pentru toŃi clienŃii c c.handleEventQ<br />
Implementarea şablonului Mediator necesită:<br />
+void handleEventf)<br />
+void<br />
broadcastEventf)<br />
+void<br />
ConcreteClient<br />
-Mediator mediator<br />
handleEventQ +void<br />
broadcastEventQ<br />
med iator.broadcastEvent()<br />
- Mediator - interfaŃa care <strong>de</strong>fineşte meto<strong>de</strong>le pe care clienŃii le pot apela<br />
36
- ConcrcteMediator - clasa care implementează interfaŃa Mediator. Această<br />
clasă reprezintă un mediator între mai multe clase <strong>de</strong> clienŃi. ConŃine informaŃiile<br />
<strong>de</strong>spre procese, precum şi referinŃe către clienŃii săi. Pe baza informaŃiilor pe care le<br />
primeşte Mediator-ul, ea poate fie invoca meto<strong>de</strong>le specifice, fie o metodă generică<br />
pentru a informa clienŃii <strong>de</strong> modificările survenite, fie o combinaŃie a acestora<br />
- Client - interfaŃa care <strong>de</strong>fineşte meto<strong>de</strong>le generale pe care un Mediator le<br />
poate folosi pentru a crea instanŃe ale clienŃilor<br />
- ConcreteClient - clasa care implementează interfaŃa Client şi toate meto<strong>de</strong>le<br />
client. Ha poate păstra o referinŃă către o instanŃă Mediator pentru a informa clienŃii<br />
când apare o schimbare<br />
• Beneficii şi inconveniente<br />
Componentele individuale <strong>de</strong>vin mai simple şi mai uşor <strong>de</strong> utilizat, <strong>de</strong>oarece<br />
ele nu mai sunt nevoite sa trimită mesaje direct între ele. Strategiile <strong>de</strong> comunicarea<br />
generală <strong>de</strong>vin mai uşor <strong>de</strong> administrat, ele fiind acum responsabilitatea exclusivă a<br />
mediator-ului. Clasele unei aplicaŃii pot fi folosite şi în cadrul aplicaŃiei, fiind<br />
necesară doar rescrierea Mediator-ului pentru noua aplicaŃie.<br />
Testarea unor implementări complexe ale unui Mediator poate fi <strong>de</strong>stul <strong>de</strong><br />
dificilă, <strong>de</strong>oarece trebuie testată o componentă care constă din Mediator şi obiectele<br />
asociate lui. Codul Mediator-ului poate <strong>de</strong>veni greu <strong>de</strong> administrat pe măsura ce<br />
numărul şi complexitatea participanŃilor creşte. O posibilă soluŃie este <strong>de</strong> a transforma<br />
mediatorul într-o structură compusă, bazată pe un număr <strong>de</strong> mediatori foarte<br />
focalizaŃi. în acest caz. Mediator-ul e format dintr-un obiect central <strong>de</strong> administrare<br />
asociat unui număr <strong>de</strong> clase individuale, fiecare punând la dispoziŃie o parte <strong>de</strong><br />
funcŃionalitate.<br />
■ Variante ale şablonului<br />
- UnidirecŃional Communication - unele implementări permit doar trimiterea<br />
sau doar primirea <strong>de</strong> clienŃi în sistem<br />
- Threading - ca multe alte şabloane comportamentale. Mediator-ul foloseşte<br />
lucrul cu multiple fire <strong>de</strong> execuŃie<br />
- Configurable Roles - în această variantă, clientul <strong>de</strong>fineşte un rol, care se<br />
poate modifica pe măsură ce sistemul rulează, rol ce <strong>de</strong>fineşte cerinŃele trimiterii <strong>de</strong><br />
mesaje. Deja cu o implementare complexă, <strong>de</strong>finirea participanŃilor ca roluri poate<br />
conduce la un Mediator mai generic<br />
- Client Puii - un Mediator poate stoca mesaje <strong>de</strong>taliate şi trimite doar o<br />
notificare generală către clienŃi. ClienŃii pot cere apoi informaŃii <strong>de</strong>taliate <strong>de</strong>spre un<br />
eveniment dacă este cazul<br />
• Şabloane asociate<br />
Printre şabloanele asociate se află:<br />
- Observer - acest şablon este <strong>de</strong> obicei folosit pentru a administra<br />
comunicarea dintre Client şi Mediator, atunci când aceasta este locală<br />
- Half-Object Plus Protocol - şablonul Mediator ce rulează <strong>de</strong>-a lungul unei<br />
reŃele poate folosi şablonul Half-Object Plus Protocol care conŃine suport pentru<br />
comunicare<br />
37
Exemplu<br />
Având o interfaŃă grafică complexă, <strong>de</strong> fiecare dată când se apasă pe un buton<br />
o acŃiune este pornită sau oprită. Se poate <strong>de</strong>scrie o clasă Mediator care să conŃină<br />
toate clasele asociate:<br />
class Mediator {<br />
BtnView btnView;<br />
BtnSearch btnSearch;<br />
Btr.Book btnBook;<br />
LbIDisplay show;;<br />
void registerView (5tr.Vi.ew v) { btnView = v; }<br />
void registerSearch(BtnSearch s) { btnSearch = s; }<br />
void registerBook(BtnBook b) { btnBook = b; }<br />
void registerDisplay(LbIDisplay d) { show = d; }<br />
veid book() {<br />
btnBooK.setEnabled(false);<br />
btnView.setEnabled(true);<br />
btnSearch.setEnabled(true);<br />
show.setText("booking...");<br />
btnView.setEnabled(false);<br />
btnSearch.setEnabled (true) ;<br />
btnBook.setEnabled(true);<br />
void search() {<br />
btnSearch.setEnabled ( false);<br />
btnView.setEnabled(true);<br />
btnBook.setEnabled(true);<br />
show.setText("searching...");<br />
III.6. Memento<br />
• Cunoscut şi ca Token, Snapshot<br />
• ProprietăŃi<br />
• Scop<br />
- Tip: comportamental<br />
- Nivel: obiect<br />
Să păstreze un instantaneu al stării unui obiect, astfel încât obiectul să poate<br />
reveni la starea originală fără a-şi <strong>de</strong>zvălui conŃinutul restului lumii.<br />
• Introducere<br />
Fiecare aplicaŃie are obiecte ale căror informaŃii trebuie păstrate şi după<br />
terminarea duratei <strong>de</strong> viaŃă a obiectelor. De regulă, acest lucru se face prin distribuirea<br />
datelor, dar dacă informaŃiile private ale unei obiect trebuie păstrate? Trimiterea <strong>de</strong><br />
date către un alt obiect este o i<strong>de</strong>e rea. încălcând principiul încapsulării. Dacă se trimit<br />
date către alte obiecte, acestea pot citi sau, mai rău. pot modifica datele.<br />
38
Este asemănător cu mersul la un parc naŃional un<strong>de</strong> se protejează elanul.<br />
Obiectul ale cărui date sunt salvate este elanul. Xu este permisă luarea acasă a<br />
elanului, dar ve<strong>de</strong>ri şi tricouri cu elanul sunt disponibile la magazinul <strong>de</strong> suveniruri al<br />
parcului.<br />
O mai bună abordare este folosirea unui obiect care să conŃină datele ce<br />
trebuie salvate. Se trimite acest obiect care poate fi folosit pentru recrearea<br />
originalului, în loc <strong>de</strong> a trimite datele neprelucrate. Alte obiecte nu ar putea citi sau<br />
modifica datele <strong>de</strong>oarece acestea ar fi încapsulate. Acesta este şablonul Memento,<br />
un<strong>de</strong> un obiect este folosit ca un suvenir cu ajutorul căruia se poate restabili starea<br />
originală a obiectului iniŃial.<br />
• Utilizare<br />
Şablonul Memento se foloseşte când:<br />
- este nevoie reŃinerea unui instantaneu al stării obiectului<br />
- acest instantaneu e utilizat pentru recrearea stării originale a obiectului<br />
- o interfaŃă directă către obiect încalcă încapsularea<br />
• Descriere<br />
Dacă încapsularea a fost implementată corect, atunci toate obiectele au stări<br />
private şi vor permite acces la atribute doar prin meto<strong>de</strong>. Dar poate fi necesară<br />
trimiterea stării curente către un alt obiect, <strong>de</strong> exemplu, în ve<strong>de</strong>rea restaurării stării<br />
iniŃiale într-un moment ulterior. O posibilitate <strong>de</strong> a face asta este <strong>de</strong> a trimite starea<br />
direct către obiectul interesat. Apar însă inconveniente, cum ar fi expunerea structurii<br />
interne a obiectului sau posibilitatea modificării stării obiectului <strong>de</strong> către un altul.<br />
SoluŃia este învăluirea stării care se doreşte păstrată într-un alt obiect folosind<br />
şablonul Memento. Memento este un obiect care conŃine starea internă curentă a<br />
obiectului original <strong>de</strong> tip Originator. doar acesta din urma putând accesa şi retrage<br />
informaŃii din Memento. Pentru restul lumii, Memento reprezintă doar un obiect<br />
arbitrar.<br />
■ Implementare<br />
Diagrama <strong>de</strong> clase a şablonului Memento este următoarea:<br />
Originator<br />
-State state<br />
+void setMemento(Memento m)<br />
+Object getMemento()<br />
1 creează<br />
t<br />
Memento<br />
StateHol<strong>de</strong>r<br />
-State state Memento este o clasa statica<br />
interna a Originator-ului<br />
+State getState() +void<br />
setState(State s)<br />
39
Pentru implementarea şablonului Memento este nevoie <strong>de</strong>:<br />
- Originator - creează Memento, lblosindu-1 ulterior pentru a-şi restaura starea<br />
- Memento - clasa statică a Originator-ului şi păstrătorul stării acestuia.<br />
Originator-ul <strong>de</strong>termină câte date sunt păstrate <strong>de</strong> Memento. Starea reŃinută <strong>de</strong><br />
Memento trebuie să fie inaccesibilă tuturor, cu excepŃia Originator-ului<br />
- StateHol<strong>de</strong>r - obiectul care doreşte să păstreze starea. El nu trebuie să ştie ce<br />
se afla în interiorul obiectului Memento: trebuie doar să ştie că obiectul pe care-1<br />
primeşte îi permite să restaureze starea Originator-ului<br />
Deoarece Memento trebuie să fie accesibil doar Originator-ului, este preferabil<br />
ca Memento să fie o clasă publică din interiorul Originator-ului. Toate meto<strong>de</strong>le sunt<br />
<strong>de</strong>clarate private astfel că sunt disponibile doar Memento-ului şi clasei care-1 conŃine.<br />
InstanŃe ale unei clase interne sunt mereu asociate cu cele are clasei externe. Acest<br />
lucru este necesar <strong>de</strong>oarece o clasă internă are întot<strong>de</strong>auna acces la instanŃe ale<br />
variabilelor clasei externe. Acest lucru cauzează însă o problemă în această situaŃie.<br />
Memento ar trebui să fie in<strong>de</strong>pen<strong>de</strong>nt <strong>de</strong> o instanŃă specifică a Originator-ului. Prin<br />
urmare clasa Memento trebuie să fie statică.<br />
■ Beneficii şi inconveniente<br />
Folosirea şablonului Memento are următoarele consecinŃe:<br />
- respectă principiul încapsulării - chiar şi când starea Originator-ului trebuie<br />
să fie stocată în afara obiectului <strong>de</strong> tip Originator într-un client, starea este<br />
inaccesibilă clientului. Are doar o referinŃă către obiectul Memento, neexistând nici o<br />
cale <strong>de</strong> a accesa informaŃia din interior. Totodată face treaba clientul mai simplă<br />
<strong>de</strong>oarece el nu mai trebuie să ştie totul <strong>de</strong>spre lucrul intern al Originator-ului,<br />
exceptând modul <strong>de</strong> obŃinere şi <strong>de</strong> utilizare a Memento-ului<br />
- un Originator simplu - presupunând că Originator-ul trebuie să urmărească<br />
toate stările, el va <strong>de</strong>veni curând plin şi greu <strong>de</strong> mânuit. Este mult mai uşor dacă se dă<br />
această responsabilitate clientului. Originator-ul trebuie acum doar să fie capabil să<br />
creeze şi să utilizeze Memento-ul<br />
- Memento-uri costisitoare - Memento-urile sunt costisitor <strong>de</strong> creat daca<br />
fiecare bucată a stării Originator-ului trebuie stocată în Memento. Daca Originator-ul<br />
este mare. şablonul Memento nu este unul potrivit<br />
- stocare costisitoare a Memento-ului - obiectul care păstrează starea este<br />
responsabil cu administrarea ciclului <strong>de</strong> viaŃă după ce primeşte Memento-ul <strong>de</strong> la<br />
Originator. Totuşi, el nu ştie cât <strong>de</strong> mare este Memento-ul <strong>de</strong> fapt. Daca el nu este<br />
păstrat cât <strong>de</strong> mic posibil, obiectul StateHol<strong>de</strong>r va avea <strong>de</strong> plătit preŃul<br />
• Variante ale şablonului<br />
Dacă Memento-ul trebuie să fie o clasă <strong>de</strong> sine stătătoare şi nu o clasă internă,<br />
trebuie <strong>de</strong>finite două interfeŃe - Wi<strong>de</strong>Memento şi NarrowMemento. InterfaŃa largă<br />
este pentru Originator-ul Memento-ului astfel încât el să poată accesa Memento-ul<br />
pentru a-si extrage starea. InterfaŃa restrânsă este pentru obiectele StateHol<strong>de</strong>r şi<br />
pentru alŃi clienŃi folosiŃi.<br />
40
• Şabloane asociate<br />
Printre şabloanele asociate se află:<br />
- Command - poate folosi Memento-uri pentru a urmări stările în ve<strong>de</strong>rea<br />
anulării unor acŃiuni<br />
- State<br />
• Exemplu<br />
Şablonul Memento poate 11 folosit, <strong>de</strong> exemplu, pentru a reŃine un număr:<br />
class Memento { ir.t num;<br />
Memento(int c) {<br />
num = c; } int<br />
getNum() {<br />
return num;<br />
x<br />
III.7. State<br />
• Cunoscut şi ca Objects For States<br />
• ProprietăŃi<br />
• Scop<br />
- Tip: comportamental<br />
- Nivel: obiect<br />
Să schimbe uşor comportamentul unui obiect la rulare.<br />
• Introducere<br />
De multe ori, o aplicaŃie se comportă diferit în funcŃie <strong>de</strong> valorile variabilelor<br />
ei interne. De exemplu, când se lucrează cu un fişier text, acesta trebuie salvat din<br />
când în când. Multe dintre editoarele <strong>de</strong> text curente permit salvarea documentului<br />
doar când acesta s-a modificat. Imediat ce textul a fost salvat, el este consi<strong>de</strong>rat<br />
"curat"; conŃinutul fişierului este i<strong>de</strong>ntic cu cel afişat pe ecran.<br />
Implementarea acestui lucru într-o metodă individuală face codul greu <strong>de</strong><br />
menŃinut şi <strong>de</strong> citit. Aceste meto<strong>de</strong> vor conŃine structuri if/else. O tactică ar fi<br />
reŃinerea stării unui obiect într-o singură variabilă folosind constante pentru o valoare,<br />
caz în care meto<strong>de</strong>le vor conŃine structuri switch/case care vor fi asemănătoare în<br />
fiecare metodă.<br />
Obiectele reprezintă stare şi comportament; starea este păstrată în atributele lor<br />
iar comportamentul este <strong>de</strong>finit în meto<strong>de</strong>. Şablonul State permite schimbarea<br />
dinamică a comportamentului unui obiect. Acest comportament dinamic este obŃinut<br />
prin <strong>de</strong>legarea tuturor apelurilor <strong>de</strong> meto<strong>de</strong> care se bazează pe anumite valori către un<br />
41
obiect <strong>de</strong> tip State. Acest obiect este la rândul l u i comportamental, prin urmare<br />
schimbarea unor obiecte State duce la obŃinerea unui comportament diferit. Meto<strong>de</strong>le<br />
specifice din clasele State nu mai trebuie sa folosească structuri if/else sau<br />
switch/case; obiectul State <strong>de</strong>fineşte comportamentul pentru o singură stare.<br />
■ Utilizare<br />
Şablonul State se foloseşte când:<br />
- comportamentul obiectului <strong>de</strong>pin<strong>de</strong> <strong>de</strong> starea sa. aceasta schimbându-se<br />
frecvent<br />
- meto<strong>de</strong>le conŃin structuri condiŃionale mari care <strong>de</strong>pind <strong>de</strong> starea obiectului<br />
■ Descriere<br />
Obiectele care au un comportament diferit în funcŃie <strong>de</strong> starea lor curentă pot<br />
fi greu <strong>de</strong> implementat fără şablonul State. Implementarea fără acest şablon presupune<br />
folosirea <strong>de</strong> constante pentru urmărirea stării curentă şi implementarea unor structuri<br />
switch lungi. Majoritatea acestor meto<strong>de</strong> aflate în aceeaşi clasă au o structură similară.<br />
Consi<strong>de</strong>rând o uşa, care sunt operaŃiile normale care se pot face cu o simplă<br />
uşă? Aceasta poate fi <strong>de</strong>schisă şi închisă, <strong>de</strong>ci ea se poate afla în una din cele două<br />
stări - închis sau <strong>de</strong>schis. Apelarea meto<strong>de</strong>i <strong>de</strong> închi<strong>de</strong>re a unei uşi închise nu duce la<br />
nimic, dar apelarea acesteia pentru o uşa <strong>de</strong>schisă conduce la schimbarea stării uşii.<br />
• Implementare<br />
Diagrama <strong>de</strong> clase a şablonului State este următoarea:<br />
Context<br />
-State currentState<br />
+void setCurrentState(State s)<br />
i k<br />
i<br />
k T<br />
1 1 1 1<br />
__ L<br />
_<br />
interface State<br />
+void someMethod()<br />
------------- * ----------------<br />
ConcreteStateA ConcreteStateB<br />
+void someMethod()<br />
Implementarea şablonului State necesită:<br />
+void someMethodQ<br />
- Context - menŃine o referinŃă către starea curentă, fiind interfaŃa utilizată <strong>de</strong><br />
clienŃi<br />
- State - <strong>de</strong>fineşte meto<strong>de</strong>le care <strong>de</strong>pind <strong>de</strong> starea obiectului<br />
- ConcreteState - clasa care implementează interfaŃa State şi comportamentul<br />
specific unei stări<br />
42
Context sau ConcreteState pot <strong>de</strong>termina tranziŃiile între stări. Când numărul<br />
stărilor este fixat, cel mai apropiat loc în care se poate reŃine logica tranziŃiei este<br />
Context, lotuşi, se obŃine o flexibilitate mai mare dacă aceasta se plasează în<br />
subclasele clasei State. în acest caz. fiecare obiect State <strong>de</strong>termină tranziŃia - care este<br />
următoarea stare. în ce circumstanŃe şi când are loc tranziŃia. Acest lucru face mai<br />
uşoară schimbarea unei părŃi a obiectului State şi adăugarea unuia nou. Inconvenientul<br />
este că fiecare clasă care implementează clasa State <strong>de</strong>pin<strong>de</strong> <strong>de</strong> alte clase. Dacă<br />
implementarea clasei State <strong>de</strong>termină tranziŃia, interfaŃa Context trebuie să conŃină o<br />
calc pentru introducerea noii stări în context.<br />
• Beneficii şi inconveniente<br />
Printre beneficiile şi inconvenientele şablonului State se numără:<br />
- partiŃionarea comportamentului în funcŃie <strong>de</strong> stări - acest lucru oferă o<br />
ve<strong>de</strong>rea mai bună a comportamentului. Când un obiect se află într-o stare specifică,<br />
privind subclasele corespunzătoare clasei State se observă că toate comportamentele<br />
posibile ale acelei stări sunt incluse acolo<br />
- oferă structură, având o intenŃie clară - o variantă <strong>de</strong>s folosită a şablonului<br />
State este utilizarea constantelor şi a unei structuri switch pentru a <strong>de</strong>termina acŃiunile<br />
următoare. Aceasta este o soluŃie slabă <strong>de</strong>oarece creează duplicate. Multe meto<strong>de</strong><br />
folosesc aproape în acelaşi fel structura switch. Dacă se doreşte adăugarea unei noi<br />
stări, trebuie modificate toate meto<strong>de</strong>le clasei Context prin adăugarea unui nou<br />
element fiecărei structuri switch. Prin contrast, aceeaşi schimbare în sistem, folosind<br />
şablonul State, este realizată simplu prin crearea unei noi implementări a stării<br />
- tranziŃiile sunt explicite - când se folosesc constante pentru stări, este uşor să<br />
se confun<strong>de</strong> o schimbare <strong>de</strong> stare cu o atribuire a unei variabile <strong>de</strong>oarece sintactic<br />
arată la fel. Utilizând şablonul State, stările sunt compartimentate în obiecte, făcând<br />
mult mai uşoară recunoaşterea unei schimbări <strong>de</strong> stări<br />
- stările pot fi distribuite - dacă subclasele clasei State conŃin doar<br />
comportamente şi nu instanŃe ale variabilelor, ele <strong>de</strong>vin practic şabloane Flyweights.<br />
Orice stare <strong>de</strong> care au nevoie poate să le fie trimisă prin intermediul interfeŃei<br />
Context. Se reduce astfel numărul obiectelor din sistem<br />
- şablonul State foloseşte un număr mare <strong>de</strong> clase - acest lucru poate fi<br />
consi<strong>de</strong>rat un <strong>de</strong>zavantaj. Şablonul State creează cel puŃin o clasă pentru fiecare stare<br />
posibilă. Insă este <strong>de</strong> preferat acest lucru, alternativa fiind lungi structuri switch<br />
• Variante ale şablonului<br />
Unul din lucrurile importante în ceea ce priveşte şablonul State este<br />
<strong>de</strong>terminarea cui guvernează tranziŃiile dintre stări. O altă opŃiune, în afara subclaselor<br />
lui State şi Context, ar 11 privirea tranziŃiilor într-o structură <strong>de</strong> tabel, cu un tabel<br />
pentru fiecare stare. Se transformă astfel codul <strong>de</strong> tranziŃie într-o operaŃie <strong>de</strong> privire a<br />
unui tabel. Pentru a schimba criteriul <strong>de</strong> tranziŃie, doar datele din tabel trebuie<br />
schimbate în loc <strong>de</strong> a modifica codul. Dezavantajele sunt însă numeroase: tabelele<br />
sunt <strong>de</strong> multe ori mai ineficiente <strong>de</strong>cât apelul unei meto<strong>de</strong>; introducerea logicii unei<br />
tranziŃii într-un tabel o face mult mai greu <strong>de</strong> înŃeles. Principala diferenŃă este că<br />
şablonul State se concentrează pe mo<strong>de</strong>larea comportamentului în funcŃie <strong>de</strong> stări, pe<br />
când folosirea tabelelor se bazează pe tranziŃiile dintre diferite stări. O alăturare a<br />
acestor doua abordări combină dinamică mo<strong>de</strong>lului cu tabele cu şablonul State.<br />
43
TranziŃiile se stochează într-un obiect HashMap. dar în loc <strong>de</strong> a avea un tabel pentru<br />
fiecare stare, se creează un obiect HashMap pentru fiecare metodă din clasa State.<br />
• Şabloane asociate<br />
Printre şabloanele asociate se află:<br />
- Flyweight - stările pot fi disponibile folosind acest şablon<br />
- Singleton - multe şabloane State sunt şabloane Singleton. în special atunci<br />
când sunt şi şabloane Flyweights<br />
• Exemplu<br />
Presupunem că utilizatorii se conectează la o bază <strong>de</strong> date pentru a efectua<br />
anumite acŃiuni. Se folosesc astfel două subclase Management şi Sales. pentru a se<br />
exemplifica şablonul State:<br />
abstract class Connection {<br />
public abstract void open(); public<br />
abstract void close ( ) ; public abstract;<br />
void log Ń ) ; }<br />
class Sales extends Connection {<br />
public void open() {<br />
Systerr.. out. println ( "<strong>de</strong>seh: <strong>de</strong>re bază <strong>de</strong> date pentru<br />
vânzări"); }<br />
public void close() {<br />
System.out.println("închi<strong>de</strong>re bază <strong>de</strong> date"); }<br />
public void log() {<br />
System.out.println("iocare activităŃi";;<br />
class Management extends Connection {<br />
public void open Ń) {<br />
System, out. println ( "<strong>de</strong>schi<strong>de</strong>re bază <strong>de</strong> date pentru<br />
management");<br />
\<br />
public void closet) {<br />
System.out.println("închi<strong>de</strong>re oază <strong>de</strong> date"); Ń<br />
public veid log() {<br />
System.out.println("logare activităŃi");<br />
class Controller public sta public<br />
static Management manage; private<br />
static Connection current;<br />
Controller() {<br />
sales =• new Sales (); manage =<br />
new Management(); }<br />
public void makeSalesConnecticn() { current - sales; }<br />
public void makeManagementConnection() { current -<br />
manaae; }<br />
44
public voia open ; ; { :.::: --•:/.. : :: n ; •<br />
public void close{} { :.::,:_:.:::;;.•; ;<br />
public void log ( ) { curr^r.~..l: ~ . ; }<br />
lass Test {<br />
Stririg con;<br />
Controller controller;<br />
Test (String con) {<br />
controller = new Controller!);<br />
ii Ń con . equalsIgnoreCa.se ; "management " ) )<br />
controller . makeManagementConr.ection î )<br />
i f { con.equaÎs IgnoreCase("sales") )<br />
controller.makeSalesConnection();<br />
controller.open(); controller.log();<br />
ccntroller.closeO ;<br />
lass Server {<br />
public static Test test;<br />
public static void main(String[] args) {<br />
new Test(args[O]);<br />
III.8. Strategy<br />
• Cunoscut şi ca Policy<br />
■ ProprietăŃi<br />
• Scop<br />
- Tip: comportamental<br />
- Nivel: componentă<br />
Să <strong>de</strong>finească un grup <strong>de</strong> clase care să reprezinte un set <strong>de</strong> comportamente<br />
posibile. Aceste comportamente pot fi apoi introduse uşor în aplicaŃie, schimbând<br />
după caz funcŃionalitatea.<br />
• Introducere<br />
Se presupune că managerul personal <strong>de</strong> informaŃii conŃine o listă <strong>de</strong> contacte.<br />
Pe măsură ce numărul acestora creşte, se doreşte existenŃa unei modalităŃi <strong>de</strong> sortare a<br />
intrărilor şi <strong>de</strong> vizualizare a informaŃiilor <strong>de</strong>spre un anumit contact. Pentru a face acest<br />
lucru, se poate crea o clasă care să înregistreze contactele în memorie, să le sorteze şi<br />
să afişeze informaŃiile <strong>de</strong>spre ele. Această cale duce la apariŃia unor probleme în timp,<br />
cea mai serioasă fiind aceea că soluŃia nu poate fi modificată şi extinsă uşor.<br />
45
De fiecare dată când se vrea introducerea unei noi variante <strong>de</strong> sortare şi <strong>de</strong><br />
afişare a informaŃiilor, trebuie modificată clasa care execută aceste operaŃii. Mai mult.<br />
o dată cu creşterea numărul <strong>de</strong> opŃiuni <strong>de</strong> sortare şi afişare, mărimea şi complexitatea<br />
codului se măreşte, făcându-1 mai greu <strong>de</strong> testat şi menŃinut.<br />
Dacă în schimb se <strong>de</strong>zvoltă o serie <strong>de</strong> clase, fiecare conŃinând o modalitate<br />
specifică <strong>de</strong> sortare şi afişare a contactelor? Clasa principală predă sarcina respectivă<br />
uneia dintre aceste clase, eliminându-se astfel codul complex al celeilalte soluŃii.<br />
Şablonul Strategv se bazează pe obiecte care au o stare şi un comportament,<br />
înlocuind un obiect cu un altul, are loc o schimbare a comportamentului, şi cu toate că<br />
astfel apar mai multe clase, fiecare dintre acestea este uşor <strong>de</strong> menŃinut iar soluŃia<br />
generală este foarte extensibilă.<br />
• Utilizare<br />
Şablonul Strategv se foloseşte când:<br />
- există mai multe căi <strong>de</strong> a efectua o acŃiune<br />
- nu se ştie cu exactitate ce cale să se folosească <strong>de</strong>cât în momentul rulării<br />
- se doreşte adăugarea uşoară a altor modalităŃi <strong>de</strong> efectuare a unei acŃiuni<br />
- se vrea un cod uşor <strong>de</strong> întreŃinut pe măsură ce se adaugă noi comportamente<br />
• Descriere<br />
De multe ori există mai multe căi <strong>de</strong> a efectua aceeaşi acŃiune. Sortarea, <strong>de</strong><br />
exemplu, poate fi tăcută cu ajutorul unor algoritmi cum ar fi quick-sort şi sortarea prin<br />
metoda bulelor, prin utilizarea câmpurilor multiple sau conform anumitor criterii.<br />
Când un obiect îşi poate atinge scopul prin mai multe căi, el <strong>de</strong>vine complex şi<br />
greu <strong>de</strong> administrat. Pentru salvarea unui document în diferite formate, trebuie scris<br />
un cod care să producă o clasă capabilă să reprezinte documentul respectiv şi să-1<br />
salveze conform cerinŃelor. O dată cu creşterea numărului formatelor şi a<br />
complexităŃii lor, administrarea codului într-o singură clasă <strong>de</strong>vine greoaie.<br />
în astfel <strong>de</strong> cazuri se poate şablonul Strategv pentru menŃinerea unui echilibru<br />
între flexibilitate şi complexitate. Acest şablon separă comportamentele unui obiect,<br />
reprezentându-le în câte o clasă separată. Obiectul foloseşte apoi comportamentul care<br />
satisface cerinŃele <strong>de</strong> la un moment dat. De exemplu, pentru un document se poate<br />
<strong>de</strong>zvolta câte o clasă care să-1 salveze în fiecare format, comportamentul acestor clase<br />
fiind <strong>de</strong>finit într-o supraclasă sau o interfaŃă.<br />
Şablonul Strategy administrează seturi <strong>de</strong> algoritmi <strong>de</strong> bază. cum ar fi căutarea<br />
şi sortarea. Poate fi folosit şi pentru interogarea bazelor <strong>de</strong> date. <strong>de</strong>finirea diferitelor<br />
modalităŃi <strong>de</strong> interogare sau organizarea rezultatelor. în spaŃiul afacerilor, el este<br />
câteodată utilizat pentru a reprezenta diferite posibilităŃi <strong>de</strong> efectuare a tranzacŃiilor.<br />
Comanda unei staŃii <strong>de</strong> lucru, <strong>de</strong> exemplu, poate fi implementată cu ajutorul<br />
şablonului Strategy dacă se doreşte achiziŃionarea uneia diferită <strong>de</strong> mo<strong>de</strong>lul standard.<br />
46
• Implementare<br />
Diagrama <strong>de</strong> clase a şablonului Strateg} este următoarea:<br />
Strateg} Client<br />
-Strategv strategv<br />
+void setStrategy(Strategy s)<br />
+void performOperationQ<br />
strategv. operationQ<br />
ConcreteStrategyB<br />
+void operationQ<br />
interface Strateg}'<br />
-void operalion()<br />
Pentru implementarea şablonului Stratcgy este nevoie <strong>de</strong>:<br />
ConcreteStrategyA<br />
+void operation()<br />
- StrategyClient - clasa care foloseşte diferite strategii pentru anumite sarcini.<br />
Ea păstrează o referinŃă către instanŃa Strategy pe care o foloseşte şi conŃine o metodă<br />
care înlocuieşte instanŃa Strategy curentă cu o altă implementare<br />
- Strategy - interfaŃa ce <strong>de</strong>fineşte toate meto<strong>de</strong> ce pot fi folosite <strong>de</strong> clasa<br />
StrategyClient<br />
- ConcreteStrategy - clasa care implementează interfaŃa Strategy. utilizând un<br />
set specific <strong>de</strong> reguli pentru fiecare metodă a interfeŃei<br />
• Beneficii şi inconveniente<br />
Fiecare comportament este <strong>de</strong>finit în propria-i clasă, astfel că şablonul<br />
Strategy conduce la un control mult mai uşor al comportamentelor. Totodată,<br />
extin<strong>de</strong>rea unui mo<strong>de</strong>l care să includă noi comportamente este mai facilă.<br />
Principala provocare în ceea ce priveşte acest şablon, este stabilirea modului<br />
exact <strong>de</strong> reprezentare a unui comportament. Fiecare Strategy trebuie să aibă aceeaşi<br />
interfaŃă pentru obiectul apelat. Trebuie i<strong>de</strong>ntificat unul generic, care să poată fi<br />
aplicat mai multor implementări, dar care în acelaşi timp să fie specific pentru<br />
diferitele strategii concrete folosite.<br />
■ Şabloane asociate<br />
Printre şabloanele asociate se află:<br />
- Singleton - <strong>de</strong> multe ori implementările şablonului Strategy sunt reprezentate<br />
ca şabloane Singleton<br />
47
- Flyweight - câteodată obiectele <strong>de</strong> tip Strategy sunt proiectate ca obiecte <strong>de</strong><br />
tip Flyweights pentru a le face mai puŃin costisitoare <strong>de</strong> creat<br />
- Factory Method - şablonul Strateg) poate fi <strong>de</strong>finit ca un şablon Factory<br />
Method astfel încât clasa principală să utilizeze noi implementări ale şablonului iară a<br />
recoda alte părŃi ale aplicaŃiei<br />
Exemplu<br />
Un exemplu simplu al şablonului Strateg}- este prezentat. Se aruncă un zar şi în<br />
funcŃie <strong>de</strong> rezultat se afişează un mesaj:<br />
interface Fortur.eCookies { public void prinŃ (); }<br />
class Six implementa FortuneCookies { public void<br />
prinŃ() {<br />
System.out.prinŃIn("Perfect!");<br />
class Five irrpl emerit s FortuneCookies {<br />
public void prinŃ() {<br />
Systera. out. println ( "Destul <strong>de</strong> bine!");<br />
class t-our iniplements FortuneCookies {<br />
public void prinŃ() {<br />
System.out.crintln("Bine!");<br />
lass Three implements FortuneCookies {<br />
public void prinŃ() {<br />
System.cut.printin("Se poate şi mai bine!")<br />
class Two implements FortuneCookies {<br />
public void prinŃ () {<br />
lass One implements FortuneCookies {<br />
public void prinŃ() {<br />
System.out.println("Slab!")<br />
lass Nuli impl emerit s FortuneCookies {<br />
public void prinŃ {) {<br />
Syst em. ou c . pr int ^n ( "N — mic ! "<br />
cl ass D ice {<br />
p u b l i c i n t t h r o w l t ( ) {<br />
r e t u r n ( i nt ) ( Ma t h . r an d o m ( )" 6 ) +1 ;<br />
class lest {<br />
static void goodFortune() {<br />
irit luckyNum = new Di ce ( ) . throwlt ( ) ;<br />
FortuneCookies fc; s w i t c h (Iu c k y M u rr.) {<br />
case 1: fc = new On e( ) ; b re a k;<br />
case 2: Le = new Tw o( ) ; b re a k;<br />
48
f c . p r i n t ( ) ; }<br />
public static vcid iru<br />
gocdFortune();<br />
III.9. Visitor •<br />
ProprietăŃi<br />
• Scop<br />
- Tip: comportamental<br />
- Nivel: componentă<br />
Ci^se ' : : i : -- :. . ■ ; _ : . : -^ - ; creak;<br />
case ; : : : : : = : ^ : _.._ ; ivreak;<br />
<strong>de</strong>fault: zz -- r ev :: J 1I . ; ;<br />
Să pună la dispoziŃie o cale uşoară <strong>de</strong> a efectua acŃiuni pentru o familie <strong>de</strong><br />
clase. El centralizează comportamentele şi permite ca ele să fie modificate sau extinse<br />
tară a modifica clasele care le folosesc.<br />
• Introducere<br />
Se doreşte ca managerul personal <strong>de</strong> informaŃii să conŃină capacităŃi <strong>de</strong><br />
planificare, iar planificatorul să fie folosit pentru lucruri ca licitaŃiile, analizele <strong>de</strong> risc<br />
şi estimarea timpului. Următoarele reprezintă un proiect complex:<br />
- Project - rădăcina ierarhiei proiectului, reprezentând proiectul în sine<br />
- Task - o sarcină în cadrul proiectului<br />
- Depen<strong>de</strong>ntTask - o sarcină care <strong>de</strong>pin<strong>de</strong> <strong>de</strong> alta<br />
- Deliverable - un articol sau document ce reprezintă rezultatul proiectului<br />
Pentru i<strong>de</strong>ntificarea clară a acestor clase ca părŃi ale unui mo<strong>de</strong>l, ele sunt<br />
organizate în jurul unei interfeŃe numite Projectitem. Până aici totul este în regulă.<br />
Dar cum se poate coda abilitatea <strong>de</strong> a estima costul total al proiectului? Acest calcul<br />
va <strong>de</strong>pin<strong>de</strong> probabil <strong>de</strong> tipul specific al interfeŃei Projectitem. în această interfaŃă se<br />
<strong>de</strong>fineşte o metoda getCost care trebuie să calculeze costul pentru o parte specifică a<br />
proiectului. Acest lucru pennite calcului costului pentru fiecare articol din structura<br />
proiectului. Se poate <strong>de</strong>ci<strong>de</strong> asupra unei modalităŃi <strong>de</strong> genul:<br />
- Project - tară operaŃii, <strong>de</strong> vreme ce costul este egal cu costul tuturor<br />
articolelor proiectului<br />
- SimpleTask - costul este calculat pe baza orelor în care s-a lucrat<br />
- Depen<strong>de</strong>ntTask - la fel ca sarcina simplă, dar adăugându-se un factor<br />
adiŃional care reprezintă <strong>de</strong>pen<strong>de</strong>nŃa unei sarcini <strong>de</strong> alta<br />
- Deliverable - costul este o estimare <strong>de</strong> bază a materialelor plus costul <strong>de</strong><br />
producŃie<br />
49
Totuşi, cum se poate calcula timpul necesar pentru rezolvarea proiectului,<br />
precum şi riscul? Folosind aceeaşi metodă ca şi în cazul celorlalte costuri face codul<br />
greu <strong>de</strong> întreŃinut. Trebuie scrise o grămadă <strong>de</strong> noi meto<strong>de</strong> care vor fi împrăştiate prin<br />
toate clasele proiectului. Cu fiecare nouă operaŃie, clasa <strong>de</strong>vine din ce în ce mai largă,<br />
mai complexă şi mai greu <strong>de</strong> înŃeles. De asemenea este dificil <strong>de</strong> a Ńine evi<strong>de</strong>nŃa<br />
informaŃiilor. Dacă se doreşte estimarea costului, a timpului sau a riscului folosind<br />
astfel <strong>de</strong> meto<strong>de</strong>, trebuie găsită o modalitate <strong>de</strong> păstrare a rezultatelor intermediare, <strong>de</strong><br />
vreme ce fiecare metodă aparŃine unui obiect specific al proiectului. Se ajunge astfel<br />
la trimiterea informaŃiilor prin întreaga structură a proiectului.<br />
Şablonul Visitor oferă o alternativă. Se <strong>de</strong>fineşte o singură clasă <strong>de</strong>numită<br />
CostProjectVisitor, care efectuează toate calculele legate <strong>de</strong> cost. în loc <strong>de</strong> a efectua<br />
aceste calcule în cadrul fiecărui articol al proiectului, acestea se fac <strong>de</strong> către clasa<br />
Visitor, care reŃine şi costul total. InterfaŃa Projectltem nu va mai conŃine metoda<br />
getCost. în schimb, va avea o metodă acceptVisitor mult mai generică, care foloseşte<br />
un obiect <strong>de</strong> tip ProjectVisitor pentru a apela o metodă specifică din clasa<br />
ProjectVisitor. De exemplu, metoda acceptVisitor pentru o anumită sarcină apelează<br />
metoda visitTask a interfeŃei Visitor. Dacă aceasta este <strong>de</strong> tipul CostProjectVisitor,<br />
atunci metoda visitTask calculează costul asociat sarcinii respective.<br />
Acest mo<strong>de</strong>l oferă beneficii substanŃiale, cel mai important fiind acela că se<br />
pot adăuga uşor noi operaŃii. Pentru calculul timpului trebuie doar scrisă o clasă<br />
TimeProjectVisitor care să conŃină toate meto<strong>de</strong>le necesare pentru a calcula timpul în<br />
care s-a lucrat la proiect. Codul pentru obiectele proiectului rămâne neschimbat,<br />
<strong>de</strong>oarece el conŃine <strong>de</strong>ja apeluri către meto<strong>de</strong>le generice <strong>de</strong>finite în clasa<br />
ProjectVisitor.<br />
Mai mult. şablonul Visitor conŃine un loc pentru centralizarea situaŃiei. Pentru<br />
clasa CostProjectVisitor se pot stoca rezultatele intermediare chiar în Visitor, în timp<br />
ce se efectuează calculele costului. Codul pentru centralizarea estimării face uşoară<br />
ajustarea calculelor <strong>de</strong> bază. Prin folosirea şablonul Visitor, se pot adaugă uşor<br />
caracteristici cum ar fi factori adiŃionali, putându-se calcula <strong>de</strong> exemplu discount-ul.<br />
• Utilizare<br />
Şablonul Visitor se foloseşte atunci când sunt în<strong>de</strong>plinite următoarele condiŃii:<br />
- un sistem conŃine un grup <strong>de</strong> clase asociate<br />
- câteva operaŃii netriviale trebuie efectuate <strong>de</strong> unele sau <strong>de</strong> toate clasele<br />
asociate<br />
- operaŃiile trebuie efectuate diferit pentru clasele variate<br />
• Descriere<br />
Şablonul Visitor implică preluarea unor operaŃii asociate dintr-un grup <strong>de</strong> clase<br />
şi plasarea lor împreună într-o singură clasă. Motivul este întreŃinerea codului - în<br />
unele situaŃii <strong>de</strong>vine prea complicat <strong>de</strong> întreŃinut operaŃiile în cadrul fiecărei clase.<br />
Şablonul Visitor este folositor în aceste cazuri, <strong>de</strong>oarece conŃine un suport generic<br />
pentru susŃinerea operaŃiilor unui grup <strong>de</strong> clase.<br />
El necesită ca toate clasele ce conŃin operaŃii sau Elemente, să aibă şi o metodă<br />
<strong>de</strong> acceptare care să fie apelată atunci când Visitor-ul efectuează o operaŃie asupra<br />
unui Element. Argumentul acestei meto<strong>de</strong> <strong>de</strong> acceptare este o instanŃă a Visitor-ului.<br />
50
Fiecare implementare a unui Element conŃine metoda <strong>de</strong> acceptare care<br />
apelează metoda <strong>de</strong> vizitare din cadrul Yisitor-ului. Fiecare Visitor implementează o<br />
metodă <strong>de</strong> vizitare specifică subtipului Element-ului.<br />
• Implementare<br />
Diagrama <strong>de</strong> clase a şablonului Visitor este următoarea:<br />
interface Visitor<br />
+void visit(ConcreteElementA cmElementA)<br />
+void visitfConcreteElc'mcntB anElementB)<br />
ConcreteVisitorA Concrete VisitorB<br />
+void visit(ConcreteElementA anElementA)<br />
+void visit(ConcreteElementB anElementB)<br />
interface Element<br />
+void accept (Visitor visitor)<br />
+void visit(ConcreteElementA anElementA)<br />
+void visit(ConcreteElementB anElementB)<br />
ConcreteElementA ConcreteElementB<br />
+void accept(Visitor visitor) +void accept(Visitor visitor)<br />
visitor. visit(this) visitor.visit(this)<br />
Pentru implementarea şablonului Visitor se folosesc următoarele:<br />
- Visitor - clasa abstractă sau interfaŃa care <strong>de</strong>fineşte o metodă <strong>de</strong> vizitare<br />
pentru fiecare clasă ConcreteElement<br />
- ConcreteVisitor - clasa ce reprezintă o operaŃie specifică ce trebuie efectuată<br />
în cadrul sistemului. Implementează toate meto<strong>de</strong>le <strong>de</strong>finite în Visitor pentru o<br />
operaŃie specifică sau un algoritm<br />
- Element - clasa abstractă sau interfaŃa asupra căruia Visitor-ul operează.<br />
Minim, ea <strong>de</strong>fineşte o metodă <strong>de</strong> acceptare care primeşte un Visitor ca argument<br />
- ConcreteElement - entitate specifică a sistemului. Implementează metoda <strong>de</strong><br />
acceptare <strong>de</strong>finită în Element, apelând metoda <strong>de</strong> vizitare corespunzătoare <strong>de</strong>finită în<br />
Visitor<br />
Un lucru care trebuie urmărit cu atenŃie atunci când se implementează şablonul<br />
Visitor este supraîncărcarea meto<strong>de</strong>lor. Şablonul foloseşte supraîncărcarea meto<strong>de</strong>lor<br />
pentru metoda <strong>de</strong> vizitare. InterfaŃa Visitor poate avea două meto<strong>de</strong> <strong>de</strong> vizitare,<br />
fiecare primind un alt argument.<br />
51
■ Beneficii şi inconveniente<br />
Datorita structurii sale. şablonul Visitor tace uşoară adăugarea <strong>de</strong> noi<br />
comportamente sistemului. Când se implementează iniŃial şablonul, se <strong>de</strong>zvoltă un<br />
cadru <strong>de</strong> suport pentru fiecare altă acŃiune a Visitor-ului ce se poate efectua în viitor.<br />
Pentru a adaugă o nouă funcŃionalitate, se creează o nouă clasă care implementează<br />
interfaŃa Visitor şi se scrie noul cod.<br />
Şablonul Visitor este folositor <strong>de</strong>oarece permite centralizarea codului pentru o<br />
operaŃie, acest lucru tăcând mai facilă întreŃinerea acestuia. Acelaşi obiect <strong>de</strong> Ńip<br />
Visitor este utilizat în mod normal pentru vizitarea fiecărui Element din structură, prin<br />
urmare el pune la dispoziŃie o locaŃie centrală pentru reŃinerea datelor colectate sau<br />
pentru stocarea rezultatelor intermediare.<br />
Partea rea a acestui şablon este aceea că flexibilitatea este scăzută în lanŃul<br />
claselor Element. Orice adăugare sau modificare a ierarhiei acestor clase are o mare<br />
şansă <strong>de</strong> a produce o rescrie a codului structurii Visitor. Orice clasă adiŃională<br />
necesită că o nouă metodă să fie <strong>de</strong>finită în interfaŃa Visitor şi fiecare ConcreŃeVisitor<br />
trebuie să conŃină o implementare a acestei meto<strong>de</strong>. Mai mult, şablonul poate încalcă<br />
principiul încapsulării. Şablonul Visitor preia codul care se aplică unui obiect în afara<br />
clasei obiectului şi îl mută într-o nouă locaŃie.<br />
• Variante ale şablonului<br />
Ca multe alte şabloane, Element-ul şi Visitor-ul se poŃ reprezenta în Java ca<br />
interfeŃe sau clase abstracte. O altă <strong>de</strong>cizie ce trebuie luată este cum să fie aplicat<br />
Visitor-ul unei colecŃii <strong>de</strong> Elemente. Şablonul Visitor nu are nici o <strong>de</strong>scriere privind<br />
structura <strong>de</strong> Elemente asupra căreia operează. Se poate folosi un ConcreŃeVisitor la<br />
fel <strong>de</strong> eficient pentru parcurgerea unei colecŃii simple, unei liste sau unui arbore.<br />
Deşi unele implementări ale Visitor-ului conŃin codul <strong>de</strong> parcurgere în<br />
interiorul ConcreteVisitor-ului, şablonul nu necesită acest lucru. Se poate folosi o<br />
clasă externă, <strong>de</strong> exemplu un Iterator. pentru mişcarea într-o colecŃie. Se poŃ apoi uni<br />
aceste două şabloane, Visitor şi Iterator, în funcŃie <strong>de</strong> cerinŃe.<br />
• Şabloane asociate<br />
Printre şabloanele asociate se află:<br />
- Interpreter - se poate folosi şablonul Interpretei' pentru centralizarea<br />
interpretărilor operaŃiilor<br />
- IŃerator - se poate folosi şablonul Iterator pentru parcurgerea unei colecŃii<br />
generice<br />
- Composite - se poate combina şablonul Composite cu Visitor în ve<strong>de</strong>rea<br />
parcurgerii unei structuri arborescente<br />
<strong>de</strong> a<br />
1<br />
Exemplu • Exemplu<br />
Următorul exemplu foloseşte şablonul Visitor pentru a<br />
reprezenta modalităŃi comanda pizza <strong>de</strong> la diferite restaurante:<br />
interface Visitor { public void visit (Pizza p)<br />
interface Pizza { public Suring or<strong>de</strong>r(); }<br />
52
class ropjohn implementa -i z z; ; ■<br />
final String name - "Z :L "'::.:.'<br />
public String or<strong>de</strong>r;; ■. L r i<br />
\<br />
class FizzaHut impiements Fizza •.<br />
fina; String name --= "Fizz-Fu~<br />
pubizc String craer() { zoru:<br />
class GodFather impiements Fizza •;<br />
final String name = "GodFather"; public<br />
String or<strong>de</strong>r () { return nanie; }<br />
class ByPickup impiements Visitcr {<br />
private String name;<br />
private fina] String methcd = "Ry pick uc"; public<br />
void visit(Fizza p) { name = p.or<strong>de</strong>r(); } public<br />
String toString() { return nair.e - " " + methoc<br />
class ByDeiivery impiements Visitcr {<br />
private String name;<br />
private final String method = "Ey <strong>de</strong>livery"; public<br />
void visit(Fizza p) { name = p.or<strong>de</strong>r(); } public<br />
String roStringf) { return name + " " + methoc<br />
class Dinner {<br />
public Pizza getDinner() {<br />
switch ((int)(Math.random(;*3)){<br />
case 0: return new PopJohn(); break; case 1:<br />
return new PizzaHut () ; break; case 2:<br />
return new GodFather(); break; <strong>de</strong>fault:<br />
return nuli; }<br />
public Visitor howto() {<br />
switch ( (int) (Math.random()*2) ) {<br />
case 0: return new ByPickup(); break;<br />
case 1: return new ByDeiivery(); break;<br />
<strong>de</strong>fault: return nuli;<br />
class Test {<br />
public static void main(String[] args) { List<br />
pizzaList = new ArrayList(); pizzaList.add(new<br />
PopJohn () ) ; pizzaList.add(new PizzaHut () ) ; pi<br />
z.zaList . add (new 7 GodFather () ) ; Iteratcr it =<br />
pizzaList.iterator(); while (it.hasNext()) {<br />
System.out.prinŃIn(((Fizza)it.next()).or<strong>de</strong>r());<br />
Dinner d i; new Dinner () ;<br />
Fizza pza = d.getDinner();<br />
Visitor v = d.howtoŃ);<br />
v.visit(pza);<br />
System.out.prinŃin(v);<br />
53
111.10. Template Method<br />
• ProprietăŃi<br />
• Scop<br />
- Tip: comportamental<br />
- Nivel: obiect<br />
Să pună la dispoziŃie o metodă care să permită subclaselor să suprascrie părŃi<br />
dintr-o metodă Iară a o rescrie.<br />
■ Introducere<br />
Când se lucrează cu proiecte, se doreşte frecvent estimarea costului pentru<br />
efectuarea unei anumite sarcini sau pentru producerea rezultatului. Managerul<br />
personal <strong>de</strong> informaŃii foloseşte un număr <strong>de</strong> clase pentru a reprezenta proiectele.<br />
Minim, clasele Task şi Deliverable sunt utilizate pentru reprezentarea elementelor<br />
proiectului. Pe măsură ce un proiect <strong>de</strong>vine mai complex, trebuie create clase<br />
adiŃionale cum ar fi Project sau Depen<strong>de</strong>ntTask pentru a satisface nevoile mai<br />
sofisticate. Cu toate că se poate crea o metoda getCostEstimate pentru fiecare clasă în<br />
parte, o astfel <strong>de</strong> modalitate implică mult cod duplicat. Numărul claselor crescând,<br />
<strong>de</strong>vine din ce în ce mai dificil <strong>de</strong> întreŃinut codul din toate clasele proiectului.<br />
O abordare mai bună este gruparea tuturor claselor asociate proiectului sub o<br />
supraclasă şi <strong>de</strong>finirea meto<strong>de</strong>i getCostEstimate acolo. Dar ce se poate face atunci<br />
când părŃi din metoda getCostEstimate <strong>de</strong>pind <strong>de</strong> informaŃii specifice fiecărei clase a<br />
proiectului? Daca obiectul <strong>de</strong> tip Task are o cale diferită <strong>de</strong> calculare o orelor faŃă <strong>de</strong><br />
obiectul <strong>de</strong> tip Deliverable?<br />
în acest caz, metoda getCostEstimate trebuie <strong>de</strong>finită astfel încât să apeleze o<br />
metodă abstractă getTimeRequired şi să permită claselor Task şi Deliverable să<br />
<strong>de</strong>finească metoda cât mai convenabil. Această modalitate, numită Template Method.<br />
aduce beneficii în ceea ce priveşte reutilizarea codului, permiŃând în acelaşi timp<br />
claselor să modifice anumite părŃi ale comportamentului în funcŃie <strong>de</strong> necesitaŃi.<br />
• Utilizare<br />
Şablonul Template Method se foloseşte pentru:<br />
- a pune la dispoziŃie o structură schelet pentru o metodă, permiŃând<br />
subclaselor să re<strong>de</strong>finească părŃi specifice din metodă<br />
- a centraliza părŃi ale unei meto<strong>de</strong> care sunt <strong>de</strong>finite în toate subtipurile unei<br />
clase, dar care vor avea întot<strong>de</strong>auna o mică diferenŃă în fiecare clasă<br />
- a controla ce operaŃii subclasele au voie a suprascrie<br />
• Descriere<br />
Când se construieşte o ierarhie complexă <strong>de</strong> clase pentru aplicaŃie, codul este<br />
<strong>de</strong> multe ori duplicat în anumite locuri. Acest lucru nu este <strong>de</strong> dorit, <strong>de</strong> vreme ce se<br />
doreşte reutilizarea codului oricât <strong>de</strong> mult. Refacerea codului astfel încât meto<strong>de</strong>le să<br />
se alia în supraclasă este un pas în direcŃia bună.<br />
54
Problema este că o operaŃie care a fost refăcută se bazează pe informaŃia<br />
specifică care este valabilă doar în subclasă. Din această cauză, <strong>de</strong>seori se acceptă<br />
codul duplicat în clase multiple.<br />
Când multe meto<strong>de</strong> din clase asociate au o structură similară, şablonul<br />
Template Method este <strong>de</strong> folos. Mai întâi, se <strong>de</strong>termină care părŃi ale meto<strong>de</strong>lor sunt<br />
similare. Aceste părŃi trebuie centralizate în supraclasă. în timp ce celelalte operaŃii<br />
rămân în subclase. Metoda Template nou <strong>de</strong>finită conŃine structura operaŃiei. Pentru<br />
fiecare parte a operaŃiei care variază, o metodă abstractă este <strong>de</strong>finită în supraclasă.<br />
Subclasele suprascriu aceste meto<strong>de</strong> punând la dispoziŃie propria lor implementare.<br />
Când este apelată metoda template în subclasă, codul din supraclasă este executat.<br />
Când metoda template din supraclasă este <strong>de</strong>clarată final, subclasele sunt limitate în<br />
ceea ce priveşte părŃile supraclasei pe care le pot suprascrie.<br />
Acest şablon este <strong>de</strong>numit Template Method <strong>de</strong>oarece pune la dispoziŃie o<br />
metodă ce conŃine structura operaŃiei, dar lasă unele uşi <strong>de</strong>schise prin apelarea <strong>de</strong><br />
meto<strong>de</strong> abstracte. Este exact ca un template. <strong>de</strong>oarece subclasele umplu spaŃiile goale<br />
conŃinând implementări pentru meto<strong>de</strong>le abstracte.<br />
• Implementare<br />
Diagrama <strong>de</strong> clase a şablonului Template Method este următoarea:<br />
AbstractClass<br />
+void templateMethod()<br />
+void subOperation 1 ()<br />
+void sub()peration2()<br />
i<br />
ConcreteClass<br />
+void subOperationl()<br />
+void subOperation2()<br />
//'cod schelet<br />
subOperation 1() "mai<br />
mult cod schelet<br />
subOperation2() //si<br />
mai mult cod<br />
Pentru implementarea şablonului Template Method este nevoie <strong>de</strong>:<br />
- AbstractClass - clasa abstractă ce conŃine metoda template şi <strong>de</strong>fineşte una<br />
sau mai multe meto<strong>de</strong> abstracte. Metoda template conŃine codul schelet şi apelează<br />
una sau mai multe meto<strong>de</strong> abstracte. Pentru a preveni subclasele să suprascrie metoda<br />
template. aceasta trebuie <strong>de</strong>clarată final<br />
- ConcreteClass - extin<strong>de</strong> clasa AbstractClass şi implementează meto<strong>de</strong>le<br />
abstracte ale acesteia. Se bazează pe clasa AbstractClass pentru a pune la dispoziŃie<br />
structura operaŃiei conŃinută în metoda template
• Beneficii şi inconveniente<br />
Principalul beneficiu al şablonului Template Method este acela că permite<br />
reutilizarea codului. Fără acest şablon, codul este duplicat în mai multe subclase.<br />
Acest beneficiu face şablonul Template Method esenŃial pentru cadre. Un cadru<br />
conŃine mai multe meto<strong>de</strong> care doar minimal se bazează pe implementările specifice<br />
din subclasa. Folosind acest şablon înseamnă că întreaga structură poate fi pusă la<br />
dispoziŃie <strong>de</strong> cadru.<br />
Dacă metoda template apelează prea multe meto<strong>de</strong> abstracte, după ceva timp<br />
folosirea clasei AbstractClass că o supraclasă <strong>de</strong>vine plictisitoare. Este mai bine ca<br />
metoda template să apeleze un număr limitat <strong>de</strong> meto<strong>de</strong> abstracte.<br />
• Variante ale şablonului<br />
O variantă este ca şablonul Template Method să apeleze meto<strong>de</strong> concrete în<br />
loc <strong>de</strong> meto<strong>de</strong> abstracte. Clasa AbstractClass conŃine o implementare <strong>de</strong> bază pentru<br />
fiecare din meto<strong>de</strong>le apelate <strong>de</strong> Template Method. Aceste meto<strong>de</strong> sunt <strong>de</strong>numite<br />
meto<strong>de</strong> cârlig.<br />
RaŃiunea este că atunci când se foloseşte clasa AbstractClass, nu mai trebuie<br />
suprascrise meto<strong>de</strong>le pentru a putea folosi şablonul Template Method. AbstractClass<br />
nici nu trebuie sa fie abstractă.<br />
O responsabilitate a celui care implementează şablonul Template Method este<br />
să se documenteze în privinŃa meto<strong>de</strong>lor care sunt folosite în alte meto<strong>de</strong> template. în<br />
şablonul Template Method normal, este clar ce meto<strong>de</strong> trebuie suprascrise, <strong>de</strong>oarece<br />
toate aceste meto<strong>de</strong> sunt abstracte. Dacă meto<strong>de</strong>le cârlig nu sunt menŃionate în<br />
documentaŃie, ele nu pot fi i<strong>de</strong>ntificate.<br />
• Şabloane asociate<br />
Printre şabloanele asociate se află:<br />
- Factory Method - meto<strong>de</strong>le template apelează <strong>de</strong>seori meto<strong>de</strong> factory pentru<br />
a crea instanŃe iară a cunoaşte clasele exacte ce se creează<br />
- Strategy - acest şablon foloseşte compunerea pentru a înlocui complet<br />
comportamentul, în timp ce şablonul Template Method foloseşte moştenirea pentru a<br />
înlocui părŃi ale comportamentului<br />
- Intercepting Filter - foloseşte şablonul Template Method pentru a<br />
implementa strategia Template Filter<br />
Exemplu<br />
Atunci când se face un împrumut, trebuie să se parcurgă o serie <strong>de</strong> paşi.<br />
Şablonul Template Method se foloseşte pentru a menŃine împreună aceşti paşi. tară a<br />
consi<strong>de</strong>ra implementarea reală a subclaselor:<br />
abstract class CheckBackgrcund {<br />
public abstract; void checkBankŃ) ;<br />
public abstract voia checkCredit();<br />
public abstract void checkLoan{);<br />
public abstract void checkStock();<br />
public abstract void checklr.come Ń ) ;<br />
56
public voia chec'-:..;<br />
checkEar:.
IV. Şabloane structurale<br />
Şabloanele structurale <strong>de</strong>scriu căi efective atât <strong>de</strong> partiŃionare cât şi <strong>de</strong><br />
combinare a elementelor unei aplicaŃii. Modul în care aceste şabloane influenŃează<br />
aplicaŃiile variază. De exemplu, şablonul Adapter poate realiza comunicarea între<br />
doua sisteme incompatibile. în vreme ce Faca<strong>de</strong> permite crearea unei interfeŃe simple<br />
cu utilizatorul fără a elimina toate opŃiunile valabile din sistem.<br />
IV. 1. Adapter<br />
• Cunoscut şi ca Wrapper<br />
• ProprietăŃi<br />
■ Scop<br />
- Tip: structural<br />
- Nivel: componentă<br />
Să acŃioneze ca un intermediar între dou clase, convertind interfaŃa unei clase<br />
astfel încât să poată fi folosită şi <strong>de</strong> cealaltă.<br />
• Introducere<br />
Unul dintre marile avantaje ale programării orientate pe obiecte este acela că<br />
permite reutilizarea codului. De vreme ce datele şi comportamentul sunt centralizate<br />
într-o clasă, se poate - cel puŃin în principiu - muta clasa dintr-un proiect într-altul şi<br />
reutiliza funcŃionalitatea tară prea mare efort. Din păcate, necunoscându-se cerinŃele<br />
codului pentru viitoarele proiecte, nu se ştie mereu cum să se schiŃeze clasa pentru o<br />
reutilizare optimă.<br />
De exemplu, pentru a grăbi <strong>de</strong>zvoltarea managerului personal <strong>de</strong> informaŃii, se<br />
<strong>de</strong>ci<strong>de</strong> cooperarea cu o altă persoană dintr-o altă Ńară. Această persoană lucrează la o<br />
aplicaŃie similară şi poate pune la dispoziŃie o implementare a unor componente ale<br />
aplicaŃiei. Dar atunci când se primesc fişierele, interfeŃele nu se potrivesc. Mai mult,<br />
codul este în altă limbă. Apar două soluŃii, amândouă neatrăgătoare.<br />
Prima opŃiune este aceea <strong>de</strong> a rescrie noua componentă astfel încât să poată<br />
implementa toate cerinŃele interfeŃei. Rescrierea noii componente este o i<strong>de</strong>e rea<br />
<strong>de</strong>oarece trebuie făcut acest lucru <strong>de</strong> fiecare dată când se primeşte o versiune înnoită a<br />
componentei respective.<br />
A doua opŃiune ar fi rescrierea aplicaŃiei folosind interfaŃa primită.<br />
Inconvenientul este că trebuie trecut prin tot codul pentru a modifica fiecare apariŃie a<br />
vechii interfeŃe, codul <strong>de</strong>venind astfel greu <strong>de</strong> înŃeles <strong>de</strong>oarece nu se cunoaşte limba în<br />
care este scrisă componenta primită.<br />
Este nevoie aici <strong>de</strong> un translator. Aici intervine şablonul Adapter. El se<br />
comportă similar unui adaptor <strong>de</strong> curent, convertind un tip în altul, altminteri<br />
incompatibile. Folosind acest şablon, aplicaŃia poate folosi interfaŃa proprie,<br />
permiŃând în acelaşi timp şi utilizarea noii componente. Când soseşte o versiune<br />
înnoită, singurul lucru ce trebuie modificat este Adapter-ul.<br />
58
:<br />
■ Utilizare<br />
Şablonul Adapter se foloseşte când:<br />
- se doreşte utilizarea unui obiect într-un mediu care aşteaptă o interfaŃă care<br />
este diferită <strong>de</strong> cea a obiectului<br />
- trebuie să existe o traducere a interfeŃelor între mai multe surse<br />
- un obiect trebuie să acŃioneze ca intermediar pentru un grup <strong>de</strong> clase,<br />
necunoscându-se care clasa va fi folosită <strong>de</strong>cât la rulare<br />
• Descriere<br />
Câteodată se doreşte folosirea unei clase într-un cadru nou fără a o recoda<br />
încât să se potrivească noului mediu. în astfel <strong>de</strong> cazuri, se poate proiecta o clasă<br />
Adaptee care să se comporte ca un translator. Această clasă primeşte apeluri <strong>de</strong> la<br />
mediu şi modifică apelurile pentru ca acestea să fie compatibile cu clasa Adaptee.<br />
Medii tipice în care un Adapter este folositor includ aplicaŃiile care suportă<br />
comportament plug-in cum ar fi graficele, textele, editoarele media sau browser-ele<br />
web. Un exemplu în lumea reală al şablonului Adapter o reprezintă un translator care<br />
realizează traducerea expresiilor comune dintr-un limbaj în altul, permiŃând<br />
comunicarea între doi indivizi altfel incompatibili.<br />
• Implementare<br />
Diagrama <strong>de</strong> clase a şablonului Adapter este următoarea:<br />
Framework<br />
-Adapter adapter<br />
r-void performOperation()<br />
Adapter<br />
-Adaptee adaptee<br />
+void operaŃi on()<br />
Adaptee<br />
+void adaptedOperationQ<br />
Pentru implementarea şablonului Adapter este nevoie <strong>de</strong>:<br />
adapter.operationQ<br />
adaptee.adaptedOperationQ<br />
- Framework - foloseşte interfaŃa Adapter. Această clasă fie construieşte clasa<br />
ConcreteAdapter. fie este fixată în altă parte<br />
- Adapter - interfaŃa care <strong>de</strong>fineşte meto<strong>de</strong>le pe care le foloseşte clasa<br />
Framework<br />
59
- ConcreteAdapter - clasa care implementează interfaŃa Adapter. Ea păstrează<br />
o referinŃă către interfaŃa Adaptee şi traduce apelurile <strong>de</strong> meto<strong>de</strong> <strong>de</strong> la clasa<br />
Framework în apeluri <strong>de</strong> meto<strong>de</strong> în interfaŃa Adaptee. Această traducere poate implica<br />
şi ascun<strong>de</strong>rea sau modificarea parametrilor şi tipurilor rcturnate<br />
- Adaptee - interfaŃa care <strong>de</strong>fineşte meto<strong>de</strong>le tipului ce va fi adaptat<br />
- ConcreteAdaptee - clasa ce implementează interfaŃa Adaptee. Ea trebuie<br />
adaptată pentru a putea fi folosită <strong>de</strong> Framework<br />
• Beneficii şi inconveniente<br />
Şablonul Adapter oferă o reutilizare eficientă, permiŃând interacŃiunea dintre<br />
două sau mai multe obiecte altfel incompatibile. Totuşi, este nevoie <strong>de</strong> multă<br />
planificare în ve<strong>de</strong>rea <strong>de</strong>zvoltării unui cadru suficient <strong>de</strong> flexibil pentru a fi adaptat<br />
convenabil. Această problemă are două aspecte: structura funcŃională şi traducerea<br />
parametrilor.<br />
Dacă există o nepotrivire funcŃională între apelul trimis către Framework şi<br />
Adaptee, Adapter-ul trebuie să administreze necesităŃile <strong>de</strong> apel ale interfeŃei Adaptee.<br />
invocând orice meto<strong>de</strong> necesare înainte <strong>de</strong> a trimite apelul către Framework.<br />
O altă provocare a Adapter-ului este aceea <strong>de</strong> transfer a parametrilor, <strong>de</strong> vreme<br />
ce parametrii trimişi nu sunt întot<strong>de</strong>auna compatibili între Framework şi Adaptee. In<br />
astfel <strong>de</strong> cazuri, Adapter-ul fie creează un obiect apropiat atunci când nu există nici un<br />
echivalent direct între cele doua medii, fie învăluie un obiect pentru a-1 face utilizabil<br />
<strong>de</strong> către interfaŃa Adaptee.<br />
• Variante ale şablonului<br />
Adaptorii sunt dinamici şi foarte rar se pot ve<strong>de</strong>a doi care să fie i<strong>de</strong>ntici. Există<br />
însă unele variante:<br />
- MulŃi-Adaptee Adapters - în funcŃie <strong>de</strong> <strong>proiectare</strong>a sistemului, poate fi foarte<br />
avantajos ca un Adapter să fie o parte a Framework-ului apelat. Un astfel <strong>de</strong> Adapter<br />
se comportă <strong>de</strong> obicei ca un intermediar între sistem şi mai multe interfeŃe Adaptee<br />
- Non-lnterface-based Adapters - folosirea interfeŃelor în Java face posibilă<br />
<strong>de</strong>zvoltarea unor adaptori flexibili. Dar nu este mereu posibilă utilizarea interfeŃelor.<br />
De exemplu, nu este posibilă atunci când se lucrează cu componente complete care nu<br />
implementează nici o interfaŃă. In aceste situaŃii, şablonul Adapter este folosit Iară<br />
interfeŃe. Aceste implementări sunt însă mult mai puŃin flexibile<br />
- o interfaŃă între apelant şi Adapter şi una între Adapter şi Adaptee - interfaŃa<br />
între apelant şi Adapter permite noilor adaptori să fie mai uşor adăugaŃi în sistem pe<br />
parcursul rulării. InterfaŃa între Adapter şi Adaptee permite interfeŃelor Adaptee să fie<br />
încărcate dinamic în momentul rulării<br />
• Şabloane asociate<br />
Printre şabloanele asociate se află:<br />
- Bridge - cu toate că şabloanele Bridge şi Adapter sunt similare, intenŃia lor<br />
este diferită. Şablonul Bridge separă partea abstractă a unei componente <strong>de</strong><br />
implementarea acesteia, permiŃând fiecăreia să fie schimbate in<strong>de</strong>pen<strong>de</strong>nt. Şablonul<br />
Adapter permite utilizarea unui obiect altfel incompatibil<br />
60
- Decorator - şablonul Adapter este folosit pentru a schimba interfaŃa unui<br />
obiect, păstrând aceeaşi funcŃionalitate. Şablonul Decorator lasă interfaŃa obiectului<br />
neschimbată, dar îi intensifică funcŃionalitatea<br />
- Proxy - atât şablonul Adapter cât şi Proxy pun la dispoziŃie o interfaŃă<br />
principală unui obiect. DiferenŃa este că interfaŃa şablonului Adapter este schimbată,<br />
în timp ce interfaŃa şablonului Proxy este aceea cu cea a obiectului<br />
- Business Delegate - acest şablon pot fi folosit ca un Proxy. El poate opera ca<br />
un Adapter pentru sisteme altfel incompatibile<br />
■ Exemplu<br />
Consi<strong>de</strong>rându-se un sistem curat <strong>de</strong>ja creat, se doreşte adăugarea <strong>de</strong> noi<br />
elemente. Pentru aceasta se foloseşte şablonul Adapter:<br />
interf ace Clean { public voie! makeClean ( ) ; }<br />
elass Office irr.plements Clean { public veid<br />
makeClean() {<br />
Systern. out .println ( "Oficiu curat") ;<br />
lass Workshcp implements Clean{<br />
public voie! makeClean () {<br />
System.out.printlnŃ"3irou curat");<br />
nterface Extra exteno.s Clean; public void takeCare ( i ;<br />
lass Facility implements Extra{ public void<br />
makeCleanŃ) {<br />
System.out.println("SpaŃiu <strong>de</strong> muncă curat"<br />
public void takeCare ( ) {<br />
System.out.println("S-a realizat treaba!")<br />
class Cest {<br />
static void Jobs (Extra job) {<br />
if (job insrancecf Clean) ((Clean)job).makeClean<br />
if ((Extra) job instar.ceof Extra) Ń (Extra)j ob)<br />
.takeCare ( ) ;<br />
public static void main(Scring[] args) {<br />
Extra e =■■- nevi Facility(); Jobs (e) ;<br />
Clean ci = new Office ( ) ;<br />
Clean c.2 =■- new Worksnop ( ) ;<br />
cl.m.akeCleanO ;<br />
c2.makeClean();<br />
e.makeClean() ;
IV.2. Bridge<br />
• Cunoscut şi ca Handle / Body<br />
• ProprietăŃi<br />
• Scop<br />
- Tip: structural<br />
- Nivel: componentă<br />
Să împartă o componentă complexă în două succesiuni ierarhice separate dar<br />
asociate: funcŃionarea abstractă şi implementarea internă. Acest lucru uşurează<br />
modificarea oricărui aspect al componentei.<br />
• Introducere<br />
Pentru <strong>de</strong>zvoltarea unei liste cu lucruri ce trebuie făcute. în managerul<br />
personal <strong>de</strong> informaŃii se doreşte flexibilitate în privinŃa felului în care este<br />
reprezentată această listă utilizatorului. în plus, se vrea o posibilitate <strong>de</strong> modificare a<br />
funcŃionalităŃii <strong>de</strong> bază a listei, lăsând utilizatorilor capacitatea <strong>de</strong> a alege între o listă<br />
neordonată, o listă secvenŃială sau o listă prioritară. Pentru a introduce această<br />
caracteristică în aplicaŃie, se <strong>de</strong>zvoltă un grup <strong>de</strong> clase, fiecare conŃinând o cale<br />
specifică <strong>de</strong> afişare a listei şi <strong>de</strong> organizare a informaŃiilor. Această soluŃie <strong>de</strong>vine<br />
repe<strong>de</strong> impracticabilă, <strong>de</strong>oarece există multe modalităŃi <strong>de</strong> afişare a listei şi căi <strong>de</strong><br />
stocare a informaŃiilor listei.<br />
Este mai bine să se separe lista cu lucrurile ce trebuie făcute. Şablonul Bridge<br />
face acest lucru prin <strong>de</strong>finirea a două clase sau interfeŃe care lucrează împreună.<br />
Pentru managerul <strong>de</strong> informaŃii, acestea sunt List şi Listlmpl. Clasa List reprezintă<br />
funcŃionalitatea <strong>de</strong> afişare, ea <strong>de</strong>legând stocarea informaŃiilor articolelor listei către<br />
implementarea ei <strong>de</strong> baza, clasa Listlmpl. Beneficiul acestei soluŃii este că se pot mixa<br />
şi potrivi clasele încât să producă o mai mare funcŃionalitate.<br />
• Utilizare<br />
Şablonul Bridge se foloseşte când:<br />
- se doreşte flexibilitate între partea abstractă şi implementarea unei<br />
componente, evitându-se o relaŃie statică între cele două<br />
- orice schimbare a implementării trebuie să fie invizibilă clienŃilor<br />
- se pot crea subclase, dar se doreşte administrarea separată a celor două<br />
aspecte ale sistemului<br />
• Descriere<br />
Elementele complexe dintr-un sistem pot uneori varia atât în ceea ce priveşte<br />
funcŃionalitatea lor externă, cât şi în privinŃa implementării lor <strong>de</strong> bază. In astfel <strong>de</strong><br />
cazuri, moştenirea este o soluŃie nedorită, <strong>de</strong> vreme ce numărul claselor ce trebuie<br />
create creşte. Două reprezentări şi implementări duc la patru clase ce trebuie<br />
<strong>de</strong>zvoltate. în timp ce trei conduc la nouă clase.<br />
62
în plus. moştenirea leagă o componentă într-un mo<strong>de</strong>l static, făcând-o dificil<br />
<strong>de</strong> modificat în viitor. Schimbarea unei componente reprezintă o provocare <strong>de</strong>oarece<br />
ea tin<strong>de</strong> să varieze pe măsură ce sistemul esie <strong>de</strong>z\oltat şi utilizat. Este <strong>de</strong> preferat să<br />
se găsească o cale dinamică pentru a varia ambele aspecte ale componentei.<br />
Aici intervine şablonul Bridge. El rezolvă problema prim <strong>de</strong>scompunerea<br />
componentei în cele doua aspecte. Cu două lanŃuri <strong>de</strong> moştenire separate - unul se<br />
ocupă cu funcŃionalitatea, iar celălalt cu implementarea - este mult mai uşor să se<br />
combine elementele fiecărei părŃi. Mai mult. cerinŃele <strong>de</strong> codare a şablonului Bridge<br />
fiind scăzute, rezultă un număr mic <strong>de</strong> clase ce trebuie scrise pe măsură ce numărul<br />
variaŃiilor creşte.<br />
• Implementare<br />
Diagrama <strong>de</strong> clase a şablonului Bridge este următoarea:<br />
Abstraction<br />
-Implementation implementation<br />
+void operationQ _____<br />
implementation.operalionlmpl()<br />
RefineAbstraction<br />
interface Implementation<br />
+void operationlmpl'()<br />
ConeretelmplementationA ConeretelmplementationB<br />
+void operationlmplQ +void operationlmplC<br />
Pentru implementarea şablonului Bridge se folosesc următoarele:<br />
- Abstraction - clasa ce <strong>de</strong>fineşte funcŃionarea abstractă pentru Bridge, punând<br />
la dispoziŃie comportamentul şi structura standard. ConŃine o referinŃă către o instanŃă<br />
a interfeŃei Implementation<br />
- RefineAbstraction - clasa ce extin<strong>de</strong> clasa Abstraction. conŃinând<br />
comportamente adiŃionale sau modificate<br />
- Implementation - interfaŃa ce reprezintă funcŃionalitatea <strong>de</strong> bază folosită <strong>de</strong><br />
instanŃele Abstraction<br />
- Concretelmplementation - clasa ce implementează interfaŃa Implementation.<br />
ConŃine comportamentul şi structura claselor Implementation<br />
63
• Beneficii şi inconveniente<br />
Şablonul Bridge oferă posibilitatea <strong>de</strong> a distribui obiecte cu implementare <strong>de</strong><br />
bază între multiple obiecte abstracte. Are o mare flexibilitate în ceea ce priveşte<br />
modificarea implementărilor, schimbările putând surveni iară acŃiuni necesare din<br />
partea clientului.<br />
Când se proiectează o aplicaŃie care foloseşte şablonul Bridge, este important<br />
să se cunoască exact ce responsabilităŃi aparŃin funcŃionării abstracte şi care aparŃin<br />
implementării interne. De asemenea, trebuie observat clar care este mo<strong>de</strong>lul <strong>de</strong> bază al<br />
implementării şablonului Bridge. O problemă care poate apărea în timpul utilizării<br />
şablonului Bridge este <strong>de</strong>zvoltarea implementării şablonului în jurul uneia sau mai<br />
multe variaŃii.<br />
• Variante ale şablonului<br />
Printre variantele şablonului Bridge se numără:<br />
- Automatic Bridges - unele implementări ale şablonului Bridge sunt construite<br />
astfel încât să varieze fără o acŃiune din partea utilizatorului, bazându-se în schimb pe<br />
informaŃia dată <strong>de</strong> aplicaŃie sau <strong>de</strong> platforma <strong>de</strong> operare pentru a se modifica<br />
- Shared Implementations - unele clase implementate în special cele tară stări -<br />
clase ce nu au o stare internă - pot fi distribuite între mai multe obiecte ale aplicaŃiei.<br />
In funcŃie <strong>de</strong> cât <strong>de</strong> larg sunt distribuite aceste clase pot fi implementate ca o interfaŃă<br />
- Single Implementation - uneori există doar o singură clasă implementată care<br />
serveşte mai multe clase abstracte. In acest caz, nu este necesar să se <strong>de</strong>finească o<br />
clasă <strong>de</strong> bază pentru partea <strong>de</strong> implementare a şablonului Bridge<br />
• Şabloane asociate<br />
Printre şabloanele asociate se află:<br />
- Adapter - şabloanele Bridge şi Adapter sunt similare ca structură, dar diferă<br />
ca intenŃie. Şablonul Bridge separă partea abstractă şi implementarea pentru a le<br />
permite să se modifice separat. Şablonul Adapter permite utilizarea unui obiect a cărui<br />
interfaŃă ar fi altminteri incompatibilă<br />
- Singleton - se foloseşte când implementarea claselor poate fi distribuită<br />
- Flyweight - când structura arborescentă <strong>de</strong>vine largă, aplicarea şablonului<br />
Flyweight poate ajuta la reducerea numărului <strong>de</strong> obiecte administrate <strong>de</strong> arbore<br />
• Exemplu<br />
Dacă se doreşte afişarea unei baze <strong>de</strong> date ce conŃine întrebări, în funcŃie <strong>de</strong><br />
dorinŃele utilizatorului, se poate utiliza şablonul Bridge:<br />
interface Question {<br />
public void nextQuest i ori ( ) ; public<br />
void newQuesrion(String q); public<br />
void displayQuesti.cn (); public voi.d<br />
displayAHQuestions ( ) ;<br />
64
class QuestionManager -.<br />
prctccred Ques:i:;:i qu-sdi;<br />
public String catilv,:;<br />
public Quest ionManager ; S ■; .-"in z ?-~do:i) {<br />
this. catalog = cazaLz::;<br />
}<br />
public voia next() { quesuii.nexLQuestion ( ) ; ■<br />
pudic void newOnc (5~ring ques~; •<br />
questDB.newQuestion'queso ; ;<br />
public void display() { questDB.displayQuestion(); }<br />
pudic void displayAll() {<br />
System.ou:.println("Question Catalog: " + catalog);<br />
questDB.displayAllQuestions() ;<br />
}<br />
i j<br />
class Quest-ioriFormat extends QuestionManager {<br />
pudic QuesrionForma~ ( String catalog) { super ( catalog) ; }<br />
pudic veid displayAllŃ) { super. displayAll (); }<br />
class JavaQuestions inplements Question {<br />
private List questions = new ArrayList();<br />
private int current = C; public JavaQuestions<br />
( ) {<br />
questions.add("Ce e Java?");<br />
questions.add("Ce e o interfaŃă?");<br />
questions.add("Ce e un fir <strong>de</strong> execuŃie?");<br />
public void nextQuestion() {<br />
if (current
■ Introducere<br />
Se doreşte modificarea managerului personal <strong>de</strong> informaŃii astfel încât el să<br />
permită utilizatorilor să administreze un proiect complex. Noile caracteristici includ<br />
<strong>de</strong>finirea proiectului ca un grup <strong>de</strong> sarcini şi subsarcini asociate, precum şi asocierea<br />
rezultatelor cu sarcinile. O cale naturală pentru în<strong>de</strong>plinirea acestui lucru este<br />
<strong>de</strong>finirea unei structuri arborescente, un<strong>de</strong> din sarcina rădăcină - care reprezintă<br />
proiectul în sine - se ramifică subproiecte. şi aşa mai <strong>de</strong>parte. Prin urmare, se<br />
<strong>de</strong>fineşte o clasa Task ce reŃine o colecŃie <strong>de</strong> alte obiecte <strong>de</strong> tip Task şi Deliverable<br />
(rezultat). De vreme ce acestea sunt legate <strong>de</strong> proiect, se <strong>de</strong>fineşte un părinte comun<br />
pentru ele - clasa Projectltem.<br />
Totuşi, ce se întâmplă dacă utilizatorii trebuie să execute o acŃiune care<br />
<strong>de</strong>pin<strong>de</strong> <strong>de</strong> tot arborele? De exemplu, un manager <strong>de</strong> proiect doreşte o estimare a<br />
timpului pentru realizarea sarcinilor şi livrarea rezultatelor pentru un anumit proiect.<br />
Pentru aceasta, trebuie scris cod pentru parcurgerea arborelui şi apelarea meto<strong>de</strong>lor<br />
potrivite la fiecare ramură. Este mult <strong>de</strong> muncă, implicând scrierea unui cod separat<br />
pentru parcurgerea arborelui, apelarea meto<strong>de</strong>lor şi colectarea rezultatelor. Având<br />
clase diferite - Task şi Deliverable - la fiecare ramură a arborelui, acestea trebuie<br />
manipulate diferit în ve<strong>de</strong>rea obŃinerii estimării timpului. Pentru un număr mare <strong>de</strong><br />
clase şi un arbore complex, codul <strong>de</strong>vine dificil <strong>de</strong> administrat.<br />
Există însă o cale mai bună <strong>de</strong> rezolvare a acestei probleme. Cu ajutorul<br />
şablonului Composite se poate folosi polimorfismul şi recursia pentru a pune la<br />
dispoziŃie o soluŃie eficientă şi simplă. Se începe prin <strong>de</strong>finirea unei meto<strong>de</strong> standard<br />
pentru toate clasele ce conŃin estimarea timpului, <strong>de</strong>numită getTimeRequired. Această<br />
metodă se <strong>de</strong>fineşte pentru interfaŃa Projectltem şi se implementează acel<br />
comportament în toate clasele care sunt tipuri ale interfeŃei Projectltem. Pentru<br />
obiectele Deliverable, metoda getTimeRequired se <strong>de</strong>fineşte astfel încât să returneze 0<br />
<strong>de</strong>oarece timpul nu este direct asociat cu un rezultat. Pentru clasa Task, se returnează<br />
un timp ce constă din timpul în care s-a efectuat sarcina plus suma apelurilor meto<strong>de</strong>i<br />
getTimeRequired pentru toate obiectele-fiu <strong>de</strong> tip Task.<br />
Folosind acest şablon, metoda getTimeRequired se <strong>de</strong>fineşte încât să calculeze<br />
automat timpul estimat fiecărei părŃi a arborelui. Se apelează metoda<br />
getTimeRequired doar pentru partea dorită a sarcinii şi codul meto<strong>de</strong>i in<strong>de</strong>plineşte<br />
funcŃia <strong>de</strong> parcurgere a arborelui şi <strong>de</strong> calcul a rezultatelor.<br />
• Utilizare<br />
Şablonul Composite se foloseşte când:<br />
- există o componentă mo<strong>de</strong>l cu o structură arborescentă<br />
- structura poate avea orice nivel <strong>de</strong> complexitate şi este dinamică<br />
- se doreşte tratarea uniformă a structuri componentelor, utilizând operaŃii<br />
comune în toată ierarhia<br />
• Descriere<br />
De multe ori se vrea <strong>de</strong>zvoltarea componentelor care urmează un mo<strong>de</strong>l parteîntreag.<br />
Acesta este un mo<strong>de</strong>l care permite tratarea unei colecŃii <strong>de</strong> obiecte i<strong>de</strong>ntice ca<br />
o entitate. Aceste structuri sunt flexibile şi uşor <strong>de</strong> folosit. Utilizatorii pot modifica<br />
structura pe măsură ce aplicaŃia rulează, adăugând sau eliminând anumite părŃi.<br />
66
Şablonul Composite suportă aceste caracteristici prin <strong>de</strong>finirea unei structuri<br />
<strong>de</strong> clase ce poate fi extinsă. Această structură este compusă dintr-o componentă <strong>de</strong><br />
bază. clase frunză şi clasa Composite. Componenta <strong>de</strong> bază conŃine mo<strong>de</strong>lul central,<br />
<strong>de</strong>finind meto<strong>de</strong>le standard sau variabilele ce vor fi folosite <strong>de</strong> toate obiectele <strong>de</strong> tip<br />
Composite. Clasele frunză conŃin comportamentul terminal. Hle reprezintă părŃi ale<br />
clasei Composite. fără a conŃine alte componente. Clasei Composite i se pot adăuga<br />
alte componente, permiŃând extin<strong>de</strong>rea structurii.<br />
Un <strong>de</strong>sen creat cu ajutorul unui instrument <strong>de</strong> editare grafică este un exemplu<br />
<strong>de</strong> şablon Composite. într-un <strong>de</strong>sen, un număr <strong>de</strong> forme elementare pot fi asociate şi<br />
tratate ca un întreg; se pot <strong>de</strong>fini <strong>de</strong> asemenea <strong>de</strong>sene care să conŃină alte <strong>de</strong>sene, sau<br />
o combinaŃie <strong>de</strong> <strong>de</strong>sene şi forme.<br />
• Implementare<br />
Diagrama <strong>de</strong> clase a şablonului Composite este următoarea:<br />
Component 0..*<br />
+void operation()<br />
i i.<br />
<<br />
No<strong>de</strong><br />
Composite<br />
-Component[] components<br />
+void addComponent(Component[] c)<br />
+void operationQ +void removeComponent(Component[] c)<br />
+void operation()<br />
Şablonul Composite are trei elemente:<br />
pentru flecare copil d i n componente c.operationQ<br />
- Component - interfaŃa care <strong>de</strong>fineşte meto<strong>de</strong>le disponibile pentru toate părŃile<br />
structurii arborescente. Ea poate fi implementată ca o clasă abstractă atunci când<br />
trebuie pus la dispoziŃie un comportament standard pentru toate subtipurile. în mod<br />
normal, această interfaŃă nu poate fi instanŃiată; subclasele ei sau clasele care o<br />
implementează, numite şi noduri, pot fi instanŃiate şi folosite la crearea unei colecŃii<br />
sau structuri arborescente<br />
- Composite - clasa <strong>de</strong>finită <strong>de</strong> componentele pe care le conŃine. Ea susŃine un<br />
grup dinamic <strong>de</strong> obiecte Component, prin urmare are meto<strong>de</strong> pentru adăugarea şi<br />
eliminarea instanŃelor Component din colecŃia sa. Meto<strong>de</strong>le <strong>de</strong>finite în interfaŃa<br />
Component sunt implementate încât să exercite comportamentul specific pentru tipul<br />
respectiv <strong>de</strong> Composite şi să apeleze aceeaşi metodă în fiecare din nodurile sale<br />
- Leaf - clasa care implementează interfaŃa Component, conŃinând o<br />
implementare pentru fiecare din meto<strong>de</strong>le acesteia. DiferenŃa dintre o clasa Leaf şi o<br />
clasa Composite este aceea că prima nu conŃine nici o referinŃă către alte interfeŃe<br />
Component<br />
67
Beneficii şi inconveniente<br />
Şablonul Composite pune la dispoziŃie o combinaŃie puternică: flexibilitate<br />
consi<strong>de</strong>rabilă a structurii şi o interfaŃă uşor <strong>de</strong> administrat. Structura poate fi<br />
schimbată în orice moment prin apelarea meto<strong>de</strong>i potrivite pentru adăugarea sau<br />
eliminarea unei componente. Deoarece componentele unui Composite pot fi<br />
schimbate înseamnă că este posibilă şi modificarea comportamentului acestuia.<br />
Utilizarea interfeŃelor creşte şi mai mult flexibilitatea. Acestea permit<br />
construcŃia cadrelor folosind şablonul Composite şi introducerea <strong>de</strong> noi tipuri la<br />
rulare. în acelaşi timp. utilizarea interfeŃelor poate fi un inconvenient atunci când se<br />
doreşte <strong>de</strong>finirea unor atribute şi a unei implementări <strong>de</strong> bază încât fiecare nod să<br />
poată moşteni comportamentul. în aceste cazuri, interfaŃa Component trebuie <strong>de</strong>finită<br />
ca o clasă abstractă.<br />
Un alt inconvenient vine chiar din flexibilitatea şablonului - <strong>de</strong>oarece acesta<br />
este atât <strong>de</strong> dinamic, <strong>de</strong>vine uneori dificil <strong>de</strong> testat. El necesită în mod normal o<br />
strategie <strong>de</strong> testare/validare mai sofisticată, construită în jurul conceptului <strong>de</strong> ierarhie<br />
<strong>de</strong> obiecte parte-întreg. Dacă testarea <strong>de</strong>vine o problemă, cea mai bună rezolvare este<br />
introducerea testării în cadrul clasei Composite.<br />
• Variante ale şablonului<br />
Unele variante ale şablonului Composite includ:<br />
- The Root No<strong>de</strong> - pentru creşterea administrării în cadrul sistemului, unele<br />
implementări ale şablonului Composite <strong>de</strong>finesc un obiect distinct care se comportă<br />
ca o bază pentru întreaga ierarhie <strong>de</strong> obiecte. Dacă obiectul-rădăcină este reprezentat<br />
printr-o clasă separată, el poate fi implementat ca un Singleton. sau accesul către el se<br />
poate face cu ajutorul unui Singleton<br />
- Rule-based Branching - pentru structuri Composite mai complexe, în special<br />
pentru cele cu multiple tipuri <strong>de</strong> noduri şi ramuri, este uneori necesară impunerea <strong>de</strong><br />
reguli privind cum şi când anumite tipuri <strong>de</strong> noduri pot fi unite cu anumite tipuri <strong>de</strong><br />
ramuri<br />
• Şabloane asociate<br />
Printre şabloanele asociate se află:<br />
- Chain of Responsibility - este utilizat împreună cu şablonul Composite atunci<br />
când meto<strong>de</strong>le trebuie propagate în arbore <strong>de</strong> la frunze către ramuri<br />
- Flyweight - când structura arborescentă <strong>de</strong>vine largă, prin aplicarea acestui<br />
şablon se poate reduce numărul <strong>de</strong> obiecte administrate <strong>de</strong> arbore<br />
- Iterator - este folosit împreună cu şablonul Composite pentru a încapsula<br />
parcurgerea arborelui, care altfel ar putea <strong>de</strong>veni complicată<br />
- Visitor - folosit împreună cu şablonul Composite pentru centralizarea<br />
comportamentului care altfel ar trebui împărŃit între clasele frunză şi ramuri<br />
- Composite View - acest şablon <strong>de</strong>scrie cum o ve<strong>de</strong>re poate fi compusă din<br />
mai multe ve<strong>de</strong>ri, şi aşa mai <strong>de</strong>parte<br />
68
• Exemplu<br />
Putem consi<strong>de</strong>ra o structură arborescentă <strong>de</strong> forma următoare: un manager<br />
general are un număr <strong>de</strong> angajaŃi şi unii dintre aceştia sunt la rândul lor manageri care<br />
au un număr <strong>de</strong> angajaŃi:<br />
class Enployee {<br />
St ring nane;<br />
doucle salary;<br />
Emp]oyee(String n, double s){<br />
nane - r:;<br />
salary = s;<br />
}<br />
String getName() { return name; }<br />
double getSalary() { return salary; }<br />
public String toString() { return "Emplcyee " + name; } }<br />
class Manager (<br />
Manager mgr;<br />
Enployee[] ely;<br />
String <strong>de</strong>pt;<br />
Manager(Manager mgr,Enployee[] e, String d ) {<br />
this(e, d);<br />
this.ngr = mgr; }<br />
Manager(Employee[] e, String d) {<br />
ely = e;<br />
<strong>de</strong>pt —a;<br />
String getDeptŃ) { return <strong>de</strong>pt; }<br />
Manager getManager() { return mgr; }<br />
Employee[] getEmployee() { return ely; }<br />
public String toString() { return <strong>de</strong>pt + " manager"; }<br />
IV.4. Decorator<br />
• Cunoscut şi ca Wrapper<br />
• ProprietăŃi<br />
Scop<br />
- Tip: structural<br />
- Nivel: componentă<br />
Să pună la dispoziŃie o cale flexibilă <strong>de</strong> adăugare şi eliminare a funcŃionalităŃii<br />
componentelor fără a le schimba infăŃişarea externă sau funcŃia.<br />
69
Introducere<br />
Şablonul Composite <strong>de</strong>scris anterior adaugă managerului personal <strong>de</strong><br />
informaŃii funcŃionalitate, prin intermediul unei ierarhii <strong>de</strong> obiecte Task şi<br />
Deliverable. Toate clasele implementau interfaŃa Projectltem. care le i<strong>de</strong>ntifica ca<br />
aparŃinând unui proiect. Dacă însă se doreşte extin<strong>de</strong>rea capacităŃilor <strong>de</strong> bază ale<br />
claselor Task şi Deliverable, adăugând caracteristici în plus, cum ar fi următoarele?<br />
- articole <strong>de</strong>pen<strong>de</strong>nte - o interfaŃă Projectltem care <strong>de</strong>pin<strong>de</strong> <strong>de</strong> un alt obiect<br />
Task sau Deliverable pentru se realiza<br />
- suport <strong>de</strong> documente - un obiect Task sau Deliverable care face referinŃe<br />
către documentaŃii adiŃionale<br />
Dacă se adaugă aceste caracteristici folosind subclase, trebuie să se codifice o<br />
multitudine <strong>de</strong> clase. De exemplu, pentru a face doar obiectele Deliverable să suporte<br />
aceste caracteristici, trebuie scrise patru clase: Deliverable, Depen<strong>de</strong>ntDeliverable,<br />
SupportedDeliverable şi SupportedDepen<strong>de</strong>ntDeliverable. în faŃa acestui<br />
inconvenient, compunerea obiectelor poate fi consi<strong>de</strong>rată în ve<strong>de</strong>rea adăugării noilor<br />
funcŃionalităŃi. însă se poate ajunge la cod duplicat în mai multe locaŃii. în cel mai<br />
bun caz, se creşte doar complexitatea codului.<br />
Dacă, în schimb, se scriu clase care să aibă capacităŃi plug-in? Se creează clase<br />
<strong>de</strong>pen<strong>de</strong>nte care pot fi ataşate oricărei interfeŃe Projectltem pentru a se extin<strong>de</strong><br />
funcŃionalitatea <strong>de</strong> bază. De exemplu, se <strong>de</strong>finesc clasele Depen<strong>de</strong>ntProjectltem şi<br />
SupportedProjectltem. Fiecare clasă conŃine doar codul necesar capacităŃii sale<br />
opŃionale, precum şi o referinŃă către interfaŃa Projectltem reală pe care o extin<strong>de</strong>.<br />
Acest lucru înseamnă mai puŃin cod <strong>de</strong> întreŃinut şi libertatea <strong>de</strong> a folosi orice<br />
combinaŃie a acestor clase <strong>de</strong>pen<strong>de</strong>nte pentru a adăuga noi grupuri <strong>de</strong> capacităŃi.<br />
• Utilizare<br />
Şablonul Decorator se foloseşte când:<br />
- se doreşte efectuarea <strong>de</strong> schimbări dinamice care să fie transparente<br />
utilizatorilor, fără restricŃiile impuse <strong>de</strong> subclasare<br />
- capacităŃile componentelor pot fi adăugate sau eliminate pe măsură ce<br />
sistemul rulează<br />
- există un număr <strong>de</strong> caracteristici in<strong>de</strong>pen<strong>de</strong>nte care trebuie aplicate dinamic<br />
şi care pot fi folosite în orice combinaŃie<br />
• Descriere<br />
Unele obiecte au o funcŃionalitate complexă şi/sau o structură care poate fi<br />
adăugată sau eliminată. Şablonul Decorator funcŃionează permiŃând straturilor să fie<br />
adăugate sau eliminate dintr-un obiect <strong>de</strong> bază. Fiecare strat poate conŃine<br />
comportament (meto<strong>de</strong>) şi stare (variabile).<br />
Şablonul Decorator este potrivit pentru aplicaŃii care implică supraîncărcarea<br />
ce poate fi construită dinamic. Produsele Groupware ce permit echipelor ce lucrează<br />
în reŃea să combine munca <strong>de</strong> editare a unui unic document <strong>de</strong> bază. este un exemplu.<br />
De asemenea, şablonul Decorator este potrivit pentru editoarele <strong>de</strong> imagini, precum şi<br />
pentru majoritatea aplicaŃiilor ce implică texte sau formatare <strong>de</strong> documente.<br />
70
• Implementare<br />
Diagrama <strong>de</strong> clase a şablonului Decorator este următoarea:<br />
ConcreteComponent<br />
+void operationQ<br />
interface Component<br />
+void operation(')<br />
component.operationQ<br />
Decorator<br />
-Component component<br />
+void setComponent(Component c)<br />
+void operationQ<br />
Pentru implementarea şablonului Decorator este nevoie <strong>de</strong>:<br />
ConcreteDecorator<br />
-int newAttribute<br />
+void operation()<br />
+void newOperation()<br />
- Component - reprezintă comportamentul generic al unei componente. Poate<br />
fi o clasă abstractă sau o interfaŃă<br />
- Decorator - <strong>de</strong>fineşte comportamentul standard care se aşteaptă din partea<br />
tuturor Decorator-ilor. Poate fi o clasă abstractă sau o interfaŃă. ConŃine o referinŃă<br />
către un obiect Component, care poate fi un obiect ConcreteComponent sau un alt<br />
Decorator<br />
- ConcreteDecorator - fiecare subclasă a Decorator-ului trebuie să suporte<br />
înlănŃuirea (referinŃa către o componentă, plus abilitatea <strong>de</strong> a adăuga şi elimina<br />
această referinŃă)<br />
• Beneficii şi inconveniente<br />
Şablonul Decorator oferă oportunitatea <strong>de</strong> a ajusta uşor comportamentul unui<br />
obiect în timpul rulării. In plus. codarea poate <strong>de</strong>veni mult mai uşoară, <strong>de</strong> vreme ce<br />
trebuie scrise o serie <strong>de</strong> clase, fiecare Ńintind o funcŃionalitate specifică, în loc <strong>de</strong> a<br />
coda toate comportamentele în cadrul componentei însăşi. Acest lucru face<br />
componenta mult mai uşor <strong>de</strong> extins în viitor <strong>de</strong>oarece modificările pot fi introduse<br />
codând noi clase.<br />
în funcŃie <strong>de</strong> comportamentul lor, unele straturi ale şablonului Decorator pot fi<br />
distribuite între multiple obiecte ale componentei. Astfel se reduce memoria<br />
consumată <strong>de</strong> sistem.<br />
Folosit până la extrem, şablonul Decorator produce <strong>de</strong> regulă un număr mare<br />
<strong>de</strong> straturi, adică între utilizator şi obiectul real există mai multe obiecte mici. Acest<br />
lucru are un număr <strong>de</strong> consecinŃe. Testarea codului <strong>de</strong>vine mai dificilă iar viteza <strong>de</strong><br />
operare a unui sistem se reduce daca Decorator-ul este proiectat impropriu.<br />
71
Trebuie asigurată tratarea în mod egal a obiectelor. Aceasta este importantă<br />
pentru şablonul Decorator, <strong>de</strong> vreme ce straturile obiectului stau fiecare "în faŃa"<br />
celuilalt. Dacă egalitatea este testată în cadrul aplicaŃiei, atunci trebuie scrisă o<br />
operaŃie care să i<strong>de</strong>ntifice obiectul <strong>de</strong> bază sau combinaŃia dintre acesta şi ordinul şi<br />
valorile fiecărui strat.<br />
■ Variante ale şablonului<br />
De multe ori este <strong>de</strong> dorit <strong>de</strong>zvoltarea claselor şablonului Decorator cu o<br />
referinŃă înainte şi una înapoi pentru a le face mai uşor <strong>de</strong> înlăturat în timpul rulării<br />
sistemului.<br />
Unele implementări ale şablonului nu folosesc un Decorator abstract. In mod<br />
normal, această variantă este folosita atunci când există doar o singură variaŃie<br />
posibilă a componentei.<br />
Se pot crea Decoratori suprascrişi care vor re<strong>de</strong>fmi unele părŃi ale<br />
comportamentului componentei. Acest lucru trebuie să se facă însă cu grijă <strong>de</strong>oarece<br />
componentele care se bazează pe şablonul Decorator pot manifesta comportamente<br />
imprevizibile.<br />
• Şabloane asociate<br />
Printre şabloanele asociate se află:<br />
- Adapter - acest şablon este folosit pentru schimbarea interfeŃei asupra<br />
aceleiaşi funcŃionalităŃi. în timp ce şablonul Decorator lasă interfaŃa la fel dar schimbă<br />
funcŃionalitatea<br />
- Composite - şablonul Decorator poate fi văzut ca o versiune mai simplă a<br />
şablonului Composite; în loc <strong>de</strong> avea o colecŃie <strong>de</strong> componente, Decorator-ul<br />
păstrează o singură referinŃă către o altă componentă. Cealaltă diferenŃăă este că<br />
Decorator-ul măreşte funcŃionalitatea<br />
- Strategy - şablonul Decorator este utilizat pentru a modifica sau extin<strong>de</strong><br />
funcŃionalitatea externă a unui obiect, în timp ce şablonul Strategy modifică<br />
comportamentul intern al obiectului<br />
■ Exemplu<br />
Consi<strong>de</strong>răm o clasă care afişează un număr şi utilizăm şablonul Decorator care<br />
să adauge un text numărului respectiv:<br />
class Nuir.ber {<br />
public void prinŃ() {<br />
System, out. println (r.ew Ranaom ( ) . nextlnt Ń ) ) ;<br />
class Decorator {<br />
public Decorator() {<br />
System.out.prinŃ{"Număr aleator: " ) ;<br />
new Number() .prinŃ(} ;<br />
72
ciass Sub'\uinber exter.ds Nuiiberi<br />
public SubNumber{) {<br />
super();<br />
Systen.ont.prinŃŃ"Număr aleator: ");<br />
prinŃ();<br />
IV,5. Faca<strong>de</strong> •<br />
ProprietăŃi<br />
• Scop<br />
- Tip: structural<br />
- Nivel: componentă<br />
Să pună la dispoziŃie o interfaŃă simplificată unui grup <strong>de</strong> subsisteme sau unui<br />
subsistem complex.<br />
• Introducere<br />
Utilizatorii doresc să poată modifica o interfaŃă grafică pentru a o face mai<br />
atractivă sau mai uşor <strong>de</strong> utilizat. De exemplu, unii utilizatori pot avea o slăbiciune<br />
vizuală sau nu pot citi un font mic, <strong>de</strong>ci trebuie să mărească dimensiunea font-ului.<br />
ForŃând utilizatorul să treacă prin toŃi paşii setup-ului, interfaŃă grafică afişând<br />
<strong>de</strong>taliile în font-ul mic, nu este un lucru prea favorabil. Un wizard care să-i ajute pe<br />
cei cu <strong>de</strong>ficienŃe vizuale să treacă prin etapele setup-ului, este mult mai bun.<br />
Acest ajutor nu trebuie să limiteze opŃiunile <strong>de</strong> utilizare şi modificare a<br />
aplicaŃiei. Se doreşte punerea la dispoziŃie a unei modalităŃi <strong>de</strong> vizualizare specializată<br />
a sistemului, în acelaşi timp menŃinându-se toate caracteristicile. Acesta este şablonul<br />
Faca<strong>de</strong> - un wizard al sistemului.<br />
• Utilizare<br />
Şablonul Faca<strong>de</strong> se foloseşte pentru:<br />
- a face sistemele complexe mai uşor <strong>de</strong> utilizat prin intermediul unei interfeŃe<br />
simple, fără a elimina opŃiunile avansate<br />
- a reduce cuplarea dintre clienŃi şi subsisteme<br />
- a stratifica subsistemele<br />
• Descriere<br />
Multe din sistemele <strong>software</strong> mo<strong>de</strong>rne sunt foarte complexe. Şabloanele ajută<br />
la structurarea aplicaŃiilor şi la o mai bună tratare a complexităŃii. De multe ori se<br />
realizează acest lucru prin împărŃirea funcŃionalităŃii între o serie <strong>de</strong> clase mai mici.<br />
Clasele adiŃionale pot fi <strong>de</strong> asemenea produse ca rezultat al partiŃionării sistemului.<br />
Divizarea unui sistem în mai multe subsisteme ajută la tratarea mai uşoară a<br />
complexităŃii sistemului şi pune la dispoziŃie oportunitatea <strong>de</strong> împărŃire a muncii.
Separând un sistem într-un număr <strong>de</strong> clase specializate este o practică hună <strong>de</strong><br />
<strong>proiectare</strong> orientată pe obiecte. Totuşi, faptul că există un număr mare <strong>de</strong> clase în<br />
sistem, poate fi un inconvenient.<br />
ClienŃii care folosesc sistemul trebuie să lucreze cu mai multe obiecte.<br />
Utilizatorii tind să <strong>de</strong>vină confuzi când au sute <strong>de</strong> opŃiuni <strong>de</strong> configurare. Prin urmare,<br />
majoritatea opŃiunilor trebuie presetate. clientul urmând să aibă doar câteva opŃiuni <strong>de</strong><br />
bază <strong>de</strong> configurat. Şablonul Faca<strong>de</strong> pune la dispoziŃie aceste opŃiuni şi poate<br />
<strong>de</strong>termina ce subsisteme trebuie apelate.<br />
în mod normal. Faca<strong>de</strong> va <strong>de</strong>lega majoritatea muncii către subsisteme, dar<br />
poate în<strong>de</strong>plini anumite atribuŃiuni el însuşi. IntenŃia lui este <strong>de</strong> realiza o interfaŃă<br />
simplă cu un set <strong>de</strong> sisteme, dar clienŃii care doresc opŃiunile mai avansate pot<br />
interacŃiona cu subsistemele.<br />
• Implementare<br />
Diagrama <strong>de</strong> obiecte a şablonului Faca<strong>de</strong> este următoarea:<br />
:Subsystem<br />
:Faca<strong>de</strong> :Faca<strong>de</strong><br />
:Subsystem :Subsvstem :Subsystem<br />
:Subsystem<br />
Pentru implementarea şablonului Faca<strong>de</strong> se folosesc următoarele:<br />
- Faca<strong>de</strong> - clasa pe care o folosesc clienŃii. Cunoaşte subsistemele pe care le<br />
utilizează şi responsabilităŃile acestora. în mod normal, toate cerinŃele clientului vor fi<br />
trimise către subsistemul cel mai potrivit<br />
- Subsystem - set <strong>de</strong> clase care poate fi folosit direct <strong>de</strong> clienŃi sau care poate<br />
în<strong>de</strong>plini munca trimisă către el <strong>de</strong> Faca<strong>de</strong>. Pentru subsistem. Faca<strong>de</strong> reprezintă un<br />
simplu client<br />
74
■ Beneficii şi inconveniente<br />
Beneficiul şablonul Faca<strong>de</strong> este acela că el realizează o interfaŃă simplă cu un<br />
sistem complex tară a reduce opŃiunile puse la dispoziŃie <strong>de</strong> sistem. Această interfaŃă<br />
protejează clientul <strong>de</strong> o abun<strong>de</strong>nŃă <strong>de</strong> opŃiuni.<br />
Şablonul Faca<strong>de</strong> trimite cerinŃele clientului către subsistemele care le pot<br />
rezolva. De regulă, o cerinŃă va fi trimisă către mai multe subsisteme. Deoarece<br />
clientul interacŃionează doar cu Faca<strong>de</strong>. activitatea internă a sistemului se poate<br />
schimbă. în timp ce clientul rămâne neschimbat pentru Faca<strong>de</strong>.<br />
Şablonul Faca<strong>de</strong> poate fi folosit pentru a reduce cuplarea între subsisteme.<br />
Fiecare subsistem poate avea propriul său Faca<strong>de</strong> iar alte părŃi ale sistemului folosesc<br />
Faca<strong>de</strong> pentru a comunica cu subsistemele.<br />
■ Variante ale şablonului<br />
Şablonul Faca<strong>de</strong> se poate implementa ca o interfaŃă sau o clasă abstractă. Se<br />
lasă astfel <strong>de</strong>taliile privind implementarea pe mai târziu.<br />
Mai multe şabloane Faca<strong>de</strong> pot pune la dispoziŃie diferite şabloane pentru<br />
acelaşi set <strong>de</strong> subsisteme.<br />
Şablonul Faca<strong>de</strong> este uneori variat pentru a ascun<strong>de</strong> subsistemele. Când este<br />
folosit ca o legătură între subsistemele arhitecturii, unul din scopurile sale este să<br />
reducă complexitatea interacŃiunii sistem-sistem. De exemplu, un sistem un<strong>de</strong><br />
apelurile trec printr-un Faca<strong>de</strong> central este mai uşor <strong>de</strong> administrat <strong>de</strong>cât unul cu un<br />
număr mare <strong>de</strong> clase cuplate.<br />
• Şabloane asociate<br />
Printre şabloanele asociate se află:<br />
- Abstract Factory - acest şablon creează familii <strong>de</strong> obiecte asociate. Pentru a<br />
simplifica accesul la diferite obiecte care se creează, se poate crea <strong>de</strong> asemenea un<br />
obiect Faca<strong>de</strong><br />
- Mediator - şabloanele Mediator şi Faca<strong>de</strong> par foarte similare. DiferenŃa este<br />
în intenŃia şi implementarea lor. Mediator-ul ajută la uşurarea comunicării între<br />
componente şi a adăugării <strong>de</strong> comportamente. Faca<strong>de</strong> este doar o abstractizare a<br />
interfeŃei unuia sau mai multor subsisteme<br />
- Singleton - Faca<strong>de</strong> foloseşte şablonul Singleton pentru a garanta un singur<br />
punct <strong>de</strong> acces global la un subsistem<br />
- Session Faca<strong>de</strong> - acest şablon este un Faca<strong>de</strong> care încapsulează<br />
complexitatea lui Enterprise JavaBeans, pentru a simplifica interfaŃa acesteia cu<br />
clienŃii<br />
• Exemplu<br />
Un bun exemplu al şablonului Faca<strong>de</strong> este JDBC care este folosit pentru<br />
conexiunea la o bază <strong>de</strong> date şi pentru a manipula datele tară a expune <strong>de</strong>talii<br />
clienŃiilor:<br />
interface General { public void accessGenerai ( ) ; }<br />
interface Scecial extends General { publi.c void<br />
accessSpecial ( ) ; }<br />
75
interiace Private exLencis Genera] {<br />
r)ub] ic void accsssPnva:e ; j ;<br />
}<br />
class Generai.Tnfo inplements General<br />
cu'clic void accessGeneral() {<br />
class Soecrailnfo irnplements Speciali<br />
public void accessSpecial() { /<br />
public void accessGeneral;) {}<br />
lass Privatelnfo implements Private, Special {<br />
public voia aecessPrivate() { //... }<br />
public voia accessSpecial() { //... }<br />
public void accessGeneral() { //... }<br />
i<br />
ciass Connection {<br />
.<br />
user is unauthorized) tnrcw new Exceptiom<br />
if {user is general) return new Generallnfo();<br />
if (user is special) return new Speciallnfo();<br />
if (user is executive) return new Privatelnfoi<br />
IV.6. Flyweight<br />
• ProprietăŃi<br />
• Scop<br />
- Tip: structural<br />
- Nivel: componentă<br />
Să reducă numărul <strong>de</strong> obiecte <strong>de</strong> nivel scăzut dintr-un sistem prin intennediul<br />
distribuirii <strong>de</strong> obiecte.<br />
• Introducere<br />
Programarea orientată pe obiecte permite mai multor obiecte să existe pe<br />
perioada executării, în special dacă sunt obiecte <strong>de</strong> nivel scăzut. Se plasează astfel o<br />
incărcatură mare în memoria maşinii virtuale Java.<br />
Multe obiecte din managerul personal <strong>de</strong> informaŃii pot fi editate, <strong>de</strong>ci folosesc<br />
şablonul State pentru a <strong>de</strong>termina dacă conŃinutul articolelor va fi salvat. Fiecare din<br />
aceste articole poate avea propria sa colecŃie <strong>de</strong> obiecte State.<br />
O cale <strong>de</strong> a uşura problema <strong>de</strong> a avea mai multe obiecte, este distribuirea<br />
acestora. Multe din aceste obiecte <strong>de</strong> nivel scăzut diferă foarte puŃin. în vreme ce<br />
starea şi comportamentul lor sunt în cele mai multe cazuri i<strong>de</strong>ntice. Distribuirea<br />
instanŃelor reduce consi<strong>de</strong>rabil numărul lor, tară a se pier<strong>de</strong> ceva din funcŃionalitate.<br />
Pentru un set <strong>de</strong> obiecte, şablonul Flyweight separă acele părŃi ale obiectelor care sunt<br />
la fel, <strong>de</strong> părŃile care sunt diferite.<br />
76
• Utilizare<br />
Şablonul Flyweight se foloseşte atunci când:<br />
- aplicaŃia foloseşte mai multe obiecte i<strong>de</strong>ntice sau aproape i<strong>de</strong>ntice<br />
- pentru fiecare din obiectele aproape i<strong>de</strong>ntice, părŃile care diferă pot fi<br />
separate, permiŃând părŃii i<strong>de</strong>ntice să fie distribuită<br />
- grupuri <strong>de</strong> obiecte aproape i<strong>de</strong>ntice pot fi înlocuite <strong>de</strong> un obiect distribuit, o<br />
data ce părŃile diferite ale stării au fost eliminate<br />
- aplicaŃia trebuie să distingă între obiectele aproape i<strong>de</strong>ntice aflate în starea<br />
lor originală<br />
■ Descriere<br />
Şablonul Flyweight intenŃionează să reducă numărul obiectelor dintr-o<br />
aplicaŃie şi face aceasta prin distribuirea obiectelor. Obiectele conŃin date interne, dar<br />
datele privind contextul în care ele operează, sunt furnizate <strong>de</strong> o sursă externă. Fiecare<br />
obiect distribuit trebuie să fie pe cât <strong>de</strong> generic posibil şi in<strong>de</strong>pen<strong>de</strong>nt <strong>de</strong> context.<br />
Distribuind obiecte, şablonul Flyweight reduce semnificativ numărul acestora.<br />
Obiectul distribuit e folosit <strong>de</strong> mai mulŃi clienŃi şi se distinge clar <strong>de</strong> un obiect care nu<br />
este distribuit.<br />
Un exemplu <strong>de</strong> utilizare a şablonului Flyweight este un manager <strong>de</strong> plan.<br />
Atunci când se construieşte o interfaŃă grafică cu utilizatorul, se folosesc mai multe<br />
componente. Se utilizează prin urmare managerele <strong>de</strong> plan. în general, acestea sunt<br />
aproape i<strong>de</strong>ntice. Ele diferă doar în ceea ce priveşte componentele specifice pe care le<br />
administrează şi un anumit set <strong>de</strong> atribute. Daca se înlătură aceste componente şi<br />
atribute, fiecare instanŃă a acestor managere este i<strong>de</strong>ntică. Având un obiect distribuit<br />
pentru fiecare manager <strong>de</strong> plan. se reduce numărul total <strong>de</strong> obiecte.<br />
ClienŃii care folosesc obiectul distribuit au responsabilitatea <strong>de</strong> a pune la<br />
dispoziŃie şi/sau a calcula contextul informaŃional. InformaŃia este trimisă către<br />
obiectul distribuit atunci când este nevoie. Un Flyweight este distribuit, <strong>de</strong>ci un client<br />
nu trebuie să creeze un Flyweight direct. Insă nu toate obiectele Flyweight trebuie<br />
distribuite. Acest şablon permite distribuirea obiectelor, dar nu cere în mod expres<br />
acest lucru. El se foloseşte doar atunci când se pot i<strong>de</strong>ntifica şi extrage uşor datele<br />
externe din obiecte şi când numărul diferitelor stări este limitat.<br />
Implementare<br />
Diagrama <strong>de</strong> clase a şablonului Flyweight este următoarea:<br />
FlyweightFactory 0.. *<br />
►<br />
interface Flyweight<br />
+Flyweight getFlyweight(Object Key) +void someOperation(Object externalState)<br />
i<br />
i<br />
Client<br />
ConcreteFIyweight<br />
+void someOperation(Object externai State)<br />
77
Pentru implementarea şablonului Flyweight este nevoie <strong>de</strong>:<br />
- Flyweight - interfaŃa care <strong>de</strong>fineşte meto<strong>de</strong>le pe care clienŃii le pot folosi<br />
pentru trimiterea stării interne către obiectele flyweight<br />
- ConcretcFlyweight - clasa ce implementează interfaŃa Flyweight şi abilitatea<br />
<strong>de</strong> a stoca datele interne. Datele interne trebuie să fie reprezentative pentru toate<br />
instanŃele un<strong>de</strong> este necesar Flyweight<br />
- FlyweightFactory - clasa responsabilă pentru crearea şi administrarea<br />
obiectelor Flyweight. Ea poate crea aceste obiecte la începutul aplicaŃiei sau atunci<br />
când este nevoie <strong>de</strong> ele<br />
- Client - clasa responsabilă pentru crearea şi punerea la dispoziŃie a<br />
contextului pentru obiectele flyweight. Singura cale <strong>de</strong> a obŃine o referinŃă către un<br />
obiect flyweight este prin intermediul clasei FlyweightFactory<br />
• Beneficii şi inconveniente<br />
Beneficiul evi<strong>de</strong>nt al acestui şablon este acela că reduce numărul <strong>de</strong> obiecte ce<br />
trebuie administrate. Se salvează astfel spaŃiu, atât în memorie cât şi pe dispozitivul<br />
<strong>de</strong> stocare. Cel mai mult spaŃiu se va salva atunci când contextul informaŃional pentru<br />
flyweight este compilat în loc <strong>de</strong> a fi stocat. Totuşi, acest lucru conduce şi la<br />
inconvenientul acestui şablon: rulare costisitoare.<br />
în loc <strong>de</strong> a stoca multe obiecte, clienŃii trebuie acum să calculeze contextul şi<br />
să pună rezultatul la dispoziŃia flyweight-ului. Acesta foloseşte apoi informaŃia pentru<br />
a compila funcŃii. Administrarea unui număr mai mic <strong>de</strong> obiecte duce la creşterea<br />
performanŃelor rulării dacă implementarea s-a făcut corect.<br />
• Şabloane asociate<br />
Printre şabloanele asociate se află:<br />
- Abstract Factory - acest şablon este folosit pentru a se asigura o distribuire<br />
corectă a instanŃelor Flyweight<br />
- Composite - este <strong>de</strong> regulă folosit pentru a pune la dispoziŃie structura<br />
- State - este implementat <strong>de</strong> regulă folosind şablonul Flyweight<br />
- Strategy - este un alt şablon care poate beneficia <strong>de</strong> pe urma implementării<br />
lui ca un Flyweight<br />
• Exemplu<br />
Un exemplu al şablonului Flyweight este crearea a 1000 <strong>de</strong> cercuri folosind 6<br />
culori, ce conŃine obiectul Circle care poate fi reutilizat:<br />
class Circle {<br />
private Color color; public<br />
Circle(Color color) {<br />
this.color = color;<br />
public void draw (Graphics g, inc x, irit y, irit r) {<br />
g.setColor(color); g.drawOva1(x, y, r, r);<br />
78
dss lest extends JFra.te;<br />
private static final Coicr ^ -_■". !■ r .■= L J = i Celor, red,<br />
Color.blue, Color . ye-llcv:, Color . orange , Color.black,<br />
Color.whice i; private s':ati.c final int WIDTH = 400,<br />
HEIGHT = 400,<br />
NUMRER_OF_CIRCLES = 1000;<br />
public Tes;() {<br />
Container contentPane = gotConteniPane();<br />
JButton button = new J But tor: ( "Draw Circle"!;<br />
final JPane.l panel = nev; JPanel ( ) ;<br />
contentPane.add(panel, Bor<strong>de</strong>rlayout.CENTER);<br />
contentFane.add(button, Bor<strong>de</strong>rLayout.SOUTH);<br />
setSize(WIDTH ,HEIGHT);<br />
ser Defau.1 tClcseOperation ( JFrane . EXIT _ON_CLOSE ) ;<br />
setVisibie(true);<br />
button.addActionListener(nev; ActionLisrener() {<br />
public veid act ionPerf or.T.ed (ActionEvent event) {<br />
Graphics g = panel.getGraphics(); for (int i = 0;<br />
i < NUM3ER_OF_CIRCLES; + + i) { Circle circle = new<br />
Circle(getRandomCclor());<br />
circle . draw ( g, getRandorr.X ( ) ,<br />
ge~RandomY(), getRandomR());<br />
i /<br />
private int getRandonX{; {<br />
return (irit) (Math . random Ń ) *WIDTH ) ; }<br />
private int getRandomY() {<br />
return (int)(Math.random()*HEIGKT); }<br />
private int getRandomR() {<br />
return (int) (Math. random () "" (HEIGHT/10) ) ;<br />
private Color getRandomCo]or() {<br />
return colors[ ( int) (Math.random()*colors.length) }<br />
public static void main(String[] args) {<br />
Test test = new Test();<br />
IV.7. Half-Object Plus Protocol (HOPP)<br />
• ProprietăŃi<br />
• Scop<br />
- Tip: structural<br />
- Nivel: componentă<br />
Să pună la dispoziŃie o singură entitate care să trăiască în doua sau mai multe<br />
spaŃii <strong>de</strong> adresă.<br />
79
• Introducere<br />
O aplicaŃie Java poate împrăştia obiecte către diferite spaŃii <strong>de</strong> adresă - maşini<br />
virtuale Java multiple. Pentru unele tehnologii cum ar fi RMI. obiectele la distanŃă pot<br />
apela meto<strong>de</strong> în legătură cu obiecte care se află într-o altă maşină virtuală Java.<br />
permiŃând distribuirea stării şi comportamentului. Indiferent <strong>de</strong> tehnologia folosită,<br />
obiectele din diferitele maşini virtuale Java trebuie să comunice între ele pentru ca o<br />
aplicaŃie distribuită să poată funcŃiona. Dacă nu se realizează acest lucru, apare un<br />
eşec în comunicare.<br />
Presupunem că există maşinile A şi B, fiecare cu o maşină virtuală Java care<br />
rulează. Un obiect din maşina A are nevoie ca un obiect din maşina B să apeleze<br />
meto<strong>de</strong> asupra unui obiect din maşina virtuală Java B. Acest lucru se poate realiza<br />
prin mai multe mijloace. Maşina A poate apela meto<strong>de</strong> ale căror apeluri vor fi trimise<br />
către maşina B<br />
Dezavantajul este că toate apelurile <strong>de</strong> meto<strong>de</strong> vor fi trimise <strong>de</strong>-a lungul<br />
reŃelei, lucru nedorit. De multe ori se doreşte ca unele meto<strong>de</strong> invocate să se execute<br />
local, fără a se merge la obiectul <strong>de</strong> la distanŃă.<br />
Proxy-ul - reprezentarea locală a unui obiect la distanŃă - este consi<strong>de</strong>rat ca<br />
tăcând parte din obiectul la distanŃă. Executând meto<strong>de</strong>le local şi în cadrul maşinii<br />
virtuale Java la distanŃă, un obiect exercită comportament în multiple spaŃii <strong>de</strong> adresă.<br />
Şablonul HOPP realizează acest lucru.<br />
• Utilizare<br />
Şablonul HOPP se foloseşte când:<br />
- un obiect trebuie să fie în două spaŃii <strong>de</strong> adresă diferite şi nu poate fi împărŃit<br />
- o parte a funcŃionalităŃii trebuie executată la distanŃă dar unele meto<strong>de</strong> sunt<br />
apelate local<br />
- optimizări trebuie efectuate printr-o modalitate transparentă apelantului<br />
• Descriere<br />
AplicaŃiile distribuite sunt greu <strong>de</strong> scris. Una din problemele întâlnite este<br />
aceea că o singură entitate (obiect) trebuie să se afle în mai multe spaŃii <strong>de</strong> adresă, fie<br />
pentru că trebuie să acceseze multiple dispozitive fizice aflate pe maşini diferite, fie<br />
pentru că un obiect nu poate fi împărŃit logic.<br />
Pentru a diviza un obiect în două părŃi care să comunice apoi la distanŃă, se<br />
execută rmic împreună cu opŃiunea keepgenerated. Astfel se salvează codul sursă.<br />
Apoi. se editează sursa încât anumite meto<strong>de</strong> să nu fie trimise către obiectul la<br />
distanŃă. Din păcate, se limitează folosirea ulterioara a rmic-ului, <strong>de</strong>oarece <strong>de</strong> fiecare<br />
dată când acesta se utilizează asupra obiectului la distanŃă, trebuie făcută şi editarea<br />
manuală.<br />
SoluŃia este <strong>de</strong> a împărŃi obiectul în două şi a pune la dispoziŃie o cale <strong>de</strong><br />
comunicare între cele două jumătăŃi. Se implementează fiecare parte astfel încât să<br />
interacŃioneze cu obiectul aflat în spaŃiul său <strong>de</strong> adresă. Protocolul este responsabil<br />
pentru sincronizarea celor două jumătăŃi şi trimiterea informaŃiilor înainte şi înapoi.<br />
Aceasta se realizează prin intermediul şablonului HOPP - se creează un obiect<br />
care implementează interfaŃa necesară şi care conŃine o referinŃă către obiectul la<br />
distantă.<br />
80
Client<br />
■ Implementare<br />
Diagrama <strong>de</strong> clase a şablonului MOPP este următoarea:<br />
interfacc HOPP<br />
+void remo1eMelhod()<br />
+void local MethodQ<br />
LocalHOPP<br />
+void remoteMethodQ<br />
+void localMethodQ<br />
RemoteObjeetProxy<br />
+void remoteMethod()<br />
+void localMethod()<br />
RemoteObjeet<br />
+void remoteMethod()<br />
+void localMethod()<br />
Acest obiect există<br />
într-un spaŃiu <strong>de</strong><br />
adresă diferit<br />
Pentru implementarea şablonului HOPP este nevoie <strong>de</strong>:<br />
- HOBB - interfaŃa care <strong>de</strong>fineşte meto<strong>de</strong>le valabile clientului. Ambele părŃi<br />
ale obiectului HOPP implementează această interfaŃă<br />
- LocalHOPP - clasa care implementează interfaŃa HOPP. Unele meto<strong>de</strong> sunt<br />
executate local. în timp ce altele sunt trimise către RemoteObjeetProxy<br />
- RemoteObjeetProxy - această clasă este un Proxy la distanŃă care trimite<br />
toate cererile către cealaltă parte a obiectului aflat în celălalt spaŃiu <strong>de</strong> adresă. Acest<br />
proxy încapsulează protocolul care leagă cele două jumătăŃi ale obiectului<br />
- RemoteObjeet - această jumătate a obiectului HOPP conŃine toate meto<strong>de</strong>le<br />
ce vor fi executate la distanŃă<br />
- Client - clasa care apelează meto<strong>de</strong>le interfeŃei HOPP. Aceste apeluri ale<br />
meto<strong>de</strong>lor sunt transparente clientului, indiferent dacă se foloseşte un Proxy la<br />
distanŃă, un HOPP sau un obiect local<br />
• Beneficii şi inconveniente<br />
Beneficiul acestui şablon este acela că permite ca un obiect să ocupe două<br />
spaŃii <strong>de</strong> adresă. Pentru clienŃii care folosesc o parte a obiectului HOPP. el este<br />
transparent. ClienŃilor nu le pasă dacă un obiect se află în unul sau mai multe spaŃii <strong>de</strong><br />
adresă.<br />
Este posibilă şi ascun<strong>de</strong>rea completă a diferenŃelor. încât un client să creadă că<br />
foloseşte un obiect local, în timp ce părŃi ale acestuia se află la distanŃă. Se poate<br />
implementa şi partea opusă astfel încât un client să creadă că foloseşte un Proxy la<br />
distanŃă, când <strong>de</strong> fapt foloseşte un HOPP care conŃine un Proxy la distantă.<br />
81
Un alt mare avantaj este că acest şablon permite optimizări. Fiecare parte a<br />
obiectului HOPP poate <strong>de</strong>termina când şi cum doreşte să comunice cu cealaltă parte.<br />
Aceste strategii <strong>de</strong> comunicare duc la creşterea performanŃei prin scă<strong>de</strong>rea numărului<br />
<strong>de</strong> apeluri <strong>de</strong>-a lungul reŃelei, tară a afecta codul clientului.<br />
Inconvenientul acestui şablon este acela că o parte a funcŃionalităŃii trebuie<br />
duplicată. Acest lucru este necesar <strong>de</strong>oarece fiecare jumătate a obiectului trebuie să<br />
aibă suficientă funcŃionalitate ca să poată administra obiectele locale.<br />
• Variante ale şablonului<br />
Ambele jumătăŃi păstrează o referinŃă una către cealaltă şi trimit mesaje înainte<br />
şi înapoi. în forma clasică, doar jumătatea aflată <strong>de</strong> parte clientului are o referinŃă<br />
către cealaltă. ConsecinŃa este că se poate iniŃia comunicarea doar <strong>de</strong> către jumătatea<br />
aflată <strong>de</strong> partea clientului prin apelarea unei meto<strong>de</strong> a jumătăŃii aflate la distanŃă.<br />
Aceasta din urmă poate răspun<strong>de</strong> doar o dată la fiecare apel prin intermediul valorii pe<br />
care o returnează. Atunci când este necesar ca ambele părŃi să iniŃieze comunicarea,<br />
fiecare trebuie să conŃine o referinŃă către cealaltă.<br />
O altă varianta este SHOPP - Smart HOPP. In cadrul acestei implementări,<br />
partea locală a obiectului HOPP îşi poate alege duplicatul pe baza unor strategii <strong>de</strong><br />
conectare. Aceasta este <strong>de</strong> ajutor când. <strong>de</strong> exemplu, aplicaŃia este distribuită <strong>de</strong>-a<br />
lungul unei reŃele flexibile un<strong>de</strong> maşinile vin şi pleacă.<br />
In versiunea Asymmetric HOPP, cele doua jumătăŃi nu trebuie să<br />
implementeze neapărat aceeaşi interfaŃă. Partea la distanŃă poate pune la dispoziŃie un<br />
nou proxy pe care cealaltă parte să-1 folosească. Noul proxy poate conŃine optimizări<br />
sau poate fi un proxy pentru un obiect la distanŃă diferit.<br />
• Şabloane asociate<br />
Printre şabloanele asociate se află:<br />
- Mediator - obiectele ce trebuie mediate sunt distribuite către multiple spaŃii<br />
<strong>de</strong> adresă. Mediator-ul poate folosi şablonul HOPP pentru a uşura comunicarea<br />
- Proxy - şablonul HOPP foloseşte şablonul Proxy pentru comunicarea<br />
transparentă între cele două jumătăŃi<br />
• Exemplu<br />
Consi<strong>de</strong>rându-se un calendar, acesta se păstrează într-un singur loc dar trebuie<br />
să fie disponibil oriun<strong>de</strong>. Pentru aceasta este nevoie <strong>de</strong> o interfaŃă Calendar care să<br />
<strong>de</strong>scrie meto<strong>de</strong>le disponibile la distanŃă, <strong>de</strong> o clasă Calendarlmpl ce conŃine<br />
implementările meto<strong>de</strong>lor la distanŃă şi <strong>de</strong> o clasă CalendarHopp care să poată rula<br />
local ceea ce în mod normal ar fi meto<strong>de</strong> la distanŃă:<br />
public class CalendarHO?? implements Calendar, java.io.Serializable {<br />
private static final String PROTOCOL = "rmi://"; private static<br />
final String REMOTE_SERVICE = "/calenaarimpl"; private staric<br />
final String HOPP_SERVICE = "calendar"; private static final<br />
Striric private Calendar calendar; private String host; public<br />
CalendarHOPP() {<br />
t h i s(DE FAULT HOS L) ;<br />
82
public CalendarHOPF(String host) {<br />
t r v {<br />
this.host - nost;<br />
String uri = PROTOCOL ^ host I REMOTE SERVICE;<br />
calendar = (Calendar)Naming.lookup(uri);<br />
Kaming.rebind(HOPP_SERVICE, triis);<br />
catcn (Exception exc) {<br />
System.err.println("Erroare !" - exc);<br />
}<br />
oublic String getHost() { return host; ;<br />
pudic ArrayList getAppointrrients (Date date) throws<br />
RemoteException { Return<br />
calendar.getAppointnents(date);<br />
public void addAppcintment(Appointment appointment, Date date<br />
throws Rem.oteException {<br />
calendar.addAppointment(appointment, date);<br />
IV.8. Proxy<br />
• Cunoscut şi ca Surrogate<br />
• ProprietăŃi<br />
• Scop<br />
- Tip: structural<br />
- Nivel: componentă<br />
Să pună la dispoziŃie o reprezentare a unui alt obiect, pe motive <strong>de</strong> acces,<br />
viteză sau securitate.<br />
• Introducere<br />
Pe măsură ce managerul personal <strong>de</strong> informaŃii se <strong>de</strong>zvoltă, el trebuie să<br />
administreze din ce în ce mai multe date. El conŃine adresele a numeroase persoane,<br />
inclusiv informaŃii <strong>de</strong>spre familiile acestora, hobby-uri. etc. La început numărul<br />
contactelor a fost mic, dar acum a ajuns <strong>de</strong> ordinul miilor.<br />
De multe ori. managerul <strong>de</strong> informaŃii este folosit doar pentru a schimba<br />
<strong>de</strong>taliile unei întâlniri sau pentru introducerea unei note. Prin urmare, vizualizarea<br />
cărŃii <strong>de</strong> adrese <strong>de</strong> fiecare dată când se utilizează managerul, <strong>de</strong>vine inutilă şi<br />
enervantă. Doar <strong>de</strong>schi<strong>de</strong>rea acesteia este o operaŃie costisitoare care duce la<br />
amânarea activităŃilor care nu necesită utilizarea ei.<br />
Pe un utilizator însă nu îl interesează acest fapt - el vrea doar să aibă la<br />
dispoziŃie cartea <strong>de</strong> adrese atunci când are nevoie <strong>de</strong> ea. Dar atunci când o foloseşte,<br />
nu are mereu nevoie <strong>de</strong> toate informaŃiile din ea. De exemplu, se doreşte doar<br />
cunoaşterea numărului total <strong>de</strong> contacte sau adăugarea unuia nou, fără a fi necesară<br />
observarea sau editarea întregii cărŃi <strong>de</strong> adrese.<br />
83
SoluŃia este un obiect placehol<strong>de</strong>r care să pună la dispoziŃie o interfaŃă cu<br />
cartea <strong>de</strong> adrese sau cu o parte a acesteia. Acest obiect arată asemănător cu cartea <strong>de</strong><br />
adrese, dar nu implică şi rularea acesteia. Totuşi, când este nevoie ca întreaga carte <strong>de</strong><br />
adrese să efectueze o sarcină cum ar fi modificarea adresei unui coleg, obiectul<br />
placehol<strong>de</strong>r creează a<strong>de</strong>vărata carte <strong>de</strong> adrese pentru a efectua sarcina respectivă.<br />
Acest obiect placehol<strong>de</strong>r este un Proxy.<br />
■ Utilizare<br />
- Remote Proxy (proxy la distanŃă) - se foloseşte atunci când se doreşte o<br />
reprezentare locală pentru un obiect într-un alt spaŃiu <strong>de</strong> adresă<br />
- Virtual Proxy (proxy virtual) - amână crearea obiectelor costisitoare<br />
- Protection Proxy (proxy <strong>de</strong> protecŃie) - <strong>de</strong>termină drepturile <strong>de</strong> acces la<br />
obiectul real<br />
• Descriere<br />
Un proxy este o reprezentare pentru un alt obiect. Pentru a putea face capabil<br />
proxy-ul să reprezinte obiectul real, el trebuie să implementeze aceeaşi interfaŃă ca şi<br />
obiectul real. Mai mult. proxy-ul păstrează o referinŃă către obiectul real. referinŃă <strong>de</strong><br />
care are nevoie pentru a apela meto<strong>de</strong> asupra obiectului real atunci când este necesar.<br />
ClienŃii vor interacŃiona cu proxy-ul. dar acesta poate <strong>de</strong>lega executarea către obiectul<br />
real. Cu toate că implementează aceeaşi interfaŃă ca şi obiectul real, proxy-ul poate<br />
efectua sarcini pe care obiectul real nu le poate face. cum ar fi comunicarea la distanŃă<br />
sau securitatea.<br />
Proxy-ul este un fel <strong>de</strong> suplinitor pentru obiectul real. El poate fi comparat cu<br />
sistemul <strong>de</strong> filmare a cascadoriilor periculoase pentru un film. Proxy-ul este dublura<br />
obiectului real - starul filmului. în timpul cascadoriei periculoase, proxy-ul este cel<br />
care sare din avion în locul obiectului real. Deoarece implementează aceeaşi interfaŃa<br />
ca şi obiectul real. audienŃa nu poate face diferenŃa şi cre<strong>de</strong> ca obiectul real este cel<br />
care a sărit. Dar atunci când camera se apropie, proxy-ul apelează obiectul real care<br />
preia efectuarea acŃiunii.<br />
• Implementare<br />
Diagrama <strong>de</strong> clase a şablonului Proxy este următoarea:<br />
interface Service<br />
ServiceProxv Servicelmpl<br />
84
Pentru implementarea şablonului Proxy se folosesc următoarele:<br />
- Service - interfaŃa folosită atât <strong>de</strong> proxy. cât şi <strong>de</strong> obiectul real<br />
- ServiceProxy - clasa ce implementează interfaŃa Service şi trimite apelurile<br />
meto<strong>de</strong>lor către obiectul real - Servicelmpl - atunci când este cazul<br />
- Servicelmpl - implementarea completă şi reală a interfeŃei. Acesta este<br />
obiectul reprezentat <strong>de</strong> proxy.<br />
• Beneficii şi inconveniente<br />
ConsecinŃele acestui şablon variază consi<strong>de</strong>rabil în funcŃie <strong>de</strong> tipul specific <strong>de</strong><br />
proxy folosit. Beneficiul proxy-ul la distanŃă este acela că se poate ascun<strong>de</strong> reŃeaua <strong>de</strong><br />
client. Clientul va cre<strong>de</strong> că un obiect local efectuează munca, când <strong>de</strong> fapt acesta<br />
trimite o cerere prin reŃea pentru a se efectua acŃiunea respectivă. Inconvenientul ar fi<br />
timpul mai lung <strong>de</strong> realizare a cerinŃelor, având în ve<strong>de</strong>re comportamentul reŃelei.<br />
Marele beneficiu al proxy-ul virtual este acela că se interacŃionează cu el, fără<br />
a crea obiectul real înainte <strong>de</strong> a avea nevoie <strong>de</strong> el. In plus, el poate efectua unele<br />
optimizări în ceea ce priveşte când şi cum să creeze obiectul real.<br />
Beneficiul proxy-ul <strong>de</strong> protecŃie este acela că permite controlul accesului.<br />
• Variante ale şablonului<br />
O variantă a acestui şablon este atunci când proxy-ul nu trebuie să cunoască<br />
obiectul real <strong>de</strong>cât prin intermediul interfeŃei. Creşte astfel flexibilitatea, dar acest<br />
lucru funcŃionează doar dacă proxy-ul nu este responsabil pentru crearea şi/sau<br />
distrugerea obiectului real.<br />
• Şabloane asociate<br />
Printre şabloanele asociate se află:<br />
- Adapter - pune la dispoziŃie o interfaŃă către un obiect specific, aşa cum face<br />
şi şablonul Proxy. Totuşi, Proxy-ul are aceeaşi interfaŃă ca şi obiectul. în timp ce<br />
Adapter-ul are o interfaŃă diferită<br />
- HOPP - poate folosi şablonul Proxy pentru comunicarea între două jumătăŃi<br />
distribuite ale lui<br />
- Business Delegate - acest şablon poate fi folosit ca un proxy<br />
• Exemplu<br />
Atunci când se încarcă o imagine mare, se doreşte crearea unor obiecte<br />
luminoase până când imaginea a fost încărcată complet. Pentru aceasta se poate utiliza<br />
şablonul Proxy:<br />
abstract class Graphic {<br />
public abstract voia load();<br />
public abstract void draw();<br />
85
la.ss Jmage extends Graphic{<br />
public voici l o a d ( ) ■ /<br />
public void draw() { /,<br />
lass InaFroxy extends Graphic {<br />
public void ]oadŃ) {<br />
if (irr.age -- nuli) {<br />
irr.age = r:cw Image ( f iî ename<br />
puoli.c void draw() { //...<br />
86
V. Concluzii<br />
Şabloanele ajută la <strong>de</strong>zvoltarea proiectelor <strong>software</strong> complexe, recurgându-se<br />
<strong>de</strong> foarte multe ori la soluŃii aplicate în trecut. Fiecare şablon se ocupă cu o problemă<br />
<strong>de</strong> <strong>de</strong>sign sau o implementare a unui sistem <strong>software</strong> specifică. Şabloanele pot fi<br />
folosite pentru construirea <strong>de</strong> arhitecturi <strong>software</strong> având anumite proprietăŃi.<br />
Programarea orientată pe obiecte este grea; programarea pe obiecte folosind cod<br />
reutilizabil este şi mai grea. Utilizatorii experimentaŃi realizează totuşi un <strong>de</strong>sign<br />
foarte bun şi asta pentru că ştiu faptul că nu mereu trebuie rezolvate problemele <strong>de</strong> la<br />
început. Ei reutilizează soluŃii obŃiinute anterior, şabloanele ajutând astfel la crearea<br />
unui <strong>de</strong>sign <strong>de</strong> <strong>software</strong> flexibil, uşor <strong>de</strong> întreŃinut şi <strong>de</strong> reutilizat.<br />
90
Bibliografie<br />
1. Stelting Stephen, Maassen Olav - Applied Java Patterns<br />
2. Cooper James W. - The Design Patterns Java Companion<br />
3. www.javacamp.org<br />
4. www.dofactory.com<br />
5. www.wikipedia.org<br />
91