This is an old revision of the document!


Laboratorul – Design Patterns 1

Arhiva laborator

Î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.

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.

Singleton-ul va fi punctul de acces global la datele platformei.

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):

Song song = new Song.Builder()
        .title(title)
        .artists(artists)
        .duration(duration)
        .views(views)
        .releaseDate(releaseDate)
        .ratings(ratings)
        .build();

Exemplu de metodă setter în Builder:

public Builder title(String title) {
    this.title = title;
    return this;
}

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:

// 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()));

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):

RevenueVisitor visitor = new RevenueVisitor();
for (User user : users) {
    user.accept(visitor);
}
return visitor.getTotalRevenue();

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.
poo/laboratoare/11.1765708096.txt.gz · Last modified: 2025/12/14 12:28 by george.tudor1906
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