This shows you the differences between two versions of the page.
poo-ca-cd:laboratoare:design-patterns [2022/12/12 01:20] tudor.diaconu [Exerciții] |
poo-ca-cd:laboratoare:design-patterns [2024/11/20 14:42] (current) bogdan.rusu1707 [Exerciții] |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ===== Laboratorul 9: Design patterns - Factory, Strategy, Observer, Command, Builder ===== | + | ===== Laboratorul 7: Design patterns - Factory, Strategy, Observer, Command, Builder ===== |
**Video introductiv:** [[https://www.youtube.com/watch?v=khRqWZrz1YU | link]] + [[https://www.youtube.com/watch?v=F2HMN4mvVYY | link]] | **Video introductiv:** [[https://www.youtube.com/watch?v=khRqWZrz1YU | link]] + [[https://www.youtube.com/watch?v=F2HMN4mvVYY | link]] | ||
Line 35: | Line 35: | ||
- | În laboratoarele precedente au fost descrise patternurile [[:poo-ca-cd:laboratoare:static-final#singleton_pattern|Singleton]] și [[:poo-ca-cd:laboratoare:visitor|Visitor]]. //Singleton// este un pattern creațional, simplu, a cărui folosire este controversată (vedeți în [[:poo-ca-cd:laboratoare: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. | + | În laboratoarele precedente au fost descrise patternurile [[https://ocw.cs.pub.ro/courses/poo-ca-cd/laboratoare/constructori-referinte|Singleton]] și [[https://ocw.cs.pub.ro/courses/poo-ca-cd/laboratoare/visitor|Visitor]]. //Singleton// este un pattern creațional, simplu, a cărui folosire este controversată (vedeți în [[:poo-ca-cd:laboratoare: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. |
Line 187: | Line 187: | ||
* implementează interfața Observator | * implementează interfața Observator | ||
- | Aceasta schemă se poate extinde, în funcție de aplicație, observatorii pot ține referințe catre subiect sau putem adauga clase speciale pentru reprezentarea evenimentelor, notificarilor. Un alt exemplu îl puteți găsi [[http://www.research.ibm.com/designpatterns/example.htm |aici]]. | + | Aceasta schemă se poate extinde, în funcție de aplicație, observatorii pot ține referințe catre subiect sau putem adauga clase speciale pentru reprezentarea evenimentelor, notificarilor. |
=== Implementare === | === Implementare === | ||
- | 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. |
API-ul Java oferă clasele [[http://docs.oracle.com/javase/8/docs/api/java/util/Observer.html |Observer]] și [[http://docs.oracle.com/javase/8/docs/api/java/util/Observable.html |Observable]] care pot fi subclasate pentru a implementa propriile tipuri de obiecte ce trebuie monitorizate și observatorii acestora. | API-ul Java oferă clasele [[http://docs.oracle.com/javase/8/docs/api/java/util/Observer.html |Observer]] și [[http://docs.oracle.com/javase/8/docs/api/java/util/Observable.html |Observable]] care pot fi subclasate pentru a implementa propriile tipuri de obiecte ce trebuie monitorizate și observatorii acestora. | ||
Line 617: | Line 617: | ||
==== Exerciții ==== | ==== Exerciții ==== | ||
- | Laboratorul trebuie rezolvat pe platforma LambdaChecker, fiind găsit [[https://beta.lambdachecker.io/contest/20 | aici]]. | ||
- | |||
<note important> | <note important> | ||
- | În cadrul acestui laborator, exercițiile valorează în total 20p. Pentru primirea punctajului maxim pe acest laborator, trebuie să acumulați 10p din rezolvarea exercițiilor, orice depășește 10p fiind contorizat ca și bonus. | + | Pentru a încărca soluția, va trebui să accesați link-ul https://code.devmind.ro/login, să intrați pe tab-ul Contests, unde veți găsi laboratorul grupei voastre. |
</note> | </note> | ||
<note important> | <note important> | ||
- | Primele 4 task-uri vor fi toate rezolvate în cadrul aceleiași [[https://beta.lambdachecker.io/problem/43/20| probleme]] de pe LambdaChecker. | + | În cadrul acestui laborator, exercițiile valorează în total 18p. Pentru primirea punctajului maxim pe acest laborator, trebuie să acumulați 10p din rezolvarea exercițiilor, orice depășește 10p fiind contorizat ca și bonus. |
</note> | </note> | ||
- | ** [[https://beta.lambdachecker.io/problem/43/20| Task 1]] - Observer pattern (3p) ** | ||
- | Scheletul de cod vă oferă clasele //MainApp// (entry-point pentru testare), //Utils// și //DataRepository//. | + | **Task 1 - Observer, Strategy, Factory (8p) ** |
+ | |||
+ | **Part 1. (4p) - Strategy, Factory ** | ||
+ | |||
+ | În cadrul acestui exercițiu, dorim să implementăm un magazin, ce are disponibile mai multe modalități de plată pentru clienții săi: | ||
+ | |||
+ | * folosind un voucher | ||
+ | * IBAN-ul | ||
+ | * un card pe care îl posedă | ||
+ | |||
+ | Cele 3 modalități de plată sunt reprezentate de clase ce implementează interfața **PaymentStrategy**. | ||
- | ''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''. | + | Trebuie să implementați: |
- | * 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. | + | |
- | <note tip>Vedeți metodele din [[https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Observable.html|Observable]] pentru notificarea observatorilor, schimbarea stării obiectului observat și adăugarea de observatori.</note> | + | |
+ | * metoda **pay** din cadrul fiecărei metode de plată. | ||
+ | * metoda **getPaymentMethod**, ce primește un client ca parametru și instanțiază o tranzacție folosind o anumită metodă de plată, aleasă random. | ||
- | ** [[https://beta.lambdachecker.io/problem/43/20 | 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). | + | **Part 2. (4p) - Observer ** |
- | * 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(60);'' | + | |
+ | În acest context, **subscriber-ul/observer-ul** este clasa Person. Această clasă implementează interfața **BalanceObserver**. Clasa Shop, care este **subiectul/publisher-ul** care trebuie să implementeze interfața **TransactionPublisher** va avea rolul de a **notifica** un client când o tranzacție este efectuată. | ||
- | ** [[https://beta.lambdachecker.io/problem/43/20 | Task 3]] - Factory pattern (2p)** | + | Trebuie să implementați: |
+ | * metoda **update** din cadrul clasei **Person**, ce preia notificarea faptului că un client are în cont mai puțini Lei decât o anumită limită, specifică fiecărui client în parte, printr-un mesaj adecvat (”$nume$ $prenume$, ramai fara bani, saracule!”); | ||
+ | * metoda **payBy** din cadrul clasei Person, ce întoarce valoarea de adevăr a reușitei efectuării unei plăți (dacă e posibil, efectuează plata), iar în caz negativ afisează și un mesaj adecvat (”$nume$ $prenume$, pleaca de aici, saracule!”); | ||
+ | * metoda **createTransaction** din cadrul clasei **Shop**, ce verifică dacă trebuie notificată persoana cu ajutorul metodei **update**, în urma unei tranzacții. | ||
- | 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. | ||
- | ** [[https://beta.lambdachecker.io/problem/43/20 | Task 4]] - Putting all together (2p)** | + | **Task 2 - Builder pattern (2p)** |
- | 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. | ||
- | ** [[https://beta.lambdachecker.io/problem/44/20 | Task 5]] - Builder pattern (2p)** | + | a) Examinați câmpurile din skeletul clasei **House** care includ câteva facilități obligatorii în construcția unei case, spre exemplu: |
- | a) Scrieți câmpuri în skeletul clasei House pentru câteva facilități obligatorii în construcția unei case, spre exemplu locația construcției, numărul de etaje, încălzire, camere dar și unele opționale pe care le poate selecta sau nu clientul, cum ar fi electrocasnice, piscină, panouri solare, securitate etc. | + | * locația construcției |
+ | * numărul de etaje | ||
+ | * camere | ||
+ | * unele **opționale** pe care le poate selecta sau nu clientul, cum ar fi: | ||
+ | * electrocasnice | ||
+ | * piscină | ||
+ | * panouri solare | ||
+ | * securitate | ||
- | Completați constructorul privat, metodele de get și metoda toString. | ||
+ | Completați constructorul public și metoda toString. | ||
- | b) În clasa de builder, completați câmpurile, constructorul și metodele de adăugare | + | b) În clasa **HouseBuilder**, completați câmpurile, constructorul și metodele de adăugare a facilităților opționale. |
- | a facilităților opționale. | + | |
- | c) Finalizați metoda build și testați funcționalitatea într-o clasă Main creată de voi, | + | c) Finalizați metoda build și decomentați codul din Main pentru a putea testa corectitudinea funcționalității. |
- | acoperind cazuri în care se construiește o casa doar cu facilitați obligatorii și | + | |
- | altele adăugând și pe cele opționale. | + | |
- | ** [[https://beta.lambdachecker.io/problem/41/20 | Task 6]] - Command pattern (8p)** | + | **Task 3 - Command pattern (8p)** |
- | Implementați folosind patternul Command un editor de diagrame foarte simplificat. [[https://github.com/oop-pub/oop-labs/tree/master/src/lab10 | Scheletul de cod]] conține o parte din clase și câteva teste. | + | Implementați folosind patternul Command un editor de diagrame foarte simplificat. Scheletul de cod conține o parte din clase și câteva teste. |
Componentele principale ale programului: | Componentele principale ale programului: | ||
Line 680: | Line 686: | ||
* //Client// - entry-point-ul în program | * //Client// - entry-point-ul în program | ||
- | (4p) Implementați 5 tipuri de comenzi, pentru următoarele acțiuni: | + | (4p) Implementați 4 tipuri de comenzi, pentru următoarele acțiuni: |
* Draw rectangle - crează o DiagramComponent și o adaugă în DiagramCanvas | * Draw rectangle - crează o DiagramComponent și o adaugă în DiagramCanvas | ||
* Resize - modifică width și height al unei DiagramComponent pe baza unui procent dat | * Resize - modifică width și height al unei DiagramComponent pe baza unui procent dat | ||
* Change color - modifică culoarea unei DiagramComponent | * Change color - modifică culoarea unei DiagramComponent | ||
* Change text - modifică textul unei DiagramComponent | * Change text - modifică textul unei DiagramComponent | ||
- | * Connect components - conectează o DiagramComponent la alta | ||
Implementați pe Invoker metoda execute() care va executa comanda primită ca argument. | Implementați pe Invoker metoda execute() care va executa comanda primită ca argument. | ||
Line 695: | Line 700: | ||
Comenzile implementează în afară de metodele interfeței și metoda [[https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/Object.html#toString()|toString()]] pentru a afișa comanda. Recomandăm folosirea IDE-ului pentru a o genera. | Comenzile implementează în afară de metodele interfeței și metoda [[https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/Object.html#toString()|toString()]] pentru a afișa comanda. Recomandăm folosirea IDE-ului pentru a o genera. | ||
- | (6p) Implementați în comenzi și în Invoker mecanismul de undo/redo al comenzilor. Recomandăm în Invoker sa folosiți două structuri de date, una care să mențină comenzile efectuate, iar una pentru comenzile făcute undo. Metoda reset() de pe Invoker va avea ca scop resetarea tuturor membrilor acestuia. | + | (4p) Implementați în comenzi și în Invoker mecanismul de undo/redo al comenzilor. Recomandăm în Invoker sa folosiți două structuri de date, una care să mențină comenzile efectuate, iar una pentru comenzile făcute undo. Metoda reset() de pe Invoker va avea ca scop resetarea tuturor membrilor acestuia. |