This shows you the differences between two versions of the page.
poo-ca-cd:laboratoare:visitor [2024/11/10 17:26] silvia_elena.nistor [Exerciţii] |
poo-ca-cd:laboratoare:visitor [2024/11/13 09:56] (current) silvia_elena.nistor [Exerciţii] |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ===== Laboratorul 7: Visitor pattern ===== | + | ===== Laboratorul 6: Visitor pattern ===== |
**Video introductiv:** [[https://www.youtube.com/watch?v=_mfLYYInv6c| link ]] | **Video introductiv:** [[https://www.youtube.com/watch?v=_mfLYYInv6c| link ]] | ||
Line 9: | Line 9: | ||
==== Visitor Design Pattern ==== | ==== Visitor Design Pattern ==== | ||
- | <note>Design pattern-urile reprezintă soluții generale și reutilizabile ale unei probleme comune în design-ul software. Un design pattern este o descriere a soluției sau un template ce poate fi aplicat pentru rezolvarea problemei, nu o bucata de cod ce poate fi aplicata direct. În general pattern-urile orientate pe obiect arată relațiile și interacțiunile dintre clase sau obiecte, fără a specifica însă forma finală a claselor sau a obiectelor implicate.</note> | + | <note>Design pattern-urile reprezintă soluții generale și reutilizabile ale unei probleme comune în design-ul software. Un design pattern este o descriere a soluției sau un template ce poate fi aplicat pentru rezolvarea problemei, nu o bucată de cod ce poate fi aplicată direct. În general, pattern-urile orientate pe obiect arată relațiile și interacțiunile dintre clase sau obiecte, fără a specifica însă forma finală a claselor sau a obiectelor implicate.</note> |
- | //Visitor// este un **behavioral design pattern** ce oferă posibilitatea de a adăuga în mod __extern__ funcționalități pe o întreagă ierarhie de clase fără să fie nevoie să modificăm efectiv structura acestora. | + | //Visitor// este un **behavioural design pattern** ce oferă posibilitatea de a adăuga în mod __extern__ funcționalități pe o întreagă ierarhie de clase, fără să fie nevoie să modificăm efectiv structura acestora. |
- | Acest pattern este behavioral (//comportamental//) pentru că definește modalități de comunicare între obiecte. | + | Acest pattern este behavioural (//comportamental//) pentru că definește modalități de comunicare între obiecte. |
=== Aplicabilitate === | === Aplicabilitate === | ||
Pattern-ul **Visitor** este util când: | Pattern-ul **Visitor** este util când: | ||
- | * se doreşte prelucrarea unei //structuri complexe//, ce cuprinde mai multe obiecte de //tipuri diferite// | + | * se dorește prelucrarea unei //structuri complexe//, ce cuprinde mai multe obiecte de //tipuri diferite// |
- | * se dorește definirea de operații specifice pentru aceeași structură, fără a polua interfeţele claselor implicate, cu multe detalii specifice algoritmilor. Vizitatorul centralizează logica comună, păstrând în același timp detaliile specifice în interiorul acestuia. | + | * se dorește definirea de operații specifice pentru aceeași structură, fără a polua interfețele claselor implicate, cu multe detalii specifice algoritmilor. Vizitatorul centralizează logica comună, păstrând în același timp detaliile specifice în interiorul acestuia. |
- | * ** clasele ce se doresc prelucrate se modifică rar, în timp ce operaţiile de prelucrare se definesc des**. Vizitatorul permite adăugarea de noi funcționalități fără modificarea claselor existente. | + | * ** clasele ce se doresc prelucrate se modifică rar, în timp ce operațiile de prelucrare se definesc des**. Vizitatorul permite adăugarea de noi funcționalități fără modificarea claselor existente. |
=== Structură === | === Structură === | ||
Line 44: | Line 44: | ||
**Visitable:** | **Visitable:** | ||
- | * Este o interfață pentru obiecte pe care pot fi aplicate operațiile | + | * Este o interfață pentru obiecte pe care pot fi aplicate operațiile. |
* Această operație permite unui obiect să fie "vizitat" de către un obiect "Visitor". | * Această operație permite unui obiect să fie "vizitat" de către un obiect "Visitor". | ||
* Exemplu: Interfața Visitable cu metoda accept(Visitor visitor). | * Exemplu: Interfața Visitable cu metoda accept(Visitor visitor). | ||
**ConcreteVisitable:** | **ConcreteVisitable:** | ||
- | * Aceste clase implementează interfața Visitable sau clasa și definesc operația accept. | + | * Aceste clase implementează interfața sau clasa Visitable și definesc operația accept. |
* Prin intermediul acestei operații, obiectul "Vizitabil" primește un obiect "Visitor". | * Prin intermediul acestei operații, obiectul "Vizitabil" primește un obiect "Visitor". | ||
* Exemplu: Clasele ConcreteElementA, ConcreteElementB, etc., care implementează interfața Visitable și definesc metoda accept. | * Exemplu: Clasele ConcreteElementA, ConcreteElementB, etc., care implementează interfața Visitable și definesc metoda accept. | ||
- | <note tip> Flowul aplicării acestui pattern: | + | <note tip> Flow-ul aplicării acestui pattern: |
- Când un client dorește să efectueze operații pe obiectele vizitabile, el creează un obiect vizitator corespunzător, le "vizitează" apelând metoda accept, iar fiecare obiect vizitabil interacționează cu vizitatorul prin intermediul metodelor visit. | - Când un client dorește să efectueze operații pe obiectele vizitabile, el creează un obiect vizitator corespunzător, le "vizitează" apelând metoda accept, iar fiecare obiect vizitabil interacționează cu vizitatorul prin intermediul metodelor visit. | ||
- Acest pattern oferă o modalitate de a separa algoritmii de obiectele pe care operează, facilitând extinderea și adăugarea de noi operații fără a modifica clasele obiectelor vizitabile. | - Acest pattern oferă o modalitate de a separa algoritmii de obiectele pe care operează, facilitând extinderea și adăugarea de noi operații fără a modifica clasele obiectelor vizitabile. | ||
Line 63: | Line 63: | ||
**Visitor și structurile de date** | **Visitor și structurile de date** | ||
- | Aparent, folosirea lui //accept// este artificială. De ce nu declanşăm vizitarea unui obiect, apelând **direct** //v.visit(e)// atunci când dorim vizitarea unui obiect oarecare? Răspunsul vine însă chiar din situaţiile în care vrem să folosim pattern-ul; vrem să lăsăm structura internă a colecţiei să facă aplicarea vizitatorilor. Cu alte cuvinte vizitatorul se ocupă de fiecare obiect în parte, iar colecţia îl "plimbă" prin elementele sale. De exemplu, când dorim să vizităm un arbore: | + | Aparent, folosirea lui //accept// este artificială. De ce nu declanșăm vizitarea unui obiect, apelând **direct** //v.visit(e)// atunci când dorim vizitarea unui obiect oarecare? Răspunsul vine însă chiar din situațiile în care vrem să folosim pattern-ul; vrem să lăsăm structura internă a colecţiei să facă aplicarea vizitatorilor. Cu alte cuvinte, vizitatorul se ocupă de fiecare obiect în parte, iar colecţia îl "plimbă" prin elementele sale. De exemplu, când dorim să vizităm un arbore: |
- | * declanşarea vizitării se va face printr-un apel ''accept'' pe un prim obiect (e.g. rădacina arborelui) | + | * declanşarea vizitării se va face printr-un apel ''accept'' pe un prim obiect (e.g. rădăcina arborelui) |
- | * elementul curent este vizitat, prin apelul ''v.visit(this)'' | + | * elementul curent este vizitat prin apelul ''v.visit(this)'' |
* pe lângă vizitarea elementului curent, este necesar sa declanşăm vizitarea //tuturor elementelor accesibile din elementul curent// (e.g. nodurile-copil din arbore etc). Realizăm acest lucru apelând ''accept'' pe //fiecare// dintre aceste elemente. Acest comportament depinde de logica structurii. | * pe lângă vizitarea elementului curent, este necesar sa declanşăm vizitarea //tuturor elementelor accesibile din elementul curent// (e.g. nodurile-copil din arbore etc). Realizăm acest lucru apelând ''accept'' pe //fiecare// dintre aceste elemente. Acest comportament depinde de logica structurii. | ||
</note> | </note> | ||
Line 117: | Line 117: | ||
Ne interesează să interogăm toţi angajaţii noştri asupra //venitului lor total//. Observăm că: | Ne interesează să interogăm toţi angajaţii noştri asupra //venitului lor total//. Observăm că: | ||
- | * anagajaţii obişnuiţi au salariul ca unic venit | + | * angajaţii obişnuiţi au salariul ca unic venit |
* şefii posedă, pe lângă salariu, un posibil bonus | * şefii posedă, pe lângă salariu, un posibil bonus | ||
- | Varianta la îndemână ar fi să definim, în fiecare din cele doua clase, câte o metodă, //getTotalRevenue()//, care întoarce salariul pentru angajaţi, respectiv suma dintre salariu şi bonus pentru şefi: | + | Varianta la îndemână ar fi să definim în fiecare din cele doua clase, câte o metodă, //getTotalRevenue()//, care întoarce salariul pentru angajaţi, respectiv suma dintre salariu şi bonus pentru şefi: |
<code java> | <code java> | ||
Line 198: | Line 198: | ||
Secvenţele de cod de mai sus definesc: | Secvenţele de cod de mai sus definesc: | ||
- | * o interfaţă, **Visitor**, ce reprezintă un //algoritm// oarecare, ce va putea vizita orice clasă. Observaţi definirea câte //unei metode visit(...)// pentru //fiecare clasă ce va putea fi vizitată// | + | * o interfaţă, **Visitor**, ce reprezintă un //algoritm// oarecare, ce va putea vizita orice clasă. Observaţi definirea câte //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ă. | * o interfaţă, **Visitable**, a carei metodă ''accept(Visitor)'' permite rularea unui algoritm pe structura curentă. | ||
* implementări ale metodei ''accept(Visitor)'', în cele două clase, care, pur şi simplu, solicită vizitarea instanţei curente de către vizitator. | * implementări ale metodei ''accept(Visitor)'', în cele două clase, care, pur şi simplu, solicită vizitarea instanţei curente de către vizitator. | ||
- | * o implementare a unei operații aplicabilă pe obiectele de tip Visitable | + | * o implementare a unei operații aplicabilă pe obiectele de tip Visitable. |
În exemplul de mai sus, putem identifica : | În exemplul de mai sus, putem identifica : | ||
Line 209: | Line 209: | ||
=== Double-dispatch === | === Double-dispatch === | ||
- | Mecanismul din spatele pattern-ului Visitor poartă numele de **double-dispatch**. Acesta este un concept raspândit, şi se referă la faptul că metoda apelată este determinată la //runtime// de doi factori. În exemplul Employee-Manager, efectul vizitarii, solicitate prin apelul ''e.accept(v)'', depinde de: | + | Mecanismul din spatele pattern-ului Visitor poartă numele de **double-dispatch**. Acesta este un concept răspândit, şi se referă la faptul că metoda apelată este determinată la //runtime// de doi factori. În exemplul Employee-Manager, efectul vizitării, solicitate prin apelul ''e.accept(v)'', depinde de: |
* tipul elementului vizitat, ''e'' (//Employee// sau //Manager//), pe care se invocă metoda | * tipul elementului vizitat, ''e'' (//Employee// sau //Manager//), pe care se invocă metoda | ||
* tipul vizitatorului, ''v'' (//RevenueVisitor//), care conţine implementările metodelor //visit// | * tipul vizitatorului, ''v'' (//RevenueVisitor//), care conţine implementările metodelor //visit// | ||
Line 260: | Line 260: | ||
==== Exerciţii ==== | ==== Exerciţii ==== | ||
- | Dorim să prelucrăm forme geometrice, pe care să le afișăm în diverse formate: text și JSON [https://datatracker.ietf.org/doc/html/rfc8259]. Pentru un design decuplat între formele prelucrate și tipurile de formate dorite, implementați conversia folosind patternul Visitor. | + | Dorim să prelucrăm forme geometrice, pe care să le afișăm în diverse formate: text și JSON https://datatracker.ietf.org/doc/html/rfc8259. Pentru un design decuplat între formele prelucrate și tipurile de formate dorite, implementați conversia folosind patternul Visitor. |
Problema de pe DevMind va avea două task-uri, corespunzătoare celor două tipuri de Visitor. Pentru simplitatea implementării acestor Visitors, vă sugerăm să urmăriți TODO-urile din schelet. | Problema de pe DevMind va avea două task-uri, corespunzătoare celor două tipuri de Visitor. Pentru simplitatea implementării acestor Visitors, vă sugerăm să urmăriți TODO-urile din schelet. | ||
- | * Vom avea trei tipuri de forme geometrice care implementează interfața comună "**Shape**": **Dot**, **Circle**, **Triangle**. Aceste tipuri de forme vor accepta obiecte Visitor pentru a putea permite afișarea lor în cele două formate. | + | * Vom avea trei tipuri de forme geometrice care implementează interfața comună "**Shape**": **Dot**, **Circle**, **Rectangle**. Aceste tipuri de forme vor accepta obiecte Visitor pentru a putea permite afișarea lor în cele două formate. |
* Vom avea două tipuri de Visitor care implementează interfața comună "**Visitor**": **TextVisitor** și **JsonVisitor**. Fiecare Visitor va implementa metoda visit(), care va aplica modalitatea de afișare specifică pe obiectul primit ca parametru. | * Vom avea două tipuri de Visitor care implementează interfața comună "**Visitor**": **TextVisitor** și **JsonVisitor**. Fiecare Visitor va implementa metoda visit(), care va aplica modalitatea de afișare specifică pe obiectul primit ca parametru. | ||
* Scheletul conține în fiecare clasă copil a tipului Shape, câmpuri specifice formei geometrice. Pentru acestea, va trebui să creați getters și setters. | * Scheletul conține în fiecare clasă copil a tipului Shape, câmpuri specifice formei geometrice. Pentru acestea, va trebui să creați getters și setters. | ||
Line 274: | Line 274: | ||
Exemplu de format text | Exemplu de format text | ||
- | { | + | Circle - radius = 30 |
- | Circle - radius = 30 | + | |
- | } | + | |