Sunteți pe pagina 1din 10

2013/12/11 14:52

1/10

Visitor pattern

Visitor pattern
G G G

Responsabil: Adriana Drghici Data publicrii: 08.12.2013 Data ultimei modificri: 08.12.2013

Obiective
Scopul acestui laborator este prezentarea design pattern-ului Visitor i familiarizarea cu situaiile n care acesta este util de aplicat.

Design Patterns
Design pattern-urile reprezint soluii generale i reutilizabile ale unei probleme comune n design-ul software. Un design pattern nu este un design n forma final, ceea ce nseamn ca nu poate fi transformat direct n cod. Acesta este o descriere a soluiei sau un template ce poate fi aplicat pentru rezolvarea problemei. In general pattern-urile orientate obiect arat relaiile i interaciunile dintre clase sau obiecte, fr a specifica ns forma final a claselor sau obiectelor implicate. Design Pattern-urile fac parte din domeniul modulelor i interconexiunilor. La un nivel mai nalt se gsesc pattern-urile arhitecturale (Architectural Patterns) ce descriu structura ntregului sistem. Se consider c exista aproximativ 2000 de design patterns [2], iar principalul mod de a le clasifica este urmtorul:
G G G

Gang of Four patterns Concurrency patterns Architectural patterns - descriere la un nivel mai inalt decat design patterns, stabilesc nivele i componente ale sistemelor/aplicaiilor, interactiuni ntre acestea. Sunt de obicei implementat n frameworks (e.g. Java Spring).

O carte de referin pentru design patterns este Design Patterns: Elements of Reusable Object-Oriented Software [1], denumit i Gang of Four. Aceasta definete 23 de design patterns, foarte cunoscute i utilizate n prezent. Aplicaiile pot ncorpora mai multe pattern-uri pentru a reprezenta legturi dintre diverse componente (clase, module). n afar de GoF, i ali autori au adus n discuie pattern-uri orientate n special pentru aplicaiile enterprise i cele distribuite. Pattern-urile GoF sunt clasificate dup urmtoarele tipuri:
G

Creational Patterns - mecanisme de creare a obiectelor Singleton, Factory etc Structural Patterns - definesc relaii ntre entiti
H

Programare Orientat pe Obiecte - http://elf.cs.pub.ro/poo/

Last update: 2013/12/11 10:27


H

laboratoare:visitor

http://elf.cs.pub.ro/poo/laboratoare/visitor

Decorator, Adapter, Facade, Composite, Proxy etc. Behavioural Patterns - definesc comunicarea ntre entiti Visitor, Observer, Command, Mediator, Strategy etc.
H

Design pattern-urile nu trebuie privite ca nite reete care pot fi aplicate direct pentru a rezolva o problem din design-ul aplicaiei, pentru c de multe ori pot complica inutil arhitectura. Trebuie nti neles dac este cazul s fie aplicat un anumit pattern, si de-abia apoi adaptat pentru situaia respectiv. Este foarte probabil chiar s folosii un pattern (sau o abordare foarte similar acestuia) fr s v dai seama sau s l numii explicit. Ce e important de reinut dup studierea acestor pattern-uri este un mod de a aborda o problem de design.

Visitor
Design pattern-ul Visitor ofer o modalitate de a separa un algoritm de structur pe care acesta opereaz. Avantajul const n faptul c putem adauga noi posibiliti de prelucrare a structurii, fr s o modificm. Extrapolnd, folosind Visitor, putem aduga noi funcii care realizeaz prelucrri asupra unei familii de clase, fr a modifica efectiv structura claselor. Acest pattern este comportamental (behavioral) pentru c definete modaliti de comunicare ntre obiecte. Cum recunoatem o situaie n care Visitor e aplicabil?
G G G

Mai multe obiecte i operaii pentru acestea Schimbarea/adaugarea operaiilor fr a modifica clasele Elemente heterogene - tipuri diferite de obiecte pe care se vor aplica operaiile

Decizia de utilizare a pattern-ului Visitor este n strns legtur cu stabilitatea ierarhiilor de clase prelucrate: dac noi clase copil sunt adugate rar, atunci se poate aplica acest pattern (ntr-o manier eficient), altfel nu este indicat.

Structur

http://elf.cs.pub.ro/poo/

Printed on 2013/12/11 14:52

2013/12/11 14:52

3/10

Visitor pattern

Fig. 1: Componente pattern Visitor Visitor - o interfa pentru operaia aplicat Visitable - o interfa pentru obiecte pe care pot aplicate operaiile (n diagram este numit Element)
G G

metoda accept e independent de tipul concret al Visitor-ului n accept se folosete obiectul de tip Visitor

Pentru fiecare algoritm/operaie ce trebuie aplicat, se implementeaz clase de tip Visitor. n fiecare obiect de tip Visitor trebuie s implementm metode care aplic operaia pentru fiecare tip de element vizitabil.

n figure 2 este reprezentat flow-ul aplicrii acestui pattern: 1. Clientul este cel care folosete o colecie de obiecte de unul sau mai multe tipuri, i dorete s aplice pe acestea diferite operaii (n exerciiile din laborator clientul este practic programul vostru de test - main-ul). Clientul folosete obiecte Visitor create pentru fiecare operaie necesar. 2. Clientul parcurge colecia i n loc s aplice direct pe fiecare obiect operaia, i ofer acestuia un obiect de tip Visitor. 3. Obiectul apeleaz metoda de vizitare oferit de Visitor. 4. Pe obiectul Visitor se apeleaz metoda visit corespunztoare obiectului, iar n ea se efectueaz operaia. (!!! n Visitor folosim conceptul de overloading pentru fiecare metod visit)

Programare Orientat pe Obiecte - http://elf.cs.pub.ro/poo/

Last update: 2013/12/11 10:27

laboratoare:visitor

http://elf.cs.pub.ro/poo/laboratoare/visitor

Fig. 2: Interaciunile dintre componentele pattern-ului Visitor

Visitor i structurile de date Aparent, folosirea lui accept este artificial. De ce nu declanm vizitarea unui obiect, apelnd direct v.visit(e) atunci cnd dorim vizitarea unui obiect oarecare? Ce se intampl ns, cnd dorim s vizitm o structur complex de obiecte? (list, arbore, graf etc):
G G G

declanarea vizitrii se va face printr-un apel accept pe un prim obiect (e.g. rdacina arborelui) elementul curent este vizitat, prin apelul v.visit(this) pe lng vizitarea elementului curent, este necesar sa declanm vizitarea tuturor elementelor accesibile din elementul curent (e.g. nodurile-copil din arbore etc). Realizm acest lucru apelnd accept pe fiecare dintre aceste elemente. Acest comportament depinde de logica structurii.

Traversarea structurii poate fi realizat in 3 moduri:


G G G

de ctre structur n cadrul vizitatorului, n cazul unor parcurgeri cu o logic mai complex n conjuncie cu un iterator, care dicteaz ordinea de vizitare

Scenariu Visitor
Pentru a nelege mai bine motivaia din spatele design-pattern-ului Visitor, s considerm urmtorul exemplu.

Before
Fie ierarhia de mai jos, ce definete un angajat (Employee) i un ef (Boss), vzut, de asemenea, ca un angajat:

http://elf.cs.pub.ro/poo/

Printed on 2013/12/11 14:52

2013/12/11 14:52

5/10

Visitor pattern

Test.java class Employee { String name; float salary; public Employee(String name, float salary) { this.name = name; this.salary = salary; } public String getName() { return name; } public float getSalary() { return salary; } } class Boss extends Employee { float bonus; public Boss(String name, float salary) { super(name, salary); bonus = 0; } public float getBonus() { return bonus; } public void setBonus(float bonus) { this.bonus = bonus; } } public class Test { public static void main(String[] args) { Boss boss; List<Employee> employees = new LinkedList<Employee>(); employees.add(new Employee("Alice", 20)); employees.add(boss = new Boss("Bob", 1000)); boss.setBonus(100); } }

Ne intereseaz s interogm toi angajaii notri asupra venitului lor total. Observm c:
G G

anagajaii obinuii au salariul ca unic venit efii posed, pe lng salariu, un posibil bonus

Varianta la indemn ar fi s definim, n fiecare din cele doua clase, cte o metod, getTotalRevenue(), care ntoarce salariul pentru angajai, respectiv suma dintre salariu i bonus pentru efi: class Employee { ... public float getTotalRevenue() {
Programare Orientat pe Obiecte - http://elf.cs.pub.ro/poo/

Last update: 2013/12/11 10:27

laboratoare:visitor

http://elf.cs.pub.ro/poo/laboratoare/visitor

return salary; } } class Boss extends Employee { ... public float getTotalRevenue() { return salary + bonus; } } Acum ne intereseaz s calulm procentul mediu pe care l reprezint bonusul din venitul efilor, lundu-se n considerare doar bonusurile pozitive. Avem dou posibiliti:
G

Definim cte o metod, getBonusPercentage(), care n Employee ntoarce mereu 0, iar n Boss raportul real. Dezavantajul const n sporirea interfeelor claselor cu funcii prea specializate, de detaliu. Parcurgem lista de angajai, testm, la fiecare pas, tipul angajatului, folosind instanceof, i calculm, doar pentru efi, raportul solicitat. Dezavantajul este tratarea ntr-o manier neuniform a structurii noastre, cu evidenierea particularitilor fiecrei clase.

Datorit acestor particulariti (n cazul nostru, modalitile de calcul al venitului, respectiv procentului mediu), constatm c ar fi foarte util izolarea implementrilor specifice ale algoritmului (n cazul nostru, scrierea unei funcii n fiecare clas). Acest lucru conduce, ns, la introducerea unei metode noi n fiecare din clasele antrenate in prelucrri, de fiecare dat cand vrem s punem la dispoziie o nou operaie. Obinem urmtoarele dezavantaje:
G

n cazul unui numr mare de operaii, interfeele claselor se aglomereaz excesiv i se ascunde funcionalitatea de baz a acestora codul din interiorul clasei (care servea functionalitii primare a acesteia) va fi amestecat cu cel necesar algoritmilor de prelucrare, devenind mai greu de parcurs i ntreinut n cazul n care nu avem acces la codul claselor, singura modalitate de adugare de funcionalitate este extinderea

n final, tragem concluzia c este de dorit s izolm algoritmii de clasele pe care le prelucreaz. O prim idee se refer la utilizarea metodelor statice. Dezavantajul acestora este c nu pot reine, ntr-un mod elegant, informaie de stare din timpul prelucrrii. De exemplu, dac structura noastr ar fi arborescent (recursiv), n sensul c o instan Boss ar putea ine referine la alte instane Boss, ce reprezint efii ierarhic inferiori, o funcie de prelucrare ar trebui s menin o informaie parial de stare (precum suma procentelor calculate pn ntr-un anumit moment) sub forma unor parametri furnizai apelului recursiv: class Boss extends Employee { ... public float getPercentage(float sum, int n) { float f = bonus / getTotalRevenue(); if (f > 0) return inferiorBoss.getPercentage(sum + f, n + 1); // trimite mai departe cererea catre nivelul inferior return inferiorBoss.getPercentage(sum, n); } }
http://elf.cs.pub.ro/poo/ Printed on 2013/12/11 14:52

2013/12/11 14:52

7/10

Visitor pattern

O abordare mai bun ar fi:


G

conceperea claselor cu posibilitatea de primire/ataare a unor obiecte-algoritm, care definesc operaiile dorite definirea unor clase algoritm care vor vizita structura noastr de date, vor efectua prelucrrile specifice fiecrei clase, avnd, totodat, posibilitatea de ncapsulare a unor informaii de stare (cum sunt suma i numrul din exemplul anterior)

After
Conform obsrevaiilor precedente, structura programului Employee-Boss devine: interface Visitor { public void visit(Employee e); public void visit(Boss b); } interface Visitable { public void accept(Visitor v); } class Employee implements Visitable { ... public void accept(Visitor v) { v.visit(this); } } class Boss extends Employee { ... public void accept(Visitor v) { v.visit(this); } } public class Test { public static void main(String[] args) { ... Visitor v = new SomeVisitor(); obiect-vizitator concret for (Employee e : employees) e.accept(v); } }

// creeaza un

Iat cum poate arta un vizitator ce determin venitul total al fiecrui angajat i l afieaz: RevenueVisitor.java public class RevenueVisitor implements Visitor { public void visit(Employee e) { System.out.println(e.getName() + " " + e.getSalary()); }
Programare Orientat pe Obiecte - http://elf.cs.pub.ro/poo/

Last update: 2013/12/11 10:27

laboratoare:visitor

http://elf.cs.pub.ro/poo/laboratoare/visitor

public void visit(Boss b) { System.out.println(b.getName() + " " + (b.getSalary() + b.getBonus())); } }

Secvenele de cod de mai sus definesc:


G

o interfa, Visitor, ce reprezint un algoritm oarecare, ce va putea vizita orice clas. Observai definirea cte unei metode visit() pentru fiecare clas ce va putea fi vizitat o interfa, Visitable, a carei metod accept(Visitor) permite rularea unui algoritm pe structura curent. implementri ale metodei accept(Visitor), n cele dou clase, care, pur i simplu, solicit vizitarea instanei curente de ctre vizitator. o implementare a unei operaii aplicabil pe obiectele de tip Visitable

n exemplul de mai sus, putem identifica :


G G

Element - Visitable ConcreteElement - Employee, Boss

Double-dispatch
Mecanismul din spatele pattern-ului Visitor poart numele de double-dispatch. Acesta este un concept raspndit, i se refer la faptul c metoda apelat este determinat la runtime de doi factori. n exemplul Employee-Boss, efectul vizitarii, solicitate prin apelul e.accept(v), depinde de:
G G

tipul elementului vizitat, e (Employee sau Boss), pe care se invoc metoda tipul vizitatorului, v (RevenueVisitor), care conine implementrile metodelor visit

Acest lucru contrasteaz cu un simplu apel e.getTotalRevenue(), pentru care efectul este hotrt doar de tipul anagajatului. Acesta este un exemplu de single-dispatch.

Aplicabilitate
Pattern-ul Visitor este util cnd:
G G

se dorete prelucrarea unei structuri complexe, ce cuprinde mai multe obiecte de tipuri diferite se dorete definirea de operaii distincte pe aceeai structur, pentru a preveni poluarea interfeelor claselor implicate, cu multe detalii aparinnd unor algoritmi diferii. n acest fel, se centralizeaz aspectele legate de acelai algoritm ntr-un singur loc, dar, n acelai timp, se separ detaliile ce in de algoritmi diferii. Acest lucru conduce la simplificarea att a claselor prelucrate, ct i a vizitatorilor. Orice date specifice algoritmului rezid n vizitator. clasele ce se doresc prelucrate se schimb rar, n timp ce operaiile de prelucrare se definesc des. Dac apar des clase prelucrate, atunci este necesar modificarea vizitatorilor
Printed on 2013/12/11 14:52

http://elf.cs.pub.ro/poo/

2013/12/11 14:52

9/10

Visitor pattern

existeni, pentru adugarea unei metode visit special pentru clasa respectiv.

Avantaje:
G G

G G

Decuplarea datelor de operaiile aplicate pe acestea Uureaz adugarea unor noi operaii/algortimi. Se creeaz o implementare a unui obiect de tip Visitor i nu se schimb nimic n obiecte vizitate. Spre deosebire de Iterator poate gestiona elemente de tipuri diferite Poate menine informaii de stare pe msur ce viziteaz obiectele

Dezavantaje:
G

Depinde de stabilitatea ierarhiei de obiecte vizitate. Adugarea de obiecte vizitabile rezult n schimbarea implementrii obiectelor Visitor. obiecte de noi tipuri adugate des + multe operaii aplicabile = NU folosii Visitor Expune metode publice care folosesc informaii de stare ale obiectelor. Nu se pot accesa membrii privai ai claselor, necesitatea expunerii acestor informaii (in forma public) ar putea conduce la ruperea ncapsulrii
H

Exemple din API-uri


Visitor este de obicei utilizat pentru structuri arborescente de obiecte:
G

Parcurgerea arborilor de parsare ASTVisitor din Eclipse JDT. Folosind ASTParser se creeaza arborele de parsare al codului dat ca intrare, iar ASTVisitor parcurge arborele, oferind metode (preVisit, postVisit, visit) pentru multe tipuri de noduri (MethodDeclaration, Assignment, IfStatement etc.) Parcurgerea i vizitarea ierarhiei de directoare i fiiere Java Nio - FileVisitor FileVisitor - interfaa cu metode de vizitare trebuie apelat Files.walkFileTree transmindu-i ca parametru un obiect care implementeaz FileVisitor un tutorial
H H I I I

Exerciii
1. (1p) Scheletul de laborator conine implementarea folosind Visitor a scenariului Employee-Boss descris mai sus. Rulai codul i observai comportamentul i interaciunea dintre obiectele vizitate i obiectul de tip Visitor. 2. (6p) Obiectele Employee-Boss pot fi reprezentate printr-o structur arborescent, ce are ca rdcin un Boss (ceo-ul). Creai un Visitor care s permit parcurgerea ierarhiei i efectuarea unei aciuni pe fiecare nod. Acea aciune este practic o operaie, implementat ntr-o alt clas de tip Visitor, deci TreeVisitor-ul va primi un obiect de tip Visitor pe care s l aplice pe nodurile parcurse. fiecare Boss va ine referine ctre angajaii aflai sub rspunderea lui direct (ce pot fi ali sefi la rndul lor, sau salariai obinuii)
H

Programare Orientat pe Obiecte - http://elf.cs.pub.ro/poo/

Last update: 2013/12/11 10:27


H

laboratoare:visitor

http://elf.cs.pub.ro/poo/laboratoare/visitor

implementai un TreeVisitor care pentru: Employee - aplic operaia primit Boss - parcurge subordonaii i apoi aplic operaia primit pe Boss implementai un AverageIncomeVisitor care calculeaz venitul mediu pe toate compania (sum_salary/num_employees) 3. (3p) Adugai nc un tip de obiect vizitabil - Intern. Acesta nu are salariu, doar nume i durata (n luni) a internship-ului. modificai clasele existente deja, pentru a lua n considerare i obiectele Intern testai operaiile de la exerciiile anterioare pe o colecie care conine i obiecte Intern Observai modificrile pe care le-ai efectuat pentru a aduga o nou operaie (ex. 2) i pe cele pentru a aduga un nou tip de obiect n colecie. Ca s merite s aplicm pattern-ul Visitor, ce situaie ar trebui s fie evitat? 4. (bonus - 2p) Calculai recursiv dimensiunea n bytes a unui director folosind java.nio. implementai un FileVisitor, extinznd SimpleFileVisitor n care suprascriei metoda de vizitare a fiierelor un exemplu similar gsii n acest tutorial Clasele din API-ul nio folosite pentru acest exercitiu sunt disponibil din jdk 7.
I I H H H H H I I

Resurse
G G G

Schelet cod Soluii PDF laborator

Referine
1. Vlissides, John, et al. Design patterns: Elements of reusable object-oriented software. Addison-Wesley (1995). 2. Smith, Jason. Elemental Design Patterns. Addison-Wesley, 2012. 3. Clasificarea design pattern-urilor

From: http://elf.cs.pub.ro/poo/ - Programare Orientat pe Obiecte Permanent link: http://elf.cs.pub.ro/poo/laboratoare/visitor Last update: 2013/12/11 10:27

http://elf.cs.pub.ro/poo/

Printed on 2013/12/11 14:52

S-ar putea să vă placă și