This shows you the differences between two versions of the page.
| — |
poo:laboratoare:11-backup [2025/12/14 17:19] (current) george.tudor1906 created |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | |||
| + | ===== Laboratorul – Design Patterns 1 ===== | ||
| + | |||
| + | <hidden>{{:poo:laboratoare:arhiva_dp1.zip|Arhiva laborator}}</hidden> | ||
| + | |||
| + | <note important> | ||
| + | |||
| + | În cadrul acestui laborator se va lucra pe baza scheletului pus la dispoziție. | ||
| + | |||
| + | Fiecare TODO este numerotat conform problemei ce trebuie rezolvată. | ||
| + | |||
| + | În urma realizării cerințelor, se va obține o platformă de streaming muzical. | ||
| + | </note> | ||
| + | |||
| + | === Problema 1 - Singleton === | ||
| + | |||
| + | Se cere implementarea Design Pattern-ului **Singleton** în cadrul clasei **//Spotify//**. | ||
| + | Clasa **//Spotify//** reprezintă componenta principală a aplicației, gestionând datele | ||
| + | platformei (utilizatori și melodii). | ||
| + | |||
| + | Pattern-ul **Singleton** asigură existența unei singure instanțe a clasei pe | ||
| + | parcursul execuției programului. | ||
| + | |||
| + | Cerințe de implementare: | ||
| + | |||
| + | * Declarați constructorul clasei ca **PRIVATE**; | ||
| + | * Adăugați un atribut **STATIC PRIVATE** de tip **Spotify** pentru instanța unică; | ||
| + | * Implementați o metodă **PUBLICĂ STATICĂ** //getInstance()// care: | ||
| + | * creează instanța dacă nu există; | ||
| + | * returnează instanța existentă; | ||
| + | * Instanțiați listele (**users**, **songs**, **observers**) în constructorul privat. | ||
| + | |||
| + | <note tip> | ||
| + | Singleton-ul va fi punctul de acces global la datele platformei. | ||
| + | </note> | ||
| + | |||
| + | === Problema 2 - Factory === | ||
| + | |||
| + | Pornind de la clasa abstractă **//User//**, se va implementa Design Pattern-ul **Factory** | ||
| + | prin intermediul claselor **//Regular//**, **//Premium//** și **//UltraPremium//** care moștenesc **//User//**. | ||
| + | |||
| + | Factory pattern centralizează logica de creare a obiectelor, ascunzând detaliile | ||
| + | de implementare și permițând extinderea ușoară cu noi tipuri. | ||
| + | |||
| + | Valorile pentru câmpul **subscription**: | ||
| + | |||
| + | * **Regular**: 9.99 | ||
| + | * **Premium**: 14.99 | ||
| + | * **UltraPremium**: 19.99 | ||
| + | |||
| + | Cerințe de implementare: | ||
| + | |||
| + | * Clasele **Regular**, **Premium**, **UltraPremium** extind **User**; | ||
| + | * Fiecare clasă își setează **subscription** în constructorul propriu; | ||
| + | * **UserFactory** conține metoda statică //createUser()// care primește un **UserType** și returnează instanța corespunzătoare; | ||
| + | * În **JSONReader**, folosiți factory-ul pentru a crea utilizatorii. | ||
| + | |||
| + | |||
| + | === Problema 3 - Builder === | ||
| + | |||
| + | Se cere implementarea Design Pattern-ului **Builder** în cadrul clasei **//Song//**. | ||
| + | |||
| + | Un cântec conține: **title**, **artists**, **duration**, **views**, **releaseDate**, **ratings**. | ||
| + | |||
| + | Builder pattern permite construirea obiectelor complexe pas cu pas, oferind | ||
| + | o alternativă la constructorii cu mulți parametri. | ||
| + | |||
| + | Cerințe de implementare: | ||
| + | |||
| + | * Definiți o clasă internă statică **Builder** în **Song**; | ||
| + | * Builder-ul conține câmpuri identice cu cele din **Song**; | ||
| + | * Fiecare metodă setter din Builder returnează //this// (pentru *method chaining*); | ||
| + | * Metoda //build()// creează și returnează obiectul **Song**; | ||
| + | * Constructorul **Song** este **PRIVAT** și primește un **Builder** ca parametru; | ||
| + | * În **JSONReader**, folosiți Builder-ul pentru a crea melodiile. | ||
| + | |||
| + | Exemplu de utilizare Builder (în **JSONReader**): | ||
| + | |||
| + | <code java> | ||
| + | Song song = new Song.Builder() | ||
| + | .title(title) | ||
| + | .artists(artists) | ||
| + | .duration(duration) | ||
| + | .views(views) | ||
| + | .releaseDate(releaseDate) | ||
| + | .ratings(ratings) | ||
| + | .build(); | ||
| + | </code> | ||
| + | |||
| + | Exemplu de metodă setter în Builder: | ||
| + | |||
| + | <code java> | ||
| + | public Builder title(String title) { | ||
| + | this.title = title; | ||
| + | return this; | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | === Problema 4 - Strategy === | ||
| + | |||
| + | Fiecare utilizator poate alege modul în care sunt afișate cântecele, în funcție | ||
| + | de un criteriu de sortare preferat. | ||
| + | |||
| + | Strategy pattern permite schimbarea algoritmului de sortare la runtime, fără | ||
| + | a modifica codul clientului. | ||
| + | |||
| + | Interfața **//SortingStrategy//** definește metoda //sortedSongs(List<Song>)//. | ||
| + | |||
| + | Strategii de implementat: | ||
| + | |||
| + | * **AverageScoreStrategy** - sortare **DESCRESCĂTOARE** după scorul mediu; | ||
| + | * **TotalViewsStrategy** - sortare **DESCRESCĂTOARE** după numărul de views; | ||
| + | * **ReleaseYearStrategy** - sortare **CRESCĂTOARE** după data lansării (*oldest first*). | ||
| + | |||
| + | Cerințe de implementare: | ||
| + | |||
| + | * Implementați cele trei strategii; | ||
| + | * //User.getSortedSongs()// delegă sortarea către strategia setată; | ||
| + | * În **JSONReader**, citiți câmpul //"sortingStrategy"// din JSON și setați strategia corespunzătoare (valori: //"averageScore"//, //"totalViews"//, //"releaseYear"//). | ||
| + | |||
| + | Hint pentru sortare cu Comparator: | ||
| + | |||
| + | <code java> | ||
| + | // Sortare DESCRESCĂTOARE după un câmp | ||
| + | songs.sort((s1, s2) -> s2.getField().compareTo(s1.getField())); | ||
| + | |||
| + | // Sortare CRESCĂTOARE după un câmp | ||
| + | songs.sort((s1, s2) -> s1.getField().compareTo(s2.getField())); | ||
| + | </code> | ||
| + | |||
| + | |||
| + | === Problema 5 - Observer === | ||
| + | |||
| + | Utilizatorii platformei pot primi notificări când un cântec nou este adăugat. | ||
| + | |||
| + | Observer pattern permite notificarea automată a obiectelor interesate (**observers**) | ||
| + | când starea unui obiect (**subject**) se schimbă. | ||
| + | |||
| + | Interfețele din schelet: | ||
| + | |||
| + | * **Subject** - definește //addObserver//, //removeObserver//, //notifyObservers// | ||
| + | * **Observer** - definește //update(String message)// | ||
| + | |||
| + | Cerințe de implementare: | ||
| + | |||
| + | * **Spotify** implementează **Subject** și menține lista de observatori; | ||
| + | * **User** implementează **Observer** și afișează mesajul primit în //update()//; | ||
| + | * În //addSong()//, după adăugarea melodiei, notificați observatorii cu mesajul: //"New song added: <titlu>"//; | ||
| + | * În //readData()//, după încărcarea utilizatorilor, adăugați-i ca observatori. | ||
| + | |||
| + | === Problema 6 - Visitor === | ||
| + | |||
| + | Pentru calculul venitului total lunar, se va folosi Design Pattern-ul **Visitor**. | ||
| + | |||
| + | Visitor pattern permite adăugarea de operații noi asupra obiectelor fără a | ||
| + | modifica clasele acestora. „Vizitorul” parcurge elementele și aplică operații | ||
| + | specifice fiecărui tip. | ||
| + | |||
| + | Interfețele din schelet: | ||
| + | |||
| + | * **Element** - definește //accept(Visitor)// | ||
| + | * **Visitor** - definește //visit()// pentru fiecare tip de utilizator | ||
| + | |||
| + | Formula de calcul a venitului per utilizator: | ||
| + | |||
| + | * //revenue = subscription * (1 - discount/100.0) * VAT// | ||
| + | * unde //VAT = 1.19// | ||
| + | |||
| + | Cerințe de implementare: | ||
| + | |||
| + | * **User** implementează **Element**; | ||
| + | * **Regular**, **Premium**, **UltraPremium** implementează //accept()// apelând //visitor.visit(this)//; | ||
| + | * **RevenueVisitor** implementează **Visitor** cu metode //visit()// pentru fiecare tip; | ||
| + | * **RevenueVisitor** acumulează //totalRevenue// și oferă //getTotalRevenue()//; | ||
| + | * //Spotify.calculateRevenue()// creează un visitor, parcurge userii și returnează venitul total. | ||
| + | |||
| + | Exemplu de utilizare Visitor (în //Spotify.calculateRevenue//): | ||
| + | |||
| + | <code java> | ||
| + | RevenueVisitor visitor = new RevenueVisitor(); | ||
| + | for (User user : users) { | ||
| + | user.accept(visitor); | ||
| + | } | ||
| + | return visitor.getTotalRevenue(); | ||
| + | </code> | ||
| + | |||
| + | |||
| + | === BONUS === | ||
| + | |||
| + | Implementați pattern-ul **Observer** și pentru rating-uri. | ||
| + | |||
| + | Când un utilizator adaugă un rating unui cântec, toți utilizatorii care au dat | ||
| + | anterior un rating aceluiași cântec vor fi notificați. | ||
| + | |||
| + | Cerințe de implementare: | ||
| + | |||
| + | * **Song** menține o listă de observatori (utilizatorii care au dat rating); | ||
| + | * La //addRating()//, adăugați rating-ul, apoi notificați observatorii existenți cu mesajul: //"New rating for: <titlu>"//; | ||
| + | * Utilizatorul care adaugă rating-ul devine și el observator pentru viitor. | ||