User Tools

Site Tools


Problem constructing authldap
laboratoare:design-patterns
Differences

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

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
laboratoare:design-patterns [2019/01/06 17:42]
Laurentiu Stamate [Resurse]
laboratoare:design-patterns [2020/01/04 21:54] (current)
Adriana Draghici [Resurse] added solution
Line 1: Line 1:
-= Design patterns - Singleton, ​Factory, Observer =+= Design patterns - Factory, Strategy, Observer =
  
  
Line 6: Line 6:
 == Obiective == == Obiective ==
  
-Scopul acestui laborator este familiarizarea cu folosirea unor pattern-uri des întâlnite în design-ul atât al aplicațiilor,​ cât și al API-urilor - //Singleton//, //Factory// și //​Observer//​.+Scopul acestui laborator este familiarizarea cu folosirea unor pattern-uri des întâlnite în design-ul atât al aplicațiilor,​ cât și al API-urilor - //Factory//, //Strategy// și //​Observer//​.
  
  
Line 35: Line 35:
  
  
-În laboratorul ​[[.:​visitor|Visitor ​Pattern]] au fost introduse design ​pattern-urile șaplicabilitatea Visitor-uluiAcesta ​este un pattern comportamental,​ și după cum ați observat oferă avantaje în anumite situații, în timp ce pentru altele nu este potrivit. Pattern-urile comportamentale modelează interacțiunile dintre clasele și componentele unei aplicații, fiind folosite în cazurile în care vrem sa facem un design mai clar și ușor de adaptat și extins. ​/*În afară de acest tip de pattern-uri,​ mai se folosesc și cele //​structural//​ și //​creational//,​ prezentate în clasificarea următoare:​ +În laboratoarele precedente au fost descrise patternurile [[.:​static-final#​singleton-pattern|Singleton]] și [[.:​visitor|Visitor]]. //​Singleton//​ este un pattern ​creațional,​ simplu, a cărui folosire este controversată (vedețîn [[.:​static-final#​singleton-pattern|laborator]] explicația cu anti-pattern)//​Visitor// ​este un pattern comportamental,​ și după cum ați observat oferă avantaje în anumite situații, în timp ce pentru altele nu este potrivit. Pattern-urile comportamentale modelează interacțiunile dintre clasele și componentele unei aplicații, fiind folosite în cazurile în care vrem sa facem un design mai clar și ușor de adaptat și extins.
- +
-* //​Creational Patterns// - mecanisme de creare a obiectelor +
-   * **Singleton**,​ **Factory** etc +
-* //​Structural Patterns// - definesc relații între entități +
-   * Decorator, Adapter, Facade, Composite, Proxy etc. +
-* //​Behavioural Patterns// - definesc comunicarea între entități +
-  * Visitor,** Observer**, **Command**,​ Mediator, Strategy etc. +
-*/ +
-== Singleton Pattern == +
- +
-Pattern-ul Singleton este utilizat pentru a restricționa numărul de instanțieri ale unei clase la un singur obiect, deci reprezintă o metodă de a folosi o singură instanță a unui obiect în aplicație. +
- +
-=== Utilizari === +
- +
-Pattern-ul Singleton este util în următoarele cazuri: +
-* ca un subansamblu al altor pattern-uri:​ +
- ** impreună cu pattern-urile Abstract Factory, Builder, Prototype etc. De exemplu, în aplicație dorim un singur obiect factory pentru a crea obiecte de un anumit tip. +
-* în locul variabilelor globale. Singleton este preferat variabilelor globale deoarece, printre altele, nu poluează namespace-ul global cu variabile care nu sunt necesare. +
- +
-Singleton este utilizat des în situații în care avem obiecte care trebuie accesate din mai multe locuri ale aplicației:​ +
-* obiecte de tip logger +
-* obiecte care reprezintă resurse partajate (conexiuni, sockets etc.) +
-* obiecte ce conțin configurații pentru aplicație +
-* pentru obiecte de tip //​Factory//​. +
- +
-Exemple din API-ul Java: +
-* [[http://​docs.oracle.com/​javase/​8/​docs/​api/​java/​lang/​Runtime.html | java.lang.Runtime]] +
-* [[http://​docs.oracle.com/​javase/​8/​docs/​api/​java/​awt/​Toolkit.html | java.awt.Toolkit]] +
- +
-Din punct de vedere al design-ului și testarii unei aplicații de multe ori se evită folosirea acestui pattern, în test-driven development fiind considerat un **//​anti-pattern//​**. A avea un obiect Singleton a carei referință o folosim peste tot prin aplicație introduce multe dependențe între clase și îngreunează testarea individuală a acestora.  +
- +
-In general, codul care folosește stări globale este mai dificil de testat pentru că implică o cuplare mai strânsă a claselor, și împiedică izolarea unei componente și testarea ei individuală. Dacă o clasă testată folosește un obiect singleton, atunci trebuie testat și singleton-ul. Soluția este simularea //mock-up// a singleton-ului în teste. Încă o problemă a acestei cuplări mai strânse apare atunci când două teste depind unul de celălalt prin modificarea singleton-ului,​ deci trebuie impusă o anumită ordine a rulării testelor. +
- +
-<note tip>​Încercați să nu folosiți în exces metode statice (să le utilizați mai mult pt funcții "​utility"​) și componente Singleton.</​note>​ +
- +
- +
-=== Implementare === +
- +
-Aplicarea pattern-ului Singleton constă în implementarea unei metode ce permite crearea unei noi instanțe a clasei dacă aceasta nu există, și întoarcerea unei referințe către aceasta dacă există deja. În Java, pentru a asigura o singură instanțiere a clasei, constructorul trebuie să fie //​private//,​ iar instanța să fie oferită printr-o metodă statică, publică. +
- +
-În cazul unei implementări Singleton, clasa respectivă va fi instanțiată **lazy** (//lazy instantiation//​),​ utilizând memoria doar în momentul în care acest lucru este necesar deoarece instanța se creează atunci când se apelează ''​getInstance()'',​ acest lucru putând fi un avantaj în unele cazuri, față de clasele non-singleton,​ pentru care se face //eager instantiation//,​ deci se alocă memorie încă de la început, chiar dacă instanța nu va fi folosită (mai multe detalii și exemplu în [[http://​www.javaworld.com/​article/​2077568/​learn-java/​java-tip-67--lazy-instantiation.html |acest articol]]) +
- +
-<​imgcaption image_singleton|Diagrama de clase pentru Singleton>​{{ .:​design-patterns:​singletonclassdiagram.png |}}</​imgcaption>​ +
- +
- +
-Respectând cerințele pentru un singleton enunțate mai sus, în Java, putem implementa o componentă de acest tip în mai multe feluri, inclusiv folosind ''​enum''​-uri în loc de clase. Atunci când îl implementâm trebuie avut în vedere contextul în care îl folosim, astfel încât să alegem o soluție care să funcționeze corect în toate situațiile ce pot apărea în aplicație (unele implementări au probleme atunci când sunt accesate din mai multe thread-uri sau când trebuie serializate). +
- +
-<code java> +
- ​public class Singleton { +
- +
-   ​private static Singleton instance = null; +
-    +
-   ​private Singleton() {} +
-    +
-   ​public static Singleton getInstance() { +
-      if(instance == null) { +
-          instance = new Singleton();​ +
-      } +
-      return instance; ​      +
-   } +
-   ... +
- } +
-</​code>​ +
- +
-  * Instanța ''​instance''​ este //​private//​ +
-  * Constructorul este privat ca sa nu poata fi apelat decat din clasa respectivă +
-  * Instanța este inițial nulă +
-  * Instanța este creată la prima rulare a //​getInstance()//​ +
- +
-//De ce Singleton și nu clase cu membri statici?//​ +
- +
-O clasă de tip Singleton poate fi extinsă, iar metodele ei suprascrise,​ însă într-o clasă cu metode statice acestea nu pot fi suprascrise (//​overriden//​) (o discuție pe aceasta temă puteți gasi [[http://​geekexplains.blogspot.ro/​2008/​06/​can-you-override-static-methods-in-java.html | aici]], și o comparatie între static și dynamic binding [[http://​geekexplains.blogspot.ro/​2008/​06/​dynamic-binding-vs-static-binding-in.html | aici]]). +
  
  
Line 254: Line 181:
  
 === Implementare === === Implementare ===
- 
-Un exemplu de implementare este [[.:​clase-interne#​exercitii|exercițiul 2]] de la laboratorul ​ 5 (Clase interne). Observați diagrama de clase asociată acestuia. ​ 
  
 Tookit-urile GUI, cum este și [[http://​en.wikipedia.org/​wiki/​Swing_%28Java%29|Swing]] folosesc acest design pattern, de exemplu apăsarea unui buton generează un eveniment ce poate fi transmis mai multor //​listeners//​ înregistrați acestuia ([[http://​www.programcreek.com/​2009/​01/​the-steps-involved-in-building-a-swing-gui-application/​|exemplu]]). Tookit-urile GUI, cum este și [[http://​en.wikipedia.org/​wiki/​Swing_%28Java%29|Swing]] folosesc acest design pattern, de exemplu apăsarea unui buton generează un eveniment ce poate fi transmis mai multor //​listeners//​ înregistrați acestuia ([[http://​www.programcreek.com/​2009/​01/​the-steps-involved-in-building-a-swing-gui-application/​|exemplu]]).
Line 262: Line 187:
  
 Pentru cod complex, concurent, cu evenimente asincrone, recomandăm RxJava, care folosește Observer pattern: [[https://​github.com/​ReactiveX/​RxJava/​wiki|github]],​ [[https://​dzone.com/​articles/​rxjava-part-1-a-quick-introduction|exemplu]]. Pentru cod complex, concurent, cu evenimente asincrone, recomandăm RxJava, care folosește Observer pattern: [[https://​github.com/​ReactiveX/​RxJava/​wiki|github]],​ [[https://​dzone.com/​articles/​rxjava-part-1-a-quick-introduction|exemplu]].
 +
 +== Strategy Pattern ==
 +
 +Design pattern-ul //​Strategy//​ încapsulează algoritmii în clase ce oferă o anumită interfață de folosire, și pot fi selecționați la runtime. Ca și Command, acest pattern este //​behavioral//​ pentru ca permite decuplarea unor clase ce oferă un anumit comportament și folosirea lor independentă în funcție de situația de la runtime.
 +
 +Acest pattern este recomandat în cazul în care avem nevoie de un tip de algoritm (strategie) cu mai multe implementări posibile si dorim să alegem dinamic care algoritm îl folosim, fără a face sistemul prea strâns cuplat.
 +
 +**Exemple de utilizare**:​
 +* sisteme de tip Layout Managers din API-urile pentru UI
 +* selectarea in mod dinamic la runtime a unor algoritmi de sortare, compresie, criptare etc.
 +
 +**Structură**:​
 +* trebuie să definiți o **interfață comună** pentru strategiile pe care le implementați (fie ca o <<​interface>>​ sau ca o clasa abstractă)
 +* implementați strategiile respectând interfața comună
 +* clasa care are nevoie să folosească strategiile **va ști doar despre interfața lor**, nu va fi legată de implementările concrete
 +
 +Denumirile uzuale în exemplele acestui pattern sunt: //​Strategy//​ (pt interfață sau clasa abstractă),​ //​ConcreteStrategy//​ pentru implementare,​ //​Context//,​ clasa care folosește/​execută strategiile.
 +
 +//​__Recomandare__//:​ Urmariti link-ul de la referinte catre postul de pe Stack Overflow care descrie necesitatea pattern-ului Strategy. Pe langa motivul evident de incapsulare a prelucrarilor/​algoritmilor (care reprezinta strategiile efective), se prefera o anumita abordare: la runtime se verifica mai multe conditii si se decide asupra strategiei. Concret, folosind mecanismul de polimorfism dinamic, se foloseste o anumita instanta a tipului de strategie (//ex. Strategy str = new CustomStrategy//​),​ care se paseaza in toate locurile unde este nevoie de Strategy. Practic, in acest fel, utilizatorii unei anumite strategii vor deveni agnostici in raport cu strategia utilizata, ea fiind instantiata intr-un loc anterior si putand fi gata utilizata. Ganditi-va la browserele care trebuie sa detecteze daca device-ul este PC, smartphone, tableta sau altceva si in functie de acest lucru sa randeze in mod diferit. Fiecare randare poate fi implementata ca o strategie, iar instantierea strategiei se va face intr-un punct, fiind mai apoi pasata in toate locurile unde ar trebui sa se tina cont de aceasta strategie.
 +
 +== Summary ==
 +
 +* Principii de design adresate de aceste patternuri:
 +  * [[https://​stackoverflow.com/​questions/​62539/​what-is-the-dependency-inversion-principle-and-why-is-it-important|Dependency Injection Principle]] - componentele trebuie să depindă de tipuri abstracte, nu de implementări
 +    * Factory respectă acest principiu, componentele depinzând de interfața pentru un tip, nu de un subtip anume
 +  * Separarea codului care se schimbă de cel care rămâne la fel - în cazul Strategy folosim o interfață pentru strategii, depindem de aceea, si putem schimba implementările fără a modifica codul care le folosește ([[https://​www.freecodecamp.org/​news/​the-strategy-pattern-explained-using-java-bc30542204e0/​|exemplu]])
 +  * [[http://​thephantomprogrammer.blogspot.com/​2015/​07/​strive-for-loosely-coupled-design.html|Loosly coupled design]] - în cazul Observer componentele sunt slab legate între ele
 +
 +
  
 == Exerciții == == Exerciții ==
  
-Acest laborator și [[laboratoare:​design-patterns2|următorul]] au ca temă comună a exercițiilor realizarea unui joc controlat ​din consolă. Jocul constă dintr-o lume (aka hartă) în care se plimbă eroi de trei tipuri, colectează comori ​și se bat cu monștriÎn acestă săptămână trebuie să implementați ​o parte din funcționalitățile jocului folosind patternurile ​//Singleton////Factory// și //Observer//, urmând ca la laboratorul ​următor să terminați implementarea folosind pattern-urile studiate atunci.+În cadrul acestui ​laborator ​veți implementa o aplicație //mock// care primește date și le procesează
 + 
 +Arhitectura ei va include patternurile Observer, Strategy și Factory și este descrisă în README-ul ​din 
 +arhiva dată cu scheletul de cod. 
 + 
 + 
 +** Task 1 Observer pattern ​(3p) ** 
 + 
 +Scheletul de cod vă oferă clasele //MainApp// (entry-point pentru testare), //Utils// și //​DataRepository//​. 
 + 
 +''​DataRepository''​ este obiectul observabil, ​care va primi date noi de la MainApp. Când acesta primește date noi va notifica observatorii săi: ''​ConsoleLogger'',​ ''​ServerCommunicationController'' ​și ''​DataAggregator''​. 
 +* Pentru a avea deja mecanismul de notificare vom folosi interfața [[https://​docs.oracle.com/​en/​java/​javase/​11/​docs/​api/​java.base/​java/​util/​Observer.html|Observer]] ​și clasa [[https://​docs.oracle.com/​en/​java/​javase/​11/​docs/​api/​java.base/​java/​util/​Observable.html|Observable]] din java.util. Dacă doriți și aveți timp puteți ​să vă implementați ​propriile interfețe Oberver-Observable. 
 +   * Observer-Observable ​din java.util sunt deprecated din java 9 pentru că sunt prea simple, însă asta le face potrivite pentru acest laborator. Într-o aplicație reală puteți folosi alte api-uri care sunt mult mai complexe și oferă foarte multe tipuri de obiecte și mecanisme (termenul folosit este //reactive programming//). 
 +* Citiți în [[https://github.com/oop-pub/laboratoare/​blob/​master/​design-patterns/​skel/​src/​README|README]] rolul fiecărui observator. 
 +* //Hint:// vedețmetodele din [[https://docs.oracle.com/en/java/​javase/​11/​docs/​api/​java.base/​java/​util/​Observable.html|Observable]] pentru notificarea observatorilorschimbarea stării obiectului observat și adăugarea de observatori. 
 + 
 + 
 +** Task 2 - Strategy pattern (3p)** 
 + 
 +Scheletul vă oferă interfața ''​StepCountStrategy''​ ce va fi implementată de către "​algoritmii"​ de prelucrare a datelor: ''​BasicStepCountStrategy''​ și ''​FilteredStepCountStrategy''​. Prima adună toate valorile primite, iar a doua le adună doar pe cele ce îndeplinesc niște condiții (să fie număr pozitiv și să nu fie o valoare prea mare (mai mult de 1000 de pași) venită prea curând de la ultimul update primit (în mai puțin de 1 minut). 
 +   * strategiile vor folosi datele stocate în DataRepository 
 +   * pentru strategia Filtered puteți folosi ​următoarele constante: ''​ private static final int MAX_DIFF_STEPS_CONSECUTIVE_RECORDS = 1000;''​ și ''​private static final long TIME_FOR_MAX_DIFF = TimeUnit.SECONDS.toMillis(1);''​ 
 + 
 + 
 +** Task 3 - Factory pattern (2p)** 
 + 
 +Creați clasa ''​StepCountStrategyFactory''​ care construiește instanțe de subclase ale StepCountStrategy. 
 + 
 +Clasa factory va conține o metodă care să întoarcă o strategie. De exemplu: 
 +''​public StepCountStrategy createStrategy(String strategyType,​ DataRepository dataRepository)''​. StrategyType poate fi un string care să indice tipul strategiei, vedeți în ''​Utils''​ stringurile definite deja. 
 + 
 +** Task 4 - Putting all together (2p)**
  
-Detalii joc: +Realizați TODO-urile din codul de test din MainApp ​și rulați. Puteți să vă adăugați ​propriile exemple de date pentru a verfica corectitudinea programului.
-* //Harta// +
-   * reprezentată printr-o matrice. Fiecare element din matrice reprezintă o zonă care poate fi liberă, poate conține obstacole sau poate conține o comoară (în laboratorul următor poate conține șmonștrii). +
-   * este menținută în clasa ''​World''​. +
-* //Eroii//  +
-   * sunt reprezentați prin clase de tip ''​Hero''​ și sunt de trei tipuri: //Mage//, //​Warrior//,​ //​Priest//​. +
-   * puteți adăuga oricâți eroi doriți pe hartă (cât vă permite memoria :)) +
-   * într-o zonă pot fi mai mulți eroi +
-   * acțiunile pe care le pot face: +
-     * ''​move''​ - se mută într-o zonă învecinată +
-     * ''​attack''​ (de implementat în laboratorul următor) +
-     * ''​collect''​ - eroul ia comoara găsită în zona în care se află +
-* Entry-point-ul în joc îl consitituie clasa ''​Main''​. +
-* **(4p)** Folosiți design pattern-ul //Factory// pentru crearea obiectelor. +
-  - Creati clase care mostenesc ''​Hero''​ pentru fiecare tip de erou. +
-       * suprascrieti metoda ''​toString''​ din ''​Object''​ pentru fiecare erou +
-       * metoda ''​attack''​ - deocamdată nu va omorî pe nimeni - puteți afișa ceva la consolă +
-  - Uitați-vă la clasele ''​TreasureFactory''​ și ''​HeroFactory''​. Trebuie să implementăm două metode: ''​createTreasure''​ în ''​TreasureFactory''​ și o metodă de creare de eroi în ''​HeroFactory'',​ fie ea ''​createHero''​. +
-    - Puteți pune orice date doriți în comori, respectiv eroi. +
-    - La ''​HeroFactory.createHero'',​ pasați ca parametru un ''​Hero.Type''​ și un ''​String''​ cu numele eroului și întoarceți un subtip de ''​Hero''​ potrivit pentru tipul de erou. +
-  - După ce ați creat factory-urile, folosiți-le:​ +
-    - Completați metoda ''​populateTreasures'' ​din ''​World''​. Folosiți-vă ​de membrii ''​map''​ și ''​treasures'' ​din ''​World''​. Trebuie să marcați pe hartă că aveți o comoară ​și să adăugați obiectul-comoară în lista de comori. +
-    - Uitați-vă apoi la cazul ''​add''​ din metoda ''​main''​. Trebuie ​să adăugați eroi acolo. Folosiți ''​HeroFactory.createHero''​. +
-* **(2p)** Folosiți design pattern-ul //​Singleton//​ pentru elementele din joc care trebuie să aibă doar o instanță. +
-  * Ce clase vor avea doar o singură instanță?​ +
-* **(4p)** Folosiți design pattern-ul //​Observer//​ pentru a monitoriza ceea ce se întâmplă în joc. Scheletul de cod vă sugerează două tipuri de observatori,​ pentru bonus puteți ​adăuga ​și alții. +
-  - veți folosi interfața [[https://​docs.oracle.com/​javase/​8/​docs/​api/​java/​util/​Observer.html|Observer]] și clasa [[https://​docs.oracle.com/​javase/​8/​docs/​api/​java/​util/​Observable.html|Observable]] din java.util. +
-    - Înainte să vă apucați să scrieți, citiți comentariile din cod (e.g. TreasureDiscoverer) să vă faceți o idee despre ce vrem să facem în clasele observatoare. +
-    - Asigurati-va ca implementati corect functionalitatea din **Observable**. Mare atentie la faptul ca **metoda notifyObservers nu va face nimic daca nu este apelata mai intai metoda setChanged**. +
-  - Care sunt elementele observatoare și care sunt observabile?​ Uitați-vă și în comentariile din cod. +
-  - Implementați interfețele ''​Observer''​ și ''​Observable''​ în clasele potrivite. +
-  - Înregistrați observatori la ''​World''​. Cazul ''​start''​ din metoda ''​main''​. +
-  - Notificați observatorii lui ''​World''​ când eroii execută o acțiune. Aveți două ''​TODO''​-uri în clasa ''​Hero''​. +
-* Începeți rezolvarea prin implementarea claselor ​pentru ​eroi și implementarea design pattern-ului factory pentru crearea lor. Pentru ​putea vizualiza harta trebuie să implementați partea de observare a stării jocului''​World''​ trebuie să fie observabilă și să notifice pe observatorii săi atunci când a început jocul și când se schimbă ceva (e.g. s-a mutat un erou). +
-    * Urmăriți todo-urile din scheletul de cod (pentru a le vizualiza mai ușor pe toate puteți să folosiți view-ul pt ele din IDE, de exemplu în eclipse aveți //Window -> Show View -> Tasks//)+
  
-<​imgcaption exercitii|>​ 
-{{:​laboratoare:​design-patterns:​exercitiu.png|Componentele jocului}}</​imgcaption>​ 
  
  
 + 
 == Resurse == == Resurse ==
  
-* {{.:​design-patterns:​design-patterns1-skel.zip|Schelet}} +* {{ :​laboratoare:​design-patterns:​design-patterns-part1-skel.zip |Schelet}} 
-* {{:​laboratoare:​design-patterns:​lab10-sol.zip|Soluție}} +* {{ :​laboratoare:​design-patterns:​lab-design-patterns1-sol.zip | Soluție}} 
-<​html><​a class="​media mediafile mf_pdf"​ href="/​poo/​laboratoare/design-patterns?​do=export_pdf">​PDF laborator</​a></​html>​+[[laboratoare:old-exercises|Exerciții din alți ani]]
  
 == Referințe == == Referințe ==
Line 319: Line 269:
 *  [[http://​sourcemaking.com/​design_patterns/​observer/​java/​1 | Exemple simple pattern Observer]] ​ *  [[http://​sourcemaking.com/​design_patterns/​observer/​java/​1 | Exemple simple pattern Observer]] ​
 * [[http://​sourcemaking.com/​design_patterns/​observer | Explicații pattern Observer]]. * [[http://​sourcemaking.com/​design_patterns/​observer | Explicații pattern Observer]].
 +* [[https://​stackoverflow.com/​questions/​1710809/​when-and-why-should-the-strategy-pattern-be-used | De ce avem nevoie de Strategy Pattern? ]]
  
 /* exercitii 2012 /* exercitii 2012
laboratoare/design-patterns.1546789338.txt.gz · Last modified: 2019/01/06 17:42 by Laurentiu Stamate