Differences

This shows you the differences between two versions of the page.

Link to this comparison view

poo-ca-cd:laboratoare:visitor [2020/08/12 10:10]
florin.mihalache
poo-ca-cd:laboratoare:visitor [2023/11/19 14:55] (current)
aconstantinescu1704
Line 1: Line 1:
-===== Overriding, ​Overloading ​& Visitor pattern =====+===== Laboratorul 7: Overriding, ​overloading ​& Visitor pattern ===== 
 + 
 +**Video introductiv:​** [[https://​www.youtube.com/​watch?​v=_mfLYYInv6c| link ]]
  
 ==== Obiective ==== ==== Obiective ====
  
-Scopul acestui laborator este aprofundarea noțiunilor de programare orientată pe obiecte întalnite in laboratoarele precedente, prezentarea ​design pattern-ului Visitor și familiarizarea cu situațiile în care acesta este util de aplicat.+  * Implementarea polimorfismului în Java 
 +  * Diferența dintre Overriding & Overloading 
 +  * Prezentarea ​design pattern-ului Visitor și familiarizarea cu situațiile în care acesta este util de aplicat
  
 +==== Polimorfismul ====
  
-==== Overloading ====+**Polimorfismul reprezintă abilitatea unei clase să se comporte ca o altă clasă de pe lanțul de moștenire, și de aceea conceptul de suprascriere a metodelor este foarte strâns legat.**
  
-Supraîncarcarea ​se referă la posibilitatea ​de a avea într-o clasă mai multe metode cu același numedar implementari diferiteÎn Java, compilatorul poate distinge între metode pe baza semnăturii lor, acesta fiind mecanismul ​din spatele supraîncărcarii+Mai precis, polimorfismul permite obiectelor de tipuri diferite să fie tratate folosind o interfață comună sau o clasă de bază. 
 +Există două tipuri principale de polimorfism în POO: polimorfismul prin suprascrierea metodelor (override) și polimorfismul prin supraincarcarea metodelor (overloading). 
 + 
 +==== Overriding ==== 
 +  
 +**Suprascrierea** ​se referă la redefinirea metodelor existente în clasa părinte ​de către clasa copil în vederea specializării acestora. 
 +  
 +  * Metodele din clasa parinte nu sunt modificate.  
 +  * Putem suprascrie doar metodele vizibile pe lanțul de moștenire (publicprotected) 
 +  * O metodă din clasa copil suprascrie metoda din clasa părinte dacă are **același tip de return** și **aceeași semnatură**.
  
-**Semnătura (signature)** unei metode constă în:+<note important> ​**Semnătura (signature)** unei metode constă în:
   * numele metodei   * numele metodei
   * numărul și tipul parametrilor   * numărul și tipul parametrilor
 + </​note>​
 + 
 +În cazul suprascrierii se determină ce metodă va fi apelată, în mod dinamic, **la runtime**. Explicația este că decizia se face pe baza __tipului obiectului__ care apelează metoda, deci a instanței, care e cunoscută la runtime. ​
 +Din acest motiv, suprascrierea este cunoscută și ca **polimorfism dinamic** (__Runtime polymorphism__). ​
  
-Opțional, pe lângă semnătura metodei poate fi menționat și tipul excepțiilor ce pot fi aruncate din codul acesteia. +  
- +<note important>​ La apelarea unei metode suprascrise,​ Java se uită la tipul intern al obiectului pentru care este apelată metoda, NU la referință. Astfel dacă referința are tipul clasei părinte, dar tipul obiectului ​este al clasei copil, JVM va apela metoda din clasa copil. </​note>​ 
-<note important>​ +  
-Tipul de return al unei metode nu face parte din semnătura acesteia. Din acest motiv simpla modificare a tipului de return al unei metode nu este suficientă pentru supraîncărcare. Ceea ce vom primi este o eroare de compilare. +Câteva ​**reguli pentru suprascriere** sunt:
-<code java> +
-public class TRex { +
-    public void eat(Triceratops victim) { +
-        System.out.println("​Take 5 huge bites"​);​ +
-    } +
-     +
-    public boolean eat(Triceratops victim) { +
-        boolean satisfaction = false; +
-        if (victim.isJuicy()) { +
-            System.out.println("​Eat and be satisfied"​);​ +
-            satisfaction = true; +
-        } +
-        return satisfaction;​ +
-    } +
-     +
-    // Error "​Duplicate method eat(Triceratops)"​ in type TRex +
-</​code>​ +
-Observăm de asemenea că la compilare nu se ține cont de numele dat parametrilor. Astfel modificarea acestuia din //victim// în //dino//, spre exemplu, nu constituie o supraîncărcare validă. +
-</​note>​ +
- +
-Mai jos avem un exemplu valid de supraîncărcare pentru metoda // eat//: +
-<code java> +
-public class TRex { +
-     +
-    public void eat(Triceratops victim) {                    +
-        System.out.println("​Take 5 huge bites"​);​ +
-    } +
-     +
-    public void eat(Dromaeosaurus victim) {                  // parametru cu tip diferit +
-        System.out.println("​Take 1 single bite"​);​ +
-    } +
-     +
-    public void eat(Human firstCourse,​ Human secondCourse) {  // numar si tipuri diferite de parametrii  +
-        System.out.println("​No humans to eat at the time"​);​ +
-    } +
-     +
-    public int eat(Grass desert) {                             // parametru cu tip diferit, return type este irelevant +
-        System.out.println("​Rather starve"​);​ +
-        return 0; +
-    } +
- +
-    public static void main(String [] args) { +
-        TRex john = new TRex(); +
-         +
-        john.eat(new Triceratops()); ​                    // "Take 5 huge bites"​ +
-        john.eat(new Dromaeosaurus()); ​                  // "Take 1 single bite"​ +
-        john.eat(new Human("​Ana"​),​ new Human("​Andrei"​));​ // "No humans to eat at the time"​ +
-        john.eat(new Grass()); ​                          // "​Rather starve"​  +
-    } +
-+
-</​code>​ +
- +
-O clasă poate supraîncărca metodele moștenite. Constructorii pot fi supraîncărcați. +
- +
-**Supraîncărcarea are loc la compilare**,​ motiv pentru care mai este numită și polimorfism static (//compile time polymorphism//​). În aceasta fază compilatorul decide ce metodă este apelată pe baza __tipului referinței__ și prin analiza numelui și a listei de parametri. La runtime, când este întalnit apelul unei metode supraîncărcate,​ deja se știe unde este codul care trebuie executat. +
- +
- +
-==== Overriding ==== +
- +
-Suprascrierea se referă la redefinirea metodelor existente în clasa părinte de către clasa copil în vederea specializării acestora. Metodele din clasa parinte nu sunt modificate. Putem suprascrie doar metodele vizibile pe lanțul de moștenire (public, protected). O metodă din clasa copil suprascrie metoda din clasa părinte dacă are același tip de return și aceeași semnatură (nume și parametri). +
- +
-Spre deosebire de supraîncărcare care face acest lucru la compilare, în cazul suprascrierii se determină ce metodă va fi apelată, în mod dinamic, **la runtime**. Explicația este că decizia se face pe baza__ tipului obiectului__ care apelează metoda, deci a instanței (cunoscută la runtime). Din acest motiv, suprascrierea este cunoscută și ca polimorfism dinamic (''​Runtime polymorphism''​). **Polimorfismul reprezintă abilitatea unei clase să se comporte ca o altă clasă de pe lanțul de moștenire, și de aceea conceptul de suprascriere a metodelor este foarte strâns legat. Supraîncărcarea,​ fiind la compile-time,​ nu are legătură cu acest polimorfism dinamic.** +
- +
-<note important>​ La apelarea unei metode suprascrise,​ Java se uită la tipul intern al obiectului pentru care este apelată metoda, NU la referință. Astfel dacă referința are tipul clasei părinte, dar tipul este al clasei copil, JVM va apela metoda din clasa copil. </​note>​ +
- +
-Câteva reguli pentru suprascriere sunt:+
   * metoda suprascrisă are același tip de return și semnatură ca metoda inițială   * metoda suprascrisă are același tip de return și semnatură ca metoda inițială
   * putem avea un tip de return diferit de cel al metodei inițiale, atâta timp cat este un tip ce moștenește tipul de return al metodei inițiale   * putem avea un tip de return diferit de cel al metodei inițiale, atâta timp cat este un tip ce moștenește tipul de return al metodei inițiale
Line 90: Line 42:
   * metodele de tip ''​static''​ și ''​final''​ nu pot fi suprascrise   * metodele de tip ''​static''​ și ''​final''​ nu pot fi suprascrise
   * constructorii nu pot fi suprascriși   * constructorii nu pot fi suprascriși
 + 
 În exemplul de mai jos, metodele //purr// și //​getFeatures//​ au fost suprascrise de tipul //​GrumpyCat//​. În exemplul de mai jos, metodele //purr// și //​getFeatures//​ au fost suprascrise de tipul //​GrumpyCat//​.
 <code java> <code java>
Line 96: Line 48:
 class GrumpyCatFeatures extends CatFeatures { } class GrumpyCatFeatures extends CatFeatures { }
 class GrumpyFeatures { } class GrumpyFeatures { }
 + 
 class Cat { class Cat {
 + 
  public void purr() {  public void purr() {
  System.out.println("​purrrr"​);​  System.out.println("​purrrr"​);​
  }  }
 + 
  public CatFeatures getFeatures() {  public CatFeatures getFeatures() {
  System.out.println("​Cat getFeatures"​);​  System.out.println("​Cat getFeatures"​);​
  return new CatFeatures();​  return new CatFeatures();​
  }  }
 + 
  public final void die() {  public final void die() {
  System.out.println("​Dying! frown emoticon"​);​  System.out.println("​Dying! frown emoticon"​);​
  }  }
 } }
 + 
 class GrumpyCat extends Cat { class GrumpyCat extends Cat {
         @Override         @Override
Line 141: Line 93:
  }  }
 } }
 + 
 </​code>​ </​code>​
- +  
- +  
-<note important>​**Adnotarea** (**Annotation**) ''​@Override''​ este complet opțională. Totuși este indicat să o includeți mereu când suprascrieți o metodă. Motivele sunt simple:+<note important>​**Adnotarea** (**Annotation**) ''​@Override''​ este complet opțională. Totuși este indicat să o includeți mereu când suprascrieți o metodă. ​ 
 +Motivele sunt simple:
   * Compilatorul vă va anunța printr-o eroare dacă ați greșit numele metodei sau tipul parametrilor și această nouă metodă nu suprascrie de fapt o metodă a părintelui   * Compilatorul vă va anunța printr-o eroare dacă ați greșit numele metodei sau tipul parametrilor și această nouă metodă nu suprascrie de fapt o metodă a părintelui
   * Face codul vostru mai ușor de citit, pentru că devine evident când o metodă suprascrie o altă metodă ​   * Face codul vostru mai ușor de citit, pentru că devine evident când o metodă suprascrie o altă metodă ​
 </​note>​ </​note>​
 + 
 +<note tip>
 +O metodă cu argumente de tip primitiv nu poate fi suprascrisă cu o metodă cu tip wrapper.
 + 
 +''​public void doSmth(int x)''​ nu poate fi suprascrisă cu ''​public void doSmth(Integer x)''​
 + 
 +Metoda cu argument de tip wrapper poate primi si null, insă cea cu tipul primitiv nu, de aceea, neputând să fie păstrată echivalența,​ nu este permisă aceasta suprascriere
 + 
 +</​note>​
 + 
  
-=== super ===+==== Overloading ​==== 
 +  
 +**Supraîncarcarea** se referă la posibilitatea de a avea într-o clasă mai multe metode cu același nume, dar implementari diferite. În Java, compilatorul poate distinge între metode pe baza semnăturii lor, acesta fiind mecanismul din spatele supraîncărcarii.  
 +  
 +Opțional, pe lângă semnătura metodei poate fi menționat și tipul excepțiilor ce pot fi aruncate din codul acesteia.
  
-În laboratorul [[:​poo-ca-cd:​laboratoare:​agregare-mostenire#​cuvantul-cheie-super-intrebuintari|]] am folosit cuvântul cheie **super** pentru a invoca un anumit constructor ​din clasa părinte dar și pentru ​apela în mod explicit metoda din clasa părinte în cazul metodelor suprascrise.+<note important>​ 
 +Tipul de return al unei metode NU face parte din semnătura acesteia. Din acest motiv simpla modificare ​tipului de return al unei metode nu este suficientă pentru supraîncărcare. Ceea ce vom primi este o eroare de compilare. 
 +<code java> 
 +public class TRex { 
 +    public void eat(Triceratops victim) { 
 +        System.out.println("​Take 5 huge bites"​);​ 
 +    } 
 +     
 +    public boolean eat(Triceratops victim) { 
 +        boolean satisfaction = false; 
 +        if (victim.isJuicy()) { 
 +            System.out.println("​Eat and be satisfied"​);​ 
 +            satisfaction = true; 
 +        } 
 +        return satisfaction;​ 
 +    } 
 +     
 +    // Error "​Duplicate method eat(Triceratops)"​ in type TRex 
 +</​code>​ 
 +Observăm de asemenea că la compilare nu se ține cont de numele dat parametrilor. Astfel modificarea acestuia din //​victim// ​în //dino//, spre exemplu, nu constituie o supraîncărcare validă. 
 +</​note>​
  
-Rescriem metoda ''​purr()''​ din clasa ''​GrumpyCat''​ astfel:+  * O clasă poate supraîncărca metodele moștenite.  
 +  * Constructorii pot fi supraîncărcați. 
 + 
 +Spre deosebire de suprascriere,​ **supraîncărcarea are loc la compilare**,​ motiv pentru care mai este numită și **polimorfism static** ​(__compile time polymorphism__). În aceasta fază compilatorul decide ce metodă este apelată pe baza __tipului referinței__ și prin analiza numelui și a listei de parametri. La runtime, când este întalnit apelul unei metode supraîncărcate,​ deja se știe unde este codul care trebuie executat. 
 + 
 +Mai jos avem un exemplu valid de supraîncărcare pentru metoda // eat//:
 <code java> <code java>
-@Override +public class TRex { 
-public void purr() { +     
-    ​super.purr(); +    ​public void eat(Triceratops victim) {                    
-    System.out.println("​NO!");+        System.out.println("​Take 5 huge bites"​);​ 
 +    ​
 +     
 +    public void eat(Dromaeosaurus victim) {                  // parametru cu tip diferit 
 +        System.out.println("Take 1 single bite"); 
 +    ​
 +     
 +    public void eat(Human firstCourse,​ Human secondCourse) { // numar si tipuri diferite de parametrii  
 +        ​System.out.println("​No humans to eat at the time"); 
 +    } 
 +     
 +    public int eat(Grass desert) {                           // parametru cu tip diferit, return type este irelevant 
 +        System.out.println("​Rather starve"​);​ 
 +        return 0; 
 +    } 
 +  
 +    public static void main(String [] args) { 
 +        TRex john = new TRex(); 
 +         
 +        john.eat(new Triceratops()); ​                    // "Take 5 huge bites"​ 
 +        john.eat(new Dromaeosaurus()); ​                  // "Take 1 single bite"​ 
 +        john.eat(new Human("​Ana"​),​ new Human("​Andrei"​));​ // "No humans to eat at the time"​ 
 +        john.eat(new Grass()); ​                          // "​Rather starve"​  
 +    }
 } }
 </​code>​ </​code>​
-La apelul metodei pe o instanță a clasei ''​GrumpyCat''​ output-ul va fi: 
-<​code>​ 
-purrrr 
-NO! 
-</​code>​ 
-== Visitor == 
  
-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. ​+==== Visitor ​Design ​Pattern ====
  
-Design pattern-ul ​//Visitor// oferă ​modalitate de //​separa ​un algoritm ​de structura ​pe care acesta operează//. Avantajul constă în faptul că putem adauga noi posibilităţde prelucrare a structurii, fără ​să o modificăm. Extrapolând,​ folosind //​Visitor//,​ putem adăuga noi funcţii care realizează prelucrări asupra unei familii de clase, fără a modifica efectiv structura ​claselor. ​+<​note>​Design pattern-urile reprezintă soluții generale și reutilizabile ale unei probleme comune în design-ul software. Un design pattern este descriere ​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 șinteracțiunile dintre clase sau obiecte, fără ​a specifica însă forma finală a claselor ​sau a obiectelor implicate.</​note> ​
  
-Acest pattern este comportamental (//behavioral//) pentru că definește modalități de comunicare între obiecte+//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
  
-//Cum recunoaștem o situație în care Visitor e aplicabil?// +Acest pattern este behavioral (//comportamental//pentru ​că definește modalitățde comunicare între ​obiecte
-* Mai multe obiecte și operații ​pentru ​acestea +
-* Schimbarea/​adăugarea operațiilor fără a modifica clasele +
-* Elemente heterogene - tipuri diferite ​de obiecte ​pe care se aplică mai multe operații+
  
-Decizia de utilizare a pattern-ului Visitor este în strânsă legătură cu __stabilitatea ierarhiilor de clase prelucrate__:​ dacă noi clase copil sunt adăugate rar, atunci ​se poate aplica acest pattern (într-o manieră eficientă), altfel nu este indicat.+=== Aplicabilitate === 
 + 
 +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 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.
  
 === Structură ===  === Structură === 
  
-<​imgcaption image1|Componente pattern Visitor >{{ .:​visitor:​visitor.png?​680 | }}</​imgcaption>​+{{ .:​visitor:​visitor.png?​680 | Componente pattern Visitor ​}}
  
-**Visitor** - o interfață pentru operația aplicată  +Structura design pattern-ului "Visitor" este formată din următoarele componente:
-**Visitable** - o interfață pentru obiecte pe care pot fi aplicate operațiile (în diagramă este numită ''​Element''​) +
-   * metoda ''​accept''​ e independentă de tipul concret al Visitor-ului +
-   * în ''​accept''​ se folosește obiectul de tip Visitor ​ +
-Pentru fiecare algoritm/​operație ce trebuie aplicată, se implementează clase de tip Visitor. În fiecare obiect de tip //Visitor// trebuie să implementăm metode care aplică operația pentru fiecare tip de element vizitabil.+
  
-<note tip>În <imgref image2> este reprezentat ​**flow-ul aplicării acestui pattern**: +**Client:** 
-   ​Clientul este cel care folosește o colecție ​de obiecte de unul sau mai multe tipuri, ​și dorește ​să aplice pe acestea diferite operații (în exercițiile din laborator clientul este practic programul vostru de test - main-ul). Clientul folosește obiecte //Visitor// create ​pentru ​fiecare operație necesară. +   * Este clasa consumatoare a design pattern-ului "​Visitor"​. 
-  - Clientul parcurge colecția și în loc să aplice operaţia direct pe fiecare obiect ​de tip //​Element//,​ îi oferă acestuia un obiect de tip //​Visitor//​.  +   * Are acces la obiectele din structura ​de date și poate instrui aceste obiecte ​să accepte un "Visitor" ​pentru ​a realiza prelucrările corespunzătoare
-  ​Obiectul de tip //Element// apelează metoda ​de "​vizitare"​ oferită de //Visitor// +   * Exemplu: O aplicație care procesează diferite tipuri ​de elemente într-o structură de date complexă.
-  - Pe obiectul //Visitor// se apelează metoda //visit// corespunzătoare obiectului, iar în ea se efectuează operația. (:!: în Visitor folosim conceptul de //​overloading//​ pentru fiecare metodă //visit//)  +
-</​note>​+
  
-<​imgcaption image2|Interacțiunile dintre componentele pattern-ului ​Visitor>​{{ ​.:visitor:visitor-flow.png |}}</imgcaption>+**Visitor:​** 
 +   * Este o interfață sau o clasă abstractă folosită pentru a declara operațiile de vizitare pentru toate tipurile de clase vizitabile. 
 +   * Conține metode de vizitare corespunzătoare fiecărui tip de clasă vizitabilă. 
 +   * Exemplu: Interfața ​Visitor ​cu metodele visit(ElementA elementA), visit(ElementB elementB), etc. 
 + 
 +**ConcreteVisitor:** 
 +   * Pentru fiecare tip de "​Visitor",​ toate metodele de vizitare definite în "​Visitor"​ trebuie implementate. 
 +   * Fiecare "​Visitor"​ este responsabil pentru diferite operații. 
 +   * ExempluClasa ConcreteVisitorA implementând interfața Visitor cu metodele sale specifice pentru tratarea diferitelor tipuri de elemente. 
 + 
 +**Visitable:​** 
 +   * 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"​. 
 +   * Exemplu: Interfața Visitable cu metoda accept(Visitor ​visitor). 
 + 
 +**ConcreteVisitable:​** 
 +   * Aceste clase implementează interfața Visitable sau clasa și definesc operația accept. 
 +   * 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. 
 + 
 + 
 +<note tip> Flowul 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. 
 +  -  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. 
 +</note>
  
 <note important>​ <note important>​
Line 288: Line 317:
   * în cazul în care nu avem acces la codul claselor, singura modalitate de adăugare de funcţionalitate este extinderea   * în cazul în care nu avem acces la codul claselor, singura modalitate de adăugare de funcţionalitate este extinderea
  
-În final, tragem concluzia că este de dorit să **izolăm algoritmii de clasele pe care le prelucrează**. ​O primă idee se referă la utilizarea //metodelor statice//. Dezavantajul acestora este că nu pot reţine, într-un mod elegant, informaţie de stare din timpul prelucrării. De exemplu, dacă structura noastră ar fi arborescentă (recursivă),​ în sensul că o instanţă //Manager// ar putea ţine referinţe la alte instanţe //​Manager//,​ ce reprezintă şefii ierarhic inferiori, o funcţie de prelucrare ar trebui să menţină o informaţie parţială de stare (precum suma procentelor calculate până într-un anumit moment) sub forma unor parametri furnizaţi apelului recursiv: +În final, tragem concluzia că este de dorit să **izolăm algoritmii de clasele pe care le prelucrează**.
-<code java> +
-class Manager extends Employee { +
-        ... +
-        public float getPercentage(float sum, int n) { +
-                float f = bonus / getTotalRevenue();​ +
-                if (f > 0) +
-                        return inferiorManager.getPercentage(sum + f, n + 1); // trimite mai departe cererea catre nivelul inferior ​                +
-                return inferiorManager.getPercentage(sum,​ n); +
-        }         +
-+
-</​code>​+
  
-O abordare ​mai bună ar fi:+O abordare bună ar fi:
   * conceperea claselor cu **posibilitatea de primire/​ataşare a unor obiecte-algoritm**,​ care definesc operaţiile dorite   * conceperea claselor cu **posibilitatea de primire/​ataşare a unor obiecte-algoritm**,​ care definesc operaţiile dorite
   * definirea unor **clase algoritm** care vor __//​**vizita**//​__ structura noastră de date, vor //efectua// prelucrările specifice fiecărei clase, având, totodată, //​posibilitatea de încapsulare a unor informaţii de stare// (cum sunt suma şi numărul din exemplul anterior)   * definirea unor **clase algoritm** care vor __//​**vizita**//​__ structura noastră de date, vor //efectua// prelucrările specifice fiecărei clase, având, totodată, //​posibilitatea de încapsulare a unor informaţii de stare// (cum sunt suma şi numărul din exemplul anterior)
Line 368: Line 386:
 Acest lucru contrastează cu un simplu apel //​e.getTotalRevenue()//,​ pentru care efectul este hotărât doar de tipul anagajatului. Acesta este un exemplu de **single-dispatch**. ​ Acest lucru contrastează cu un simplu apel //​e.getTotalRevenue()//,​ pentru care efectul este hotărât doar de tipul anagajatului. Acesta este un exemplu de **single-dispatch**. ​
  
-[[:​poo-ca-cd::​laboratoare:​tutorial-doubledispatch| Tutorialul de double-dispatch]] oferă mai multe detalii legate de acest mecanism.+<​note>​[[:​poo-ca-cd::​laboratoare:​tutorial-doubledispatch| Tutorialul de double-dispatch]] oferă mai multe detalii legate de acest mecanism.</​note>​
  
-=== Aplicabilitate ​===+=== Cum implementăm? ​===
  
-Pattern-ul **Visitor** este util când: +  ​Se declară interfața care să reprezinte elementul nostrucare va conține și metoda ''​public void accept(ElementVisitor elementVisitor);''​ 
-  * se doreşte prelucrarea unei //structuri complexe//ce cuprinde mai multe obiecte de //tipuri diferite// +  ​- Se creează clasele concrete care implementează interfațdeclarată anteriorBody-ul pentru metoda accept va conține obligatoriu ''​elementVisitor.visit(this);''​ 
-  ​* se doreşte definirea de //operaţii distincte pe aceeaşi structură//, pentru ​preveni poluarea interfeţelor claselor implicate, cu multe detalii aparţinând unor algoritmi diferiţiÎn acest fel, se centralizează aspectele legate de acelaşi algoritm //într-un singur loc//, dar, în acelaştimp, //se separă detaliile ce ţin de algoritmi diferiţi//​. Acest lucru conduce ​la simplificarea atât a claselor prelucrate, cât şi a vizitatorilor. Orice date specifice algoritmului rezidă în vizitator+  - Se definește o interfață care reprezintă Visitor-ul nostru șcare va conține atâtea metode ​de visit câte clase concrete am creat la pasul anterior (câte o metodă de visit pentru fiecare tip de element)
-  ​* ** clasele ce se doresc prelucrate se modifică rar, în timp ce operaţiile de prelucrare se definesc des**. Dacă însă sunt introduse multe clase visitabile, după crearea obiectelor ​Visitor, ​atunci este necesară modificarea acestora din urmăpentru ​adăugarea ​de metode //visit// pentru noile clase.+  ​- Se creează o clasă concretă care implementează interfața de Visitor, ​unde vom adăuga implementările pentru ​fiecare tip de element în parte. (ex: ''​ElementDisplayVisitor()''​) 
 +  - În main putem testa dacă funcționează așa cum ne dorim iterând print-un arraylist/​vector ​de obiecte de tip Element astfel: ''​elementIterator.accept(new ElementDisplayVisitor());''​ 
 + 
 +=== Avantaje și dezavantaje ===
  
-<note important> ​ 
 **Avantaje:​** **Avantaje:​**
- Decuplarea datelor de operațiile aplicate pe acestea +  ​* Decuplarea datelor de operațiile aplicate pe acestea 
- ​Ușurează adăugarea unor noi operații/​algortimi. Se creează o implementare a unui obiect de tip Visitor și nu se schimbă nimic în obiecte vizitate. +  ​Ușurează adăugarea unor noi operații/​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 +  ​Spre deosebire de Iterator poate gestiona elemente de tipuri diferite 
- Poate menține informații de stare pe măsură ce vizitează obiectele+  ​Poate menține informații de stare pe măsură ce vizitează obiectele
  
 **Dezavantaje:​** **Dezavantaje:​**
-* Depinde de stabilitatea ierarhiei de obiecte vizitate. Adăugarea de obiecte vizitabile rezultă în schimbarea implementării obiectelor Visitor.  +  ​* Depinde de stabilitatea ierarhiei de obiecte vizitate. Adăugarea de obiecte vizitabile rezultă în schimbarea implementării obiectelor Visitor.  
-     ​*:!: obiecte de noi tipuri adăugate des + multe operații aplicabile = NU folosiți Visitor +  * :!: obiecte de noi tipuri adăugate des + multe operații aplicabile = NU folosiți Visitor 
-* Expune metode publice care folosesc informații de stare ale obiectelor. Nu se pot accesa membrii privați ai claselor, necesitatea expunerii acestor informaţii (in forma publică) ar putea conduce la //ruperea încapsulării//​ +  * Expune metode publice care folosesc informații de stare ale obiectelor. Nu se pot accesa membrii privați ai claselor, necesitatea expunerii acestor informaţii (in forma publică) ar putea conduce la //ruperea încapsulării//​
-</​note>​+
  
 ==== Exemple din API-uri ==== ==== Exemple din API-uri ====
  
 Visitor este de obicei utilizat pentru structuri arborescente de obiecte: Visitor este de obicei utilizat pentru structuri arborescente de obiecte:
- * Parcurgerea arborilor de parsare +  ​* Parcurgerea arborilor de parsare 
-     * [[:poo-ca-cd:https://​help.eclipse.org/​neon/​index.jsp?​topic=%2Forg.eclipse.jdt.doc.isv%2Freference%2Fapi%2Forg%2Feclipse%2Fjdt%2Fcore%2Fdom%2FASTVisitor.html| ASTVisitor]] din Eclipse JDT. Folosind [[:poo-ca-cd:http://​help.eclipse.org/​neon/​index.jsp?​topic=%2Forg.eclipse.jdt.doc.isv%2Freference%2Fapi%2Forg%2Feclipse%2Fjdt%2Fcore%2Fdom%2FASTParser.html|ASTParser]] se creează arborele de parsare al codului dat ca intrare, iar [[:poo-ca-cd:https://​help.eclipse.org/​neon/​index.jsp?​topic=%2Forg.eclipse.jdt.doc.isv%2Freference%2Fapi%2Forg%2Feclipse%2Fjdt%2Fcore%2Fdom%2FASTVisitor.html|ASTVisitor]] parcurge arborele, oferind metode (//​preVisit//,​ //​postVisit//,​ //visit//) pentru multe tipuri de noduri (MethodDeclaration,​ Assignment, IfStatement etc.) +     * [[https://​help.eclipse.org/​neon/​index.jsp?​topic=%2Forg.eclipse.jdt.doc.isv%2Freference%2Fapi%2Forg%2Feclipse%2Fjdt%2Fcore%2Fdom%2FASTVisitor.html| ASTVisitor]] din Eclipse JDT. Folosind [[http://​help.eclipse.org/​neon/​index.jsp?​topic=%2Forg.eclipse.jdt.doc.isv%2Freference%2Fapi%2Forg%2Feclipse%2Fjdt%2Fcore%2Fdom%2FASTParser.html|ASTParser]] se creează arborele de parsare al codului dat ca intrare, iar [[https://​help.eclipse.org/​neon/​index.jsp?​topic=%2Forg.eclipse.jdt.doc.isv%2Freference%2Fapi%2Forg%2Feclipse%2Fjdt%2Fcore%2Fdom%2FASTVisitor.html|ASTVisitor]] parcurge arborele, oferind metode (//​preVisit//,​ //​postVisit//,​ //visit//) pentru multe tipuri de noduri (MethodDeclaration,​ Assignment, IfStatement etc.) 
- * Parcurgerea și vizitarea ierarhiei de directoare și fișiere +  * Parcurgerea și vizitarea ierarhiei de directoare și fișiere 
-     * Java Nio - [[:​poo-ca-cd:​http://​docs.oracle.com/​javase/​8/docs/api/index.html?java/​nio/​file/​FileVisitor.html | FileVisitor]]+     * Java Nio - [[https://​docs.oracle.com/en/java/javase/12/docs/api/java.base/java/​nio/​file/​FileVisitor.html | FileVisitor]]
         * //​FileVisitor//​ - interfața cu metode de vizitare         * //​FileVisitor//​ - interfața cu metode de vizitare
-        * trebuie apelat [[:​poo-ca-cd:​http://​docs.oracle.com/​javase/​8/​docs/​api/​java/​nio/​file/​Files.html#​walkFileTree(java.nio.file.Path,​%20java.nio.file.FileVisitor) | Files.walkFileTree]] transmițându-i ca parametru un obiect care implementează //​FileVisitor//​ +        * trebuie apelat [[https://​docs.oracle.com/en/java/javase/12/docs/api/java.base/​java/​nio/​file/​Files.html#​walkFileTree(java.nio.file.Path,​java.nio.file.FileVisitor) | Files.walkFileTree]] transmițându-i ca parametru un obiect care implementează //​FileVisitor//​ 
-        * [[:poo-ca-cd:http://​docs.oracle.com/​javase/​tutorial/​essential/​io/​walk.html | un tutorial]] ​+        * [[http://​docs.oracle.com/​javase/​tutorial/​essential/​io/​walk.html | un tutorial]] ​
  
 ==== Summary ==== ==== Summary ====
  
-**Supraîncărcarea (overloading) ** mai multe metode cu același nume dar cu listă diferită de argumente +{{:poo-ca-cd:​laboratoare:lab7.png?​600|}}
-   * metoda care va fi executată este stabilită la //​compilare//,​ pe baza tipului referinței +
-   * metoda supraîncărcată are neapărat o listă diferită de argumente și poate, opțional, avea: +
-     * alți modificatori de acces +
-     * alt tip de return +
-     * alte excepții +
-   * constructorii pot fi supraîncărcati +
-   * metodele moștenite pot fi supraîncărcate+
  
-**Suprascrierea (overriding) ** - redefinirea metodelor moștenite 
-   * metoda care va fi executată este stabilită la //​runtime//,​ pe baza tipului obiectului 
-   * metoda suprascrisă are același tip de return și semnătură ca metoda inițială 
-   * putem avea un tip de return diferit de cel al metodei inițiale, atâta timp cât este un tip ce moștenește tipul de return al metodei inițiale 
-   * specificatorul de access al metodei suprascrise nu poate fi mai restrictiv decât cel al metodei inițiale 
-   * nu poate arunca mai multe excepții sau excepții mai generale, poate însă arunca mai puține sau mai particulare sau excepții unchecked (de runtime) 
-   * metodele de tip ''​static''​ și ''​final''​ nu pot fi suprascrise 
-   * constructorii nu pot fi suprascriși 
  
- +**Visitor** ​= behavioral design ​pattern 
-**Visitor** ​pattern ​pt modelarea comportamentului claselor +  * Util în situații în care:
-  * util în situații în care:+
       * avem mai multe obiecte și operații pentru acestea       * avem mai multe obiecte și operații pentru acestea
       * dorim schimbarea/​adăugarea operațiilor fără a modifica clasele       * dorim schimbarea/​adăugarea operațiilor fără a modifica clasele
-  * indicat ​de utilizat pentru operații pe colecții și parcurgerea de structuri arborescente +  * Indicat ​de utilizat pentru operații pe colecții și parcurgerea de structuri arborescente 
-  * conceptul de [[:​poo-ca-cd:​laboratoare:​tutorial-doubledispatch|double dispatch]]+  * Folosește ​conceptul de [[:​poo-ca-cd:​laboratoare:​tutorial-doubledispatch|double dispatch]]
   ​   ​
 ==== Exerciţii ==== ==== Exerciţii ====
- 
-** Task 1 ** [8p] 
  
 Dorim să prelucrăm bucăți de text pe care să le convertim în diferite formate, momentan dokuwiki și markdown. Pentru un design decuplat între obiectele prelucrate și tipurile de formate dorite, implementați conversia folosind patternul Visitor. Dorim să prelucrăm bucăți de text pe care să le convertim în diferite formate, momentan dokuwiki și markdown. Pentru un design decuplat între obiectele prelucrate și tipurile de formate dorite, implementați conversia folosind patternul Visitor.
  
-* Fișierul **[[:poo-ca-cd:https://​github.com/​oop-pub/​laboratoare/​tree/​master/​visitor/skel|README]]** din scheletul de cod cuprinde informațiile necesare designului dorit. +      ​* Fișierul **[[https://​github.com/​oop-pub/​oop-labs/​tree/​master/​src/lab7|README]]** din scheletul de cod cuprinde informațiile necesare designului dorit. 
-   ​* implementați structura de clase din diagrama din README +        * implementați structura de clase din diagrama din README 
-   ​* implementați TODO-urile din scheletul de cod +        * implementați TODO-urile din scheletul de cod 
-* Pentru simplitatea testării scheletul oferă clasa ''​Test''​ care oferă bucățile de text pe care să le prelucrați. +      * Pentru simplitatea testării scheletul oferă clasa ''​Test''​ care oferă bucățile de text pe care să le prelucrați. 
-   ​* dacă folosiți IntelliJ creați proiect din scheletul de laborator: File -> New Project -> select Java -> select the skel folder +        * dacă folosiți IntelliJ creați proiect din scheletul de laborator: File -> New Project -> select Java -> select the skel folder 
-* În implementare va trebui sa folositi clasa [[:poo-ca-cd:https://​docs.oracle.com/​en/​java/​javase/​12/​docs/​api/​java.base/​java/​lang/​StringBuilder.html|StringBuilder]]. Aceasta este o clasă mutabilă (//​mutable//​),​ spre deosebire de String, care e imutabilă (//​immutable//​). Vă recomandăm [[:poo-ca-cd:https://​www.geeksforgeeks.org/​string-vs-stringbuilder-vs-stringbuffer-in-java/​|acest link]] pentru un exemplu si explicații despre diferențele dintre ele. +      * În implementare va trebui sa folositi clasa [[https://​docs.oracle.com/​en/​java/​javase/​12/​docs/​api/​java.base/​java/​lang/​StringBuilder.html|StringBuilder]]. Aceasta este o clasă mutabilă (//​mutable//​),​ spre deosebire de String, care e imutabilă (//​immutable//​). Vă recomandăm [[https://​www.geeksforgeeks.org/​string-vs-stringbuilder-vs-stringbuffer-in-java/​|acest link]] pentru un exemplu si explicații despre diferențele dintre ele. 
-* //Tips for faster coding//: +      * //Tips for faster coding//: 
-   ​* atunci cand creati o clasa care implementeaza o interfata sau o clasa cu metode abstracte, nu scrieti de mana antetul fiecarei metode, ci folositi-va de IDE. +        * atunci cand creati o clasa care implementeaza o interfata sau o clasa cu metode abstracte, nu scrieti de mana antetul fiecarei metode, ci folositi-va de IDE. 
-     ​* In Intellij va aparea cu rosu imediat dupa ce scrieti extends.../​implements... Dati alt-enter sau option-enter (pe mac), si vi se vor genera metodele pe care trebuie sa le implementati,​ voi completand apoi continutul lor. +        * In Intellij va aparea cu rosu imediat dupa ce scrieti extends.../​implements... Dati alt-enter sau option-enter (pe mac), si vi se vor genera metodele pe care trebuie sa le implementati,​ voi completand apoi continutul lor. 
-   ​* generati constructorii folosind IDE-ul +        * generati constructorii folosind IDE-ul
- +
-** Task 2 ** - Utilizare API implementat folosind Visitor [2p] +
- +
-Afișați folosind ''​java.nio''​ informații despre fișierele cu extensia "​.class"​ sau "​.java"​ dintr-un director.  +
-  * Implementați un [[:​poo-ca-cd:​https://​docs.oracle.com/​en/​java/​javase/​12/​docs/​api/​java.base/​java/​nio/​file/​FileVisitor.html | FileVisitor]],​ extinzând [[:​poo-ca-cd:​https://​docs.oracle.com/​en/​java/​javase/​12/​docs/​api/​java.base/​java/​nio/​file/​SimpleFileVisitor.html | SimpleFileVisitor]] în care suprascrieți metoda de vizitare a fișierelor  +
-  * Un exemplu similar găsiți în [[:​poo-ca-cd:​http://​docs.oracle.com/​javase/​tutorial/​essential/​io/​walk.html | acest tutorial]] +
- +
-==== Resurse ====+
  
-  * {{:​poo-ca-cd:​laboratoare:​visitor:​lab-visitor-skel.zip |Schelet de cod}} 
-  * {{:​poo-ca-cd:​laboratoare:​visitor:​lab-visitor-sol.zip |Soluție}} 
-  * [[:​poo-ca-cd:​laboratoare:​old-exercises|Exerciții din alți ani]] 
-  * [[https://​github.com/​oop-pub/​laboratoare/​tree/​master/​visitor|Github repository]] 
  
  
 ==== Referințe ==== ==== Referințe ====
     - Kathy Sierra, Bert Bates. //SCJP Sun Certified Programmer for Java™ 6 - Study Guide//. Chapter 2 - Object Orientation ([[http://​firozstar.tripod.com/​_darksiderg.pdf|available online]]) - moștenire, polimorfism,​ overriding, overloading + exemple de întrebări     - Kathy Sierra, Bert Bates. //SCJP Sun Certified Programmer for Java™ 6 - Study Guide//. Chapter 2 - Object Orientation ([[http://​firozstar.tripod.com/​_darksiderg.pdf|available online]]) - moștenire, polimorfism,​ overriding, overloading + exemple de întrebări
-  - Vlissides, John, et al. //Design patterns: Elements of reusable object-oriented software//. Addison-Wesley (1995) ([[https://sophia.javeriana.edu.co/~cbustaca/​docencia/​DSBP-2018-01/recursos/​Erich%20Gamma,%20Richard%20Helm,​%20Ralph%20Johnson,​%20John%20M.%20Vlissides-Design%20Patterns_%20Elements%20of%20Reusable%20Object-Oriented%20Software%20%20-Addison-Wesley%20Professional%20(1994).pdf|available online]])+  - Vlissides, John, et al. //Design patterns: Elements of reusable object-oriented software//. Addison-Wesley (1995) ([[http://index-of.co.uk/Software-Engineering/Design%20Patterns%20-%20Elements%20Of%20Reusable%20Object-Oriented%20Software%20-%20Addison%20Wesley.pdf|available online]])
   - [[http://​en.wikipedia.org/​wiki/​Software_design_pattern | Clasificarea design pattern-urilor]]   - [[http://​en.wikipedia.org/​wiki/​Software_design_pattern | Clasificarea design pattern-urilor]]
   - [[:​poo-ca-cd:​laboratoare:​tutorial-doubledispatch| Tutorial double-dispatch]]   - [[:​poo-ca-cd:​laboratoare:​tutorial-doubledispatch| Tutorial double-dispatch]]
  
  
poo-ca-cd/laboratoare/visitor.1597216240.txt.gz · Last modified: 2020/08/12 10:10 by florin.mihalache
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0