Documente Academic
Documente Profesional
Documente Cultură
1
claselor vom crea în ambele proiecte pachetele sursă: sabloane_creationale,
sabloane_structurale şi sabloane_comportamentale.
În proiectul UML creăm o diagramă de clase şi dăm în spaţiul alb al acesteia click
dreapta şi „Apply Design Pattern...”. Dacă un şablon nu presupune prea multe clase şi dorim
să introducem şablonul direct în model fără să îl vizualizăm într-o clasă de diagrame putem da
click dreapta pe nodul Model şi vom găsi aceeaşi opţiune „Apply Design Pattern...”. Wizard-
ul ce se deschide ne dă posibilitatea să introducem din proiectul „GoF Design Patterns” în
propriul proiect UML principalele şabloane de proiectare din literatura de specialitate (GoF
vine de la „Gang of Four”, autorii „Design Patterns: Elements of Reusable Object-Oriented
Software”, o carte de referinţă în literatura de specialitate).
Clasele proiectului UML pot fi generate automat în proiectul Java de la opţiunea
„Generate Code” (click dreapta pe clasa UML).
Obiect_returnat = clasă.metodă_statică();
2
Fig. 8.1 Reprezentarea unei clase singleton
2) explicit în metoda statică instance( ), dacă o astfel de instanţă nu există deja (această
modalitate de instanţiere este denumită instanţiere leneşă).
marie = sabloane_creationale.Singleton.instance();
marie.setSingletonData(3);
String s = new Integer(marie.getSingletonData()).toString();
System.out.println("Atributul singletonData primeste valoarea " + s);
aceeasiMarie = sabloane_creationale.Singleton.instance();
String s = new Integer(aceeasiMarie.getSingletonData()).toString();
System.out.println("Atributul singletonData avea deja valoarea " + s);
3
- clasa destinată conexiunii cu baza de date atunci când se doreşte ca aplicaţia să
folosească la un moment dat o singură bază de date.
Pot exista şi versiuni ale şablonului singleton, cele în care se limitează instanţierea
unei clase nu doar la o instanţă, ci la un număr finit de instanţe (de preferat nu număr mic).
De exemplu, într-un joc de şah clasa jucător ar trebui să fie instanţiată numai de
două ori. Soluţia în acest caz presupune:
- un constructor privat;
- două atribute statice de tipul clasei (uniqueWhite şi uniqueBlack);
- două metode statice care să returneze cele două instanţe unice
(getUniqueWhite şi getUniqueBlack).
Metodele statice din acest exemplu ar trebui să se asigure mai întâi că există
instanţele statice corespunzătoare şi apoi să le iniţializeze, adică să le dea o nuanţă de alb sau
de negru:
4
8.1.2 Metodă factory
Termenul factory (fabrică) este utilizat în acest context pentru a arăta locul
destinat construirii de obiecte.
Şablonul metodă factory presupune crearea unei clase de bază (Creator) ce conţine
o metodă destinată creării obiectelor de un anumit tip (factoryMethod).
Subclase ale clasei factory pot suprascrie metoda factory pentru a se specifica
anumite subtipuri ale obiectelor obţinute.
Şablonul este folosit în ierarhiile de clase paralele, când obiectele dintr-o ierarhie
creează obiecte corespunzătoare din cealaltă ierarhie. De exemplu, putem dezvolta o ierarhie
de păsări (găină, raţă, struţ), ce produc anumite tipuri de ouă. Fiecărui element din ierarhia de
păsări îi corespunde un anumit tip de ou (nu punem săraca găină să producă un ou de struţ,
spre bucuria ei).
Acest şablon poate fi implementat cu dificultate atunci când este folosit pentru
clase ce au deja clienţi. Adică dacă începem prin aplicaţie să folosim constructorul implicit de
ouă, e puţin cam târziu să mai introducem în ecuaţie şi pasărea cu metoda ei factory de făcut
ouă.
5
Fig. 8.4 Şablonul clasă abstractă factory
8.1.4 Prototip
Şablonul constă în folosirea unei instanţe drept prototip pentru crearea (clonarea)
de noi obiecte.
Şablonul protoype este folosit pentru:
- a evita folosirea subclaselor concrete factory în aplicaţiile client;
- a evita crearea de obiecte în mod clasic, cu metode constructor.
Pentru implementarea acestui şablon se declară o clasă de bază abstractă ce
specifică o operaţie clonePrototype. Orice clasă ce are nevoie de un constructor polimorfic va
deriva din clasa de bază abstractă şi implementează operaţia clonePrototype.
8.1.5 Builder
Şablonul builder ajută la abstractizarea paşilor construirii unor obiecte, altfel spus
a reţetelor de fabricaţie. Obiectele de obţinut nu fac parte dintr-o anumită ierarhie de clase, dar
există constrângerea ca valorile atributelor acestora să nu aibă sens decât într-o anumită
6
combinaţie. Fiecare combinaţie de valori ale atributelor se obţine cu un anumit builder (cu o
anumită reţetă de fabricaţie). Aceşti builder-i sunt organizaţi într-o ierarhie de clase.
Clasa director este cea care construieşte efectiv produsul cu ajutorul unui builder
(reţete de fabricaţie) pe care îl conţine. Metoda construct din clasa director include o secvenţă
de apelări a metodelor de construire a părţilor (atributelor) produselor. Cum toate produsele
(obiectele) obţinute sunt instanţe ale aceeleiaşi clase înseamnă că toate au aceleaşi părţi şi prin
urmare toate se pot obţine prin respectarea aceleiaşi secvenţe de construire.
Pot exista variante ale acestui şablon în care Builder este o clasă abstractă şi nu o
interfaţă. Motivul folosirii unei clase abstracte poate fi în acest caz agregarea în Builder a unui
obiect produs protejat (protected).
Cel mai reuşit exemplu de utilizare a şablonului builder este cel de pe wikipedia
unde clasei cu rolul product este clasa pizza, clasele cu rolurile concretebuilder sunt diferitele
reţete de pizza, iar clasa cu rolul director este clasa.
8.2.1 Adaptor
Adaptorul (cunoscut şi sub numele de wrapper) este o clasă ce permite traducerea
unor interfeţe în alte interfeţe. Adaptorul permite unor clase cu interfeţe incompatibile să
interacţioneze. Acest şablon este folositor atunci când o clasă deja implementată (clasa de
adaptat sau clasa furnizor) asigură funcţionalităţile dorite, dar nu şi interfaţa dorită. Adaptorul
ştie să răspundă cel puţin la interfaţa dorită de client, din acest motiv clasa adaptor este
utilizată în mod direct de către clasa client. În acelaşi timp adaptorul ştie să utilizeze
funcţionalităţile clasei de adaptat.
În acest fel, clasa client este independentă de structura interfeţei de adaptat.
Cu un astfel de şablon clasa client poate modifica furnizorii de funcţionalităţi fără
să îşi modifice propria structură, doar utilizând alţi adaptori.
7
Există două tipuri de adaptoare, în funcţie de modul în care adaptorul reuşeşte să
utilizeze funcţionalităţile clasei de adaptat:
1. adaptoare ce implementează interfaţa dorită de client şi care conţin în plus o instanţă a
clasei de adaptat = object adapter;
8
Adaptorul descris în figura 8.8 poate fi folosit atât pentru o aplicaţie ce depinde de
ClassA, dar care dispune de ClassB, cât şi invers.
Şablonul class adapter presupune folosirea moştenirii multiple, de unde îi vin şi
anumite dezavantaje:
- nu toate mediile de programare suportă complet moştenirea multiplă (de aceea
figura 8.8 face referire la implementările a două interfeţe şi nu la extinderea a
două clase);
- pot apare conflicte între operaţiile celor două interfeţe când aceste operaţii au
aceeaşi semnătură dar o semantică diferită
Un exemplu de utilizare a şablonului object adaptor este cel în care aplicaţiile
client de messenger pot utiliza diferite servere de messenger.
Iniţial un client de messenger poate fi dezvoltat pentru un singur server, ulterior
dorindu-se utilizarea şi a altor servere. Figura 8.9 face referire la clasele specifice unei astfel
de aplicaţii iniţiale în care clientul, în metoda sendText, utilizează metoda sendText dintr-un
anumit server.
9
Alte exemple de utilizare a adaptoarelor:
- wrapper-ele din bazele de date federative (DB2) care fac legătură între baze de
date eterogene;
- comenzile de aprovizionare ce pot fi transmise direct furnizorilor într-un mediu
B2B;
- coletele de transmis prin intermediul oficiilor poştale diferite.
8.2.2 Compozit
Şablonul compozit permite unui grup de obiecte să fie tratat ca un singur obiect.
Cu ajutorul acestui şablon o operaţie specifică elementelor componente poate fi lansată şi
pentru un agregat, situaţie în care operaţia se lansează pentru toate elementele agregate.
Una din situaţiile în care şablonul compozit se face util este cea în care se
gestionează structuri ierarhice. Diferenţa structurală dintre noduri şi frunze face ca lucrul cu
structuri ierarhice să fie complex. Soluţia propusă de şablon este de a obţine elementul
compozit (nodul în cazul structurilor ierarhice) din specializarea clasei destinată
componentelor (frunzelor).
10
Fig. 8.11 Şablonul compozit
11
redactie.add(altescu);
ziar.add(redactie);
trust.add(ziar);
trust.ataca();
12
În exemplul din figura 8.12 cel care crează obiectele de tip tranzacţie (dintr-o
interfaţă utilizator sau dintr-o procedură de import) nu trebuie să cunoască detaliile claselor de
contabilitate şi gestiune, ci doar că o tranzacţie, o dată creată, trebuie transmisă faţadei
ContabilitateSiGestiune pentru înregistrarea acesteia în contabilitate şi afectarea gestiunilor
corespondente. În acest caz metoda contabilizeaza din faţada ContabilitateSiGestiune include
toată secvenţa de apelări a claselor: RegistruJurnal, CarteaMare, RegistruDeCasa,
RegistruDeBanca, Stocuri.
8.2.4 Proxy
La modul general un proxy este o resursă (clasă, server etc.) care funcţionează ca o
interfaţă pentru altceva. Un proxy nu face decât să delege acţiunile mai departe unei alte clase.
13
8.3 Şabloane comportamentale
8.3.1 Mediator
De obicei o aplicaţie este alcătuită dintr-un număr important de clase. Cu cât sunt
mai multe clase într-o aplicaţie, problema comunicării între obiecte devine mai complexă,
ceea ce face programul mai greu de citit şi întreţinut. Modificarea programelor, în astfel de
situaţii, devine mai dificilă din moment ce orice modificare poate afecta codul din mai multe
clase.
Cu ajutorul şablonului mediator comunicaţia dintre obiecte este încapsulată în
obiectul mediator. Obiectele nu mai comunică direct între ele, ci comunică în schimb prin
intermediul mediatorului. Acest lucru reduce dependenţa între obiecte, micşorând cuplarea.
În contextul acestei diagrame termenul de coleg este utilizat pentru a desemna un
obiect ce doreşte să comunice cu alte obiecte din acelaşi grup, un grup având un singur
moderator.
14
un coleg (răspândac) poate să transmită acelaşi mesaj mai multor obiecte (avantaj
întâlnit şi la şablonul compozit);
un mediator (securist) poate să folosească liste de control al accesului (avantaj
întâlnit şi la proxy);
un mediator (mai leneş) poate aştepta mai multe mesaje de la colegi pentru a lansa
o anumită operaţiune (avantaj specific şablonului mediator).
Exemplu de folosire a şablonului moderator: organizarea unor vizite (de exemplu
la Moscova) poate fi realizată prin intermediul unui moderator (de exemplu ambasadorul rus
la Bucureşti). Utilizând terminologia acestui şablon, colegi sunt persoanele ce urmează să se
întâlnească. Din păcate, şablonul mediator nu reflectă şi relaţii de subordonare între „colegi”.
Un alt exemplu (total rupt de cel anterior) este cel al unei reţele de spionaj în care
colegi sunt spionii şi persoanele decidente din structurile de informaţii. Mediatorii în acest
exemplu sunt persoanele de legătură, care fac ca de cele mai multe ori colegii să nu se
cunoască între ei.
8.3.2 Observator
Clasa Subject este o clasă abstractă (sau o interfaţă) ce asigură ataşarea şi scoaterea
observatorilor. Clasa conţine pe lângă o listă privată cu observatori şi următoarele metode:
attach( ) – adaugă un nou observator în listă;
15
detach( ) – elimină un observator din listă;
notifyObserver( ) – anuntă fiecare observator asupra unei schimbări de stare prin apelarea
metodelor update( ) ale acestora.
Clasa ConcreteSubject este elementul de interes al observatorilor. Ea trimite o
notificare tuturor observatorilor prin apelarea metodei notifyObserver( ) din clasa ei părinte.
Clasa ConcreteSubject conţine pe lângă interfaţa Subject metoda GetState ce returnează starea
subiectului de observat.
Clasa Observer defineşte o interfaţă pentru anunţarea tuturor observatorilor asupra
modificărilor survenite în subiect. Interfaţa constă într-o metodă ce va fi suprascrisă de fiecare
observator concret.
Clasa ConcreteObserver gestionează o referinţă către clasa ConcreteSubject şi
conţine operaţia update( ). Când acestă operaţie este apelată de către subiect,
ConcreteObserver apelează operaţia GetState a subiectului pentru a-şi actualiza informaţi
privind starea subiectului. Operaţia update( ) poate primi eventual parametri cu informaţii
generale ale evenimentului apărut, informaţii utile observatorului.
Şablonul observator este utilizat atunci când modificarea stării unui obiect
afectează alte obiecte şi nu se ştie la momentul scrierii codului exact ce obiecte vor trebui
anunţate.
Exemplu de folosire a şablonului observer: implementrarea modurilor de lucru,
când într-o fereastră de dialog utilizarea unui obiect poate presupune disponibilizarea sau
indisponibilizarea altor obiecte. În terminologia acestui şablon toate obiectele de pe un
formular sunt observatori, iar subiectul concret de observat este chiar formularul. Fiecare
obiect în parte nu trebuie să modifice direct celelalte obiecte de pe formular pentru că ar
trebui să cunoască mulţimea acestora.
Aceeaşi funcţionalitate a modurilor de lucru ar putrea fi implementată şi cu
ajutorul şablonului mediator.
16
Fig. 8.16 Şablonul lanţ de responsabilităţi
17
8.3.4 Memento (Amintire)
caretaker.addMemento(originator.createMemento());
originator.setMemento(caretaker.getMemento(7));
8.3.5 Strategie
18
Fig. 8.19 Şablonul strategie
Obiectele de tip context conţin câte un obiect strategy, dar alegerea unei strategii
concrete se realizează în funcţie de context.
Conform şablonului strategy comportamentul unei clase nu ar trebui să fie
moştenit, ci specific contextului în care rulează.
Şablonul iterator este folosit pentru a accesa elementele unui agregat (a unei
colecţii) în mod secvenţial, fără a ne folosi de caracteristicile acestor elemente.
In orice moment unul din elementele colecţiei este considerat ca fiind elementul
curent.
19
Operaţii ale elementelor colecţiei de parcurs ar putea fi:
- currentItem (ghici ce face fiecare operaţie);
- first;
- last;
- next;
- previous;
- hasNext;
- hasPrevious;
- isFirst;
- isLast.
Şablonul iterator are un grad mai mare de importanţă în mediile de dezvoltare în
care nu sunt implementate complet tipuri de date compuse (vectori, liste, stive).
Şablonul interpreter descrie cum se pot interpreta expresiile într-un anumit limbaj.
El îşi găseşte utilitatea în aplicaţiile economice în care se doreşte salvarea unor formule de
calcul într-un format accesibil utilizatorilor finali şi folosirea ulterioară de către aplicaţie a
acestor formule.
Conform şablonului interpreter atât operanzii, cât şi operatorii dintr-o expresie ar
trebui să aibă aceeaşi interfaţă.
Diagrama claselor de mai sus (cea din NetBeans de la acest şablon) nu surprinde
nevoia existenţei unui evaluator de expresii. Acest evaluator trebuie să împartă expresia de
interpretat în mai multe părţi cu un parser. Tot evaluatorul trebuie să parcurgă fiecare parte a
expresiei:
- când întâlneşte un operand (o variabilă pe care are voie utilizatorul să o
folosească într-o anumită formulă) îl introduce într-o stivă;
- când întâlneşte un operator extrage din stivă valorile operanzilor afectaţi şi
introduce în loc rezultatul aplicării operatorului asupra operanzilor.
Dacă se pleacă de la o expresie corectă la sfărşitul parcurgerii acesteia în stiva
utilizată va rămâne o singură valoare, ea fiind chiar rezultatul interpretării.
20
Exemple de aplicaţii în care ar putea fi utilizat şablonul interpreter:
- aplicaţii de salarizare;
- aplicaţii de procesare a datelor la importul sau exportul lor din diferite sisteme;
- aplicaţii de interpretare a unui dialect SQL sau de traducere dintr-un dialect
SQL în altul.
Un interpretor poate fi văzut şi ca o „cutie neagră” căreia i se dă o expresie şi care
returnează un rezultat. Cum nu trebuie să „reinventăm roata” putem folosi interpretore deja
existente, cum ar fi cele pentru scripturi:
import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
21
- funcţionalităţi de tip „undo”;
- comportament tranzacţional;
- progress bar sincronizat cu execuţia unui grup de comenzi;
- funcţionalităţi de tip „wizard”;
- înregistrări de macro-uri;
De exemplu, dacă un păpuşar (client) doreşte să pună o păpuşă (receiver) să se
bucure (concretecommand) foloseşte un invoker, iar acesta îi transmite păpuşii să ţopăie
(metoda action din şablon).
22