Sunteți pe pagina 1din 22

8 Şabloane de proiectare (Design patterns)

În domeniul proiectării software există soluţii reutilizabile pentru problemele ce


apar mai des. Un şablon de proiectare nu este un element de proiectare aflat într-o formă
finală, direct transformabilă în cod, ci doar o soluţie pentru o anumită problemă, soluţie care,
în timp, s-a dovedit folositoare în situaţii asemănătoare.
Şabloanele de proiectare orientate obiect conţin clase, relaţiile şi interacţiunile
dintre ele. Clasele conţinute nu sunt într-o formă finală, structurile acestora putându-se
extinde cu elementele specifice fiecărui proiect.
Algoritmii nu sunt consideraţi ca fiind şabloane de proiectare deoarece ei rezolvă
probleme de implementare.
Folosirea şabloanelor de proiectare poate duce la creşterea atât a vitezei de
dezvoltare a software-ului, cât şi a calităţii acestuia, prin utilizarea unor soluţii testate, care şi-
au dovedit eficacitatea. Proiectarea software-ului presupune luarea unor decizii a căror
corectitudine se dovedeşte mai târziu, la partea de implementare. Reutilizarea unor şabloane
de proiectare ajută la prevenirea unor probleme majore şi îmbunătăţeşte claritatea codului
pentru programatorii şi arhitecţii familiari cu aceste şabloane.
Reutilizarea şabloanelor de proiectare este diferită de reutilizarea codului.
Reutilizarea şabloanelor de proiectare este practic o reutilizare de idei şi nu de componente. În
domeniul transformării celor mai utilizate şabloane de proiectare în componente se vorbeşte
de o rată de succes de două treimi (Meyer şi Arnout).
Nu orice şablon software este un şablon de proiectare. Şabloanele de proiectare
privesc doar uşurarea muncii de proiectare a aplicaţiilor software. De exemplu, mai există în
domeniul software aşa numitele şabloane arhitecturale (architectural patterns), ce descriu, aşa
cum le sugerează şi denumirea, soluţiile unor probleme de arhitectură software.
Şabloanele de proiectare pot fi grupate în mai multe categorii:
 şabloane creaţionale (creational patterns);
o singleton;
o builder (constructor);
o metodă factory;
o clasă abstractă factory;
o prototip;
 şabloane structurale (structural patterns);
o adaptor;
o compozit;
o facade;
o proxy;
 şabloane de comportament (behavioral patterns)
o observer;
o strategy;
o command;
o iterator;
o memento;
o visitor;
o mediator;
o lanţ de responsabilităţi (chain of responsability)
Pentru exemplificare vom crea în NetBeans două proiecte: un proiect UML Java
platform Model şi un proiect Java Desktop Application. Pentru o mai bună organizare a

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).

8.1 Şabloane creaţionale

Şabloanele creaţionale sunt utilizate de obicei pentru a separa crearea obiectelor de


utilizarea lor. Scopul principal al unui astfel de demers este de a permite introducerea în
sistem a noilor tipuri derivate fără a fi necesare schimbări asupra codului ce foloseşte
clasele de bază.

8.1.1 Şablonul Singleton


Singleton este un şablon folosit pentru a restricţiona instanţierea unei clase la un
singur obiect. Acest concept este uneori generalizat pentru a restricţiona instanţierea la un
anumit număr de obiecte.
Şablonul singleton se implementează prin crearea unei singure clase ce conţine o
asociere reflexivă (unul din propriile atribute este tot de tipul clasei). Elementul cheie al
acestui şablon este faptul că atributul ce are chiar tipul clasei este de tip static. Modificatorul
static folosit la declararea unui atribut sau la declararea unei metode arată apartenenţa
membrului respectiv la clasă şi nu la o instanţă a ei (la un obiect). În cazul unui atribut static,
ca în exemplul şablonului singleton, se alocă memorie o singură dată, la prima initializare a
clasei. La urmatoarele instanţieri ale clasei nu se mai alocă memorie pentru un atribut static,
dar toate obiectele din acea clasă pot accesa aceeaşi variabilă statică, aflată în aceeaşi zonă de
memorie. Pe lângă cele specificate mai sus, o clasă singleton mai poate conţine atribute şi
operaţii specifice domeniului în care este utilizat.
Şablonul singleton prevede şi existenţa unui constructor privat (dar acest
constructor nu este definit şi în şablonul prestabilit din NetBeans). Dacă acest constructor nu
ar fi privat clasa ar putea fi instanţiată de mai multe ori şi nu ar mai ieşi un singleton.
Neexistând constructorul, instanţa unică se obţine cu o metodă statică denumită în
continuare instance( ) (tot un fel de metodă get). Această metodă fiind statică poate fi apelată
cu sintaxa:

Obiect_returnat = clasă.metodă_statică();

şi ea ne va returna întotdeuna obiectul unic instanţiat (atributul static de acelaşi tip


cu clasa în care este inclus).

2
Fig. 8.1 Reprezentarea unei clase singleton

Instanţa unică poate fi creată:


1) implicit la definirea acestui element (o instanţiere nu tocmai leneşă, mai degrabă grăbită şi
risipitoare), caz în care metoda instance( ) trebuie doar să returneze o referinţă la acest
obiect:
public static Singleton instance() {
return uniqueInstance;
}

2) explicit în metoda statică instance( ), dacă o astfel de instanţă nu există deja (această
modalitate de instanţiere este denumită instanţiere leneşă).

public static Singleton instance () {


if(uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}

Putem testa clasa Singleton la crearea a două obiecte: „marie” şi „aceeasiMarie”.

private sabloane_creationale.Singleton marie;


private sabloane_creationale.Singleton aceeasiMarie;

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);

La lansarea codului de mai sus descoperim că, indiferent de ceea ce se spune


„aceeasiMarie” are aceeaşi pălarie (pardon ... aceeaşi valoare a atributului singletonData) şi
asta pentru că este aceeaşi „marie” (... acelaşi obiect uniqueInstance).
Exemple de utilizare a şablonului singleton:
- clasa user într-o aplicaţie în care se doreşte gestiunea unitară a drepturilor unui
utilizator logat;

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).

Fig. 8.2 Clasa singleton Player într-un joc de şah

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:

public Player getUniqueWhite() {


if (uniqueWhite == null) {
uniqueWhite = new Player();
color = "White";
}
return uniqueWhite;
}

Observaţie: în exemplul de mai sus nici nu este nevoie de o operaţie setColor,


deoarece culoarea este stabilită doar la instanţierea jucătorului şi nemodificată pe parcursul
jocului.
NetBeans ne dă posibiliatea să creăm propriile şabloane de proiectare pe lângă cele
prestabilite folosind opţiunea Window\Other\UML Design Center. Aici putem crea un nou
proiect cu şabloane, denumit eventual MyPatterns. În acest proiect putem specifica propriul
singleton creând următoarea diagramă de clase:

Fig. 8.3 Diagrama de clase necesară creării propriului singleton

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).

Fig. 8.4 Şablonul metodă factory

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ă.

8.1.3 Clasă abstractă factory


Şablonul abstract factory asigură o modalitate de grupare a mai multor metode
factory ce au elemente comune.
Conform acestui şablon se creează o clasă abstractă factory ce va sta la baza mai
multor clase concrete factory. Clasele factory conţin câte o metodă factory pentru fiecare tip
de obiect ce trebuie creat.
Codul client lucrează doar cu tipul abstract de factory, nedepinzând de tipurile
concrete. Conform acestui şablon, sunt create obiect concrete, dar codul client le accesează
doar prin interfaţa lor abstractă.
Adăugarea de noi tipuri concrete sistemului se face prin modificarea codului
client, în aşa fel încât să folosească o nouă clasă concretă factory.

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.

Fig. 8.5 Şablonul prototip

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.

Fig. 8.6 Şablonul builder

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 Şabloane structurale

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;

Fig. 8.7 Şablonul object adapter

În metoda request a adaptorului se scrie în acest caz:

public void request () {


furnizor.specificRequest();
}

2. adaptoare care implementează ambele interfeţe şi care în metodele ce implementează


operaţiile interfeţei dorite se face trimitere la operaţiile interfeţei de adaptat = class
adapter. În acest caz se poate vorbi de o adaptare bidirecţională a claselor.

Fig. 8.8 Şablonul class adapter cu implementare multiplă de interfeţe

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.

Fig. 8.9 Diagrama claselor într-o aplicaţie de messenger


Pentru utilizarea unui alt server de messenger, clasa ServerMessenger se poate
specializa într-un adaptor ce conţine ca şi membru un obiect de tipul unui alt server. Metoda
sendText a adaptorului suprascrie metoda sendText a serverului iniţial făcând delegând
activitatea de realizat membrului obiect de tipul celuilalt server, mai exact folosind metoda
sendMessage a acestuia.

Fig. 8.10 Aplicaţie de messenger cu un object adapter

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).

Fig. 8.11 Şablonul compozit

Şablonul nu este util doar în cazul structurilor ierarhice, ci în toate relaţiile de


compunere în care operaţiile unui grup de obiecte include operaţiile unui singur obiect.
Clasa destinată componentelor conţine pe lângă implementarea operaţiilor
specifice domeniului afacerii şi declararea interfeţei pentru accesarea şi gestiunea
componentelor copil (adăugare, ştergere, citire etc.).
Clasa compozită implementează metodele de manipulare a componentelor copil,
iar în cazul operaţiilor specifice domeniului afacerii îşi delegă sarcinile operaţiilor
corespunzătoare ale obiectelor copil.
În exemplul următor este vorba de ziarişti care fac parte din redacţii, redacţii care
aparţin de anumite publicaţii (ziare, reviste etc.), publicaţii ce aparţin unor trusturi de presă.
Şablonul compozit ne ajută în acest caz ca o operaţie specifică unui ziarist (cea de a ataca) să
fie lansată (de un mogul) pentru un întreg trust, cu un efort minim, fără a apela fiecare ziarist
în parte, ci apelând doar operaţia ataca a obiectului trust.

10
Fig. 8.11 Şablonul compozit

public interface Componenta {


public void ataca ();
}

public class Ziarist implements Componenta {


public void ataca () {
System.out.println("Hau hau!");
}
}

public class Grup implements Componenta {


private ArrayList<Componenta> mComponenta = new ArrayList<Componenta>();

public void ataca () {


for (Componenta componenta : mComponenta) {
componenta.ataca();
}
}

public void add (Componenta c) {


mComponenta.add(c);
}

public void remove (Componenta c) {


mComponenta.remove(c);
}
}
Dacă declaraţiile de mai sus ar face parte din pachetul sabloane_structurale,
intrând în pielea unui mogul am putea utiliza codul următor:

sabloane_structurale.Grup trust = new sabloane_structurale.Grup();


sabloane_structurale.Grup ziar = new sabloane_structurale.Grup();
sabloane_structurale.Grup redactie = new sabloane_structurale.Grup();
sabloane_structurale.Ziarist popescu = new sabloane_structurale.Ziarist();
sabloane_structurale.Ziarist ionescu = new sabloane_structurale.Ziarist();
sabloane_structurale.Ziarist altescu = new sabloane_structurale.Ziarist();
redactie.add(popescu);
redactie.add(ionescu);

11
redactie.add(altescu);
ziar.add(redactie);
trust.add(ziar);
trust.ataca();

8.2.3 Facade (faţadă)

O faţadă este un obiect ce asigură o interfaţă simplificată spre un grup de clase. Cu


ajutorul acestui şablon o bibliotecă software devine mai uşor de înţeles şi de utilizat,
reducândui dependenţa de restul codului (scade cuplarea).
O faţadă este recomandată şi atunci când trebuie să folosim o colecţie de clase ce
folosesc interfeţe prost proiectate sau greu de înţeles, în speranţa că interfaţa faţadei va
rezolva aceste probleme.

Fig. 8.12 Şablonul facade

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.

Fig. 8.13 Şablonul proxy

Există patru situaţii în care se recomandă să folosim un astfel de şablon:


1) Virtual proxy = o clasă de acces către obiectele unei alte clase ce
presupunhe utilizarea multor resurse; obiectul real este creat la prima cerere de acces la
acesta.
2) Remote proxy = asigură o reprezentare locală pentru un obiect dintr-un
spaţiu diferit de memorie; în RPC şi CORBA o astfel de funcţionalitatea o asigură un
„stub”.
3) Protective proxy = clasă de control al accesului la obiecte mai sensibile
din punct de vedere al securităţii. Obiectul proxy verifică dacă expeditorul are dreptul să
transmită mesaje destinatarului.
4) Smart proxy = clasă care nu doar transmite mai departe o operaţie de
realizat, ci realizează în plus o serie de acţiuni, cum ar fi:
a. numără referirile la un anumit obiect;
b. încarcă obiecte persistente la prima utilizare;
c. verifică dacă obiectul adevărat este blocat pentru alte accese (asigură prelucrări
tranzacţionale şi diferite niveluri de izolare)

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.

Fig. 8.14 Şablonul mediator

Mediator - defineşte interfaţa de comunicare între obiecte


ConcreteMediator - implementează interfaţa Mediator şi coordonează comunicarea
între objecte. Ştie care sunt toate obiectele ce doresc să comunice şi scopurile acestor
comunicări.
ConcreteColleague – comunică cu alte obiecte “colegi” cu ajutorul mediatorului.

Şablonul mediator se poate combina cu şablonul singleton, dacă o clasă concretă


mediator are sens să fie instanţiată o singură data. Într-un astfel de caz, dacă aplicaţia
abstractizează mai multe grupuri de colegi, pentru fiecare grup se poate crea câte o clasă
concretă mediator care să fie instanţiată o singură dată.
Şablonul mediator nu trebuie confundat cu şablonul proxy. Diferenţa principală
este faptul că mediatorii şi colegii nu sunt creaţi pe baza aceleiaşi interfeţe (sau clase
abstracte). Un proxy este „imaginea” unui alt obiect, prin urmare structura unui proxy depinde
destul de mult de structura obiectului destinaţie. Nu degeaba şablonul proxy este inclus în
grupul şabloanelor structurale. Structura unui mediator nu este imaginea în oglindă a unui
obiect coleg, ceea ce îi dă posibilitatea să transmită mesajele spre mai multe tipuri de clase
concrete de colegi, altfel spus să medieze mesajele între colegi cu structuri diferite.
Există asemănări între şablonul mediator şi alte şabloane la lista de avantaje:
 un coleg (binevoitor) poate să transmită „anonime” (avantaj întâlnit şi la proxy);

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

Şablonul observator este un şablon de proiectare în care un obiect gestionează o


listă cu proprii dependenţi, pe care îi anunţă automat de eventualele modificări de stare, de
obicei prin apelarea anumitor metode.
De multe ori acest şablon este folosit pentru implementarea sistemelor distribuite.

Fig. 8.15 Şablonul 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.

8.3.3 Lanţ de responsabilităţi

Şablonul lanţului de responsabilităţi (chain of responsibility) este un şablon


comportamental care permite evitarea cuplării directe a expeditorului unei cereri cu un anumit
destinatar, folosindu-se în acest sens clase intermediare.
Avantajele unui astfel de şablon:
- expeditorul poate să nu cunoască exact care este destinatarul final al cererii
sale, pe el interesându-l doar ca respectiva sarcină să fie îndeplinită;
- clasele intermediare pot alege destinatarii, gestionând eventual şi gradul de
solicitare al acestora şi gradul de solicitare a sistemelor de calcul pe care
rulează aceştia;
- o cerere poate fi procesată de mai mulţi destinatari, în acelaşi timp, secvenţial
sau respectând chiar anumite fluxuri de cereri;
- clasele intermediare pot realiza log-uri ale cererilor;
- lipsa oricărui potential destinatar poate fi aflată de către expeditor printr-un
mesaj primit de la clasele intermediare.

16
Fig. 8.16 Şablonul lanţ de responsabilităţi

Un exemplu de astfel de şablon este modul de autorizare a unor tranzacţii de către


anumiţi angajaţi în funcţie de mărimea acestor tranzacţii. Aprobarea unui credit poate fi făcută
la nivelul unui ofiţer de credit, manager al departamentului de credite, şef de filială sau
sucursală şi în rare cazuri un credit are nevoie şi de aprobarea managerilor din centrală. Pentru
fiecare rol mentionat mai sus se crează o clasă Functie (cu rolul de ConcreteHandler), în care
există un atribut ce ne indică limita creditului ce poate fi aprobat fără a fi trimis şi la nivelul
ierarhic superior.

Fig. 8.17 Exemplu de utilizare a şablonului lanţ de responsabilităţi

Fiecare clasă Funcţie va implementa metoda proceseazăDosarCredit, în mod


asemănător cu codul următor:

public void proceseazaDosarCredit( Credit c )


{
if ( c.suma <= limitaCredit )
{
//procesează efectiv dosarul în sensul aprobării sau respingerii lui
}
else
if ( superior != null )
superior. proceseazaDosarCredit(c);
}

E ca un fel de aruncarea mâţei moarte în ograda vecinului în funcţie de starea de


putrefacţie a bietului animal. Cu cât starea de putrefacţie este mai mare cu atât pisica va
ajunge la un vecin mai îndepărtat (în speranţa că toţi vecinii vor păstra aceeaşi direcţie).

17
8.3.4 Memento (Amintire)

Şablonul amintire (memento) este un şablon comportamental destinat salvării


diferitelor stări curente ale unor obiecte şi revenirea la aceste stări.
În acest şablon se foloseşte o clasă de amintire (Memento) ce conţine aceleaşi
proprietăţi de stare ca şi clasa obiectelor de salvat (Originator). Obiectele de amintire se pot
gestiona eventual într-o colecţie (Caretaker).
Obiectul de salvat trebuie să conţină câte o metodă pentru fiecare din aceste două
acţiuni: salvare (createMemento) şi revenire la stare anterioară (setMemento).

Fig. 8.18 Şablonul memento

Clientul care ar folosi un astfel de şablon ar începe prin utilizarea metodei


createMemento din Originator, iar obiectul memento astfel obţinut ar fi folosit ca parametru
de intrare în metoda addMemento din Caretaker:

caretaker.addMemento(originator.createMemento());

Revenirea la o stare anterioară, în condiţiile în care se ştie un anumit număr de


ordine al acesteia în cadrul colecţiei de stări:

originator.setMemento(caretaker.getMemento(7));

8.3.5 Strategie

Şablonul strategie ne ajută să alegem un anumit algoritm de utilizat în funcţie de


un context. Şablonul conţine un grup de algoritmi, fiecare din aceştia fiind încapsulat într-un
obiect. Clienţii ce folosesc algoritmii nu depind de aceştia, variind în mod independent.

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ă.

8.3.6 Şablonul iterator

Ş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.

Fig. 8.20 Şablonul iterator

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).

8.3.7 Şablonul interpreter

Ş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ţă.

Fig. 8.20 Şablonul interpreter

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;

ScriptEngineManager mgr = new ScriptEngineManager();


ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");
try {
Object result = jsEngine.eval("Expresia JavaScript de evaluat!");
} catch (ScriptException ex) {
// tratarea excetiilor
}

8.3.8 Şablonul command

Conform şablonului command un obiect poate încapsula toate informaţiile


necesare pentru apelarea unei metode a altui obiect, cum ar fi: numele metodei de apelat,
obiectul ce deţine metoda şi valorile de transmis parametrilor.

Fig. 8.21 Şablonul command

Clientul instanţiază obiectul de tip comandă şi îl pregăteşte pentru a fi apelat la un


moment ulterior.
Obiectul invoker decide când va fi apelat obiectul comandă.
Obiectul receiver este cel care va efectua o acţiune ca urmare a lansării în execuţie
a comenzii.
Beneficiile acestui şablon ţin de faptul că execuţia unei anumite metode poate fi
pregătită din timp, în aşa fel încât aceasta să fie lansată fără să i se ştie numele, obiectul de
care aparţine şi momentul exact al execuţiei.
Şablonul command poate fi utilizat pentru a realiza aplicaţii cu:

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