User Tools

Site Tools


Problem constructing authldap
laboratoare:design-patterns2
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-patterns2 [2019/12/02 15:48]
Adriana Draghici
laboratoare:design-patterns2 [2020/01/05 19:42]
Adriana Draghici [Exerciții] fixed typo
Line 1: Line 1:
-= Design patterns - Command, Strategy ​=+= Design patterns - Command =
  
 == 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 - //Command// și //Builder//.+Scopul acestui laborator este familiarizarea cu folosirea ​design ​pattern-ului comportamental ​//​Command//​.
  
  
 == Introducere == == Introducere ==
  
-Design ​pattern-urile reprezintă soluții generale ​și reutilizabile ale unei probleme comune în design-ul software. Un design pattern este o descriere ​soluției sau un template ​ce poate fi aplicat pentru rezolvarea problemeinu 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 obiectefără a specifica însă forma finală a claselor sau a obiectelor implicate.+În laboratoarele precedent am prezentat ​pattern-uri ce vă ajută în realizarea unei arhitecturi mai decuplate, modulare ​și extensibile ​aplicațiilor: 
 +  * pattern-uri creaționale ​ce decuplează logica creării obiectelor: [[.static-final#​singleton-pattern|Singleton]][[.design-patterns#​factory|Factory]] 
 +  * pattern-uri comportamentale ce decuplează comunicarea ​dintre ​componente: [[.visitor#​visitor|Visitor]][[.design-patterns#​observer-pattern|Observer]],​ [[design-patterns#​strategy-pattern|Strategy]]
  
-Se consideră că există aproximativ 2000 de design patterns [[http://ptgmedia.pearsoncmg.com/images/9780321711922/​samplepages/​0321711920.pdf|[2]]]iar principalul mod de a le clasifica este următorul: +În acest laborator vom //Command//, un pattern comportamental care decuplează obiectele care execută anumite acțiuni de obiectele care le invocă.
- * **"​Gang of Four" patterns** +
- * Concurrency patterns +
- * Architectural patterns ​ - sunt folosite la un nivel mai inalt decat design patterns, stabilesc nivele și componente ale sistemelor/​aplicațiilor,​ interacțiuni ​între acestea (e.g. Model View Controller şi derivatele sale). Acestea descriu structura întregului sistem, iar multe framework-uri vin cu ele deja încoporate,​ sau faciliteaza aplicarea lor (e.g. Java Spring). ​ În cadrul laboratoarelor nu ne vom lega de acestea+
  
-O carte de referință pentru design patterns este "Design ​Patterns: Elements of Reusable Object-Oriented Software"​ [1], denumită și "Gang of Four" (GoF). Aceasta definește 23 de design patterns, foarte cunoscute și utilizate în prezent. Aplicațiile pot încorpora mai multe pattern-uri pentru a reprezenta legături dintre diverse componente (clase, module). +Design pattern-ul //​Command// ​incapsulează un apel cu tot cu parametri într-o clasă cu interfată generică. Acesta este //​Behavioral//​ pentru ​că modifică interacțiunea dintre componente, mai exact felul în care se efectuează apelurile. ​  
-În afară de GoF, și alți autori au adus în discuție pattern-uri orientate în special pentru aplicațiile enterprise și cele distribuite. +   
- + Acest pattern este recomandat în următoarele cazuri: ​  
-Pattern-urile GoF sunt clasificate în felul următor: + * pentru a ușura crearea de structuri de delegare, de callback, de apelare ​intarziată  
-* **Creational Patterns** - definesc mecanisme de creare a obiectelor + * pentru a reține lista de comenzi efectuate asupra obiectelor  
-   * Singleton, Factory etc. +     ​* accounting  
-* **Structural Patterns** - definesc relații între entități +     ​* liste de Undo, Rollback pentru tranzacții-suport pentru operații reversibile (//undoable operations//​)  
-   * Decorator, Adapter, Facade, Composite, Proxy etc. +   
-* **Behavioural Patterns** - definesc comunicarea între entități + ​Exemple de utilizare:  
-   * Visitor, Observer, Command, Mediator, Strategy etc. + * sisteme de logging, accounting pentru tranzacții  
- + * sisteme de undo (ex. editare imagini)  
-<note important>​ + * mecanism ordonat pentru delegare, apel întârziat,​ callback  
-Design pattern-urile nu trebuie privite drept niște rețete care pot fi aplicate direct pentru a rezolva o problemă din design-ul aplicației,​ pentru că de multe ori pot complica inutil arhitectura. Trebuie întâi înțeles dacă este cazul să fie aplicat un anumit pattern, si de-abia apoi adaptat pentru situația respectivă. Este foarte probabil chiar să folosiți un pattern (sau o abordare foarte similară acestuia) fără să vă dați seama sau să îl numiți explicit. Ce e important de reținut după studierea acestor pattern-uri este un mod de a aborda o problemă de design. +   
-</note> +== Funcționare și necesitate ==  
- +În esentă, Command pattern (așa cum v-ați obișnuit și lucrând ​cu celelate Pattern-uri pe larg cunoscute) presupune ​încapsularea ​unei informații ​referitoare la acțiuni/comenzi folosind un wrapper pentru a "ține minte această informație" ​și pentru a o folosi ulterior. Astfel, un astfel de wrapper va deține informații ​referitoare la tipul acțiunii ​respective (în general un asemenea wrapper va expunde o metodă ​execute(), care va descrie comportamentul pentru ​acțiunea respectivă).  
- +   
-În laboratorul [[.:​visitor|Visitor Pattern]] au fost introduse design pattern-urile și aplicabilitatea Visitor-ului. Acesta 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:​ +Mai mult încăcând vorbim de Command Pattern, ​în terminologia OOP o să întâlniți ​deseori ​și noțiunea ​de //Invoker//. Invoker-ul este un middleware ca funcționalitate ​care realizează ​managementul comenzilor. Practic, un //Client//, care vrea să facă anumite ​acțiune, va instanția ​clase care implementează ​interfață //Command//. Ar fi incomod ca, în cazul în care aceste ​instanțieri ​de comenzi provin din mai multe locuri, acest management de comenzi ​să se facă local, ​în fiecare parte (din rațiuni ​de economie, nu vrem să duplicăm ​cod). Invoker-ul apare ca o necesitate de a centraliza acest proces ​și de a realiza intern management-ul comenzilor (le ține într-o listăține cont de eventuale ​dependințe între ​ele, totul în funcție ​de context).  
- +   
-* //​Creational Patterns// - mecanisme de creare a obiectelor +Un client (generic spus, un loc de unde se lansează ​comenzi) ​instanțiază ​comenzile ​și le pasează ​Invoker-ului. Din acest motiv Invoker-ul este un middleware ​între ​client ​și receiver, ​fiindcă ​acesta va apela execute pe fiecare Command, ​în funcție ​de logica ​să internă
-   * **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. +
- +
-== Command Pattern == +
- +
-Design pattern-ul ​//Command// încapsulează un apel cu tot cu parametri într-o clasă cu interfață generică. Acesta este //​Behavioral//​ pentru ​ca modifică interacțiunea dintre componente, mai exact felul în care se efectuează apelurile.  +
- +
-Acest pattern este recomandat în următoarele cazuri:  +
-* pentru a ușura crearea de structuri de delegare, de callback, de apelare ​întârziată +
-* pentru a reține lista de comenzi efectuate asupra obiectelor +
-    * accounting +
-    * liste de Undo, Rollback pentru tranzacții - suport pentru operații reversibile (//undoable operations//​) +
- +
-Exemple de utilizare:​ +
-* sisteme de logging, accounting pentru tranzacții +
-* sisteme de undo (ex. editare imagini) +
-* mecanism ordonat pentru delegare, apel întârziat,​ callback +
- +
-=== Functionare si necesitate ​=== +
-In esenta, Command pattern (asa cum v-ati obisnuit si lucrand ​cu celelate Pattern-uri pe larg cunoscute) presupune ​incapsularea ​unei informatii ​referitoare la actiuni/comenzi folosind un wrapper pentru a "tine minte aceasta informatie" ​si pentru a o folosi ulterior. Astfel, un astfel de wrapper va detine informatii ​referitoare la tipul actiunii ​respective (in general un asemenea wrapper va expunde o metoda ​execute(), care va descrie comportamentul pentru ​actiunea respectiva). +
- +
-Mai mult incacand vorbim de Command Pattern, ​in terminologia OOP o sa intalniti ​deseori ​si notiunea ​de Invoker. Invoker-ul este un middleware ca functionalitate ​care realizeaza ​managementul comenzilor. Practic, un Client, care vrea sa faca anumite ​actiune, va instantia ​clase care implementeaza ​interfata ​Command. Ar fi incomod ca, in cazul in care aceste ​instantieri ​de comenzi provin din mai multe locuri, acest management de comenzi ​sa se face local, ​in fiecare parte (din ratiuni ​de economie, nu vrem sa duplicam ​cod). Invoker-ul apare ca o necesitate de a centraliza acest proces ​si de a realiza intern management-ul comenzilor (le tine intr-o listatine cont de eventuale ​dependinte intre ele, totul in functie ​de context). +
- +
-Si nu in cele din urma, un client (generic spus, un loc de unde se lanseaza ​comenzi) ​instantiaza ​comenzile ​si le paseaza ​Invoker-ului. Din acest motiv Invoker-ul este un middleware ​intre client ​si receiver, ​fiindca ​acesta va apela execute pe fiecare Command, ​in functie ​de logica ​sa interna.+
  
 //​__Recomandare__//:​ La Referinte aveti un link catre un post pe StackOverflow,​ pentru a intelege mai bine de ce aveti nevoie de Pattern-ul Command si de ce nu lansati comenzi pur si simplu. //​__Recomandare__//:​ La Referinte aveti un link catre un post pe StackOverflow,​ pentru a intelege mai bine de ce aveti nevoie de Pattern-ul Command si de ce nu lansati comenzi pur si simplu.
  
- +== Structura ==
-=== Structura ​===+
  
 Ideea principală este de a crea un obiect de tip //Command// care va reține parametrii pentru comandă. Comandantul reține o referință la comandă și nu la componenta comandată. Comanda propriu-zisă este anunțată obiectului //Command// (de către comandant) prin execuția unei metode specificate asupra lui. Obiectul //Command// este apoi responsabil de trimiterea (//​dispatch//​) comenzii către obiectele care o îndeplinesc (//​comandați//​). ​ Ideea principală este de a crea un obiect de tip //Command// care va reține parametrii pentru comandă. Comandantul reține o referință la comandă și nu la componenta comandată. Comanda propriu-zisă este anunțată obiectului //Command// (de către comandant) prin execuția unei metode specificate asupra lui. Obiectul //Command// este apoi responsabil de trimiterea (//​dispatch//​) comenzii către obiectele care o îndeplinesc (//​comandați//​). ​
Line 89: Line 60:
 În diagrama din <imgref cmd_image>,​ comandantul este clasa //Invoker// care conține o referință la o instanță (command) a clasei (Command). //Invoker// va apela metoda abstractă ''​execute()''​ pentru a cere îndeplinirea comenzii. //​ConcreteCommand//​ reprezintă o implementare a interfeței //​Command//,​ iar în metoda ''​execute()''​ va apela metoda din //​Receiver//​ corespunzătoare acelei acțiuni/​comenzi. În diagrama din <imgref cmd_image>,​ comandantul este clasa //Invoker// care conține o referință la o instanță (command) a clasei (Command). //Invoker// va apela metoda abstractă ''​execute()''​ pentru a cere îndeplinirea comenzii. //​ConcreteCommand//​ reprezintă o implementare a interfeței //​Command//,​ iar în metoda ''​execute()''​ va apela metoda din //​Receiver//​ corespunzătoare acelei acțiuni/​comenzi.
   ​   ​
-=== Implementare ​===+=== Exemplu ​===
  
 Diagrama de secvență din <imgref cmd_flow_image>​ prezintă apelurile în cadrul unei aplicație de editare a imaginilor, ce este structurată folosind pattern-ul Command. În cadrul acesteia, Receiver-ul este //Image//, iar comenzile //​BlurCommand//​ și //​CropCommand//​ modifică starea acesteia. Structurând aplicația în felul acesta, este foarte ușor de implementat un mecanism de undo/redo, fiind suficient să menținem în Invoker o listă cu obiectele de tip //Command// aplicate imaginii. ​ Diagrama de secvență din <imgref cmd_flow_image>​ prezintă apelurile în cadrul unei aplicație de editare a imaginilor, ce este structurată folosind pattern-ul Command. În cadrul acesteia, Receiver-ul este //Image//, iar comenzile //​BlurCommand//​ și //​CropCommand//​ modifică starea acesteia. Structurând aplicația în felul acesta, este foarte ușor de implementat un mecanism de undo/redo, fiind suficient să menținem în Invoker o listă cu obiectele de tip //Command// aplicate imaginii. ​
Line 98: Line 69:
 <​imgcaption cmd_flow_image2|Diagrama de secvență pentru comenzile de aprindere/​stingere a switch-ului>​{{ .:​design-patterns:​command_switch_sequence.png | }}</​imgcaption>​ <​imgcaption cmd_flow_image2|Diagrama de secvență pentru comenzile de aprindere/​stingere a switch-ului>​{{ .:​design-patterns:​command_switch_sequence.png | }}</​imgcaption>​
  
-== Builder == +
-Soon...+
 == Exerciții == == Exerciții ==
  
-Acest laborator ș[[laboratoare:​design-patterns|cel precedent]] 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ștrii. În acestă săptămână terminam jocul inceput in laboratorul precedent folosind pattern-urile studiate (//​Strategy//,​ //​Command//​).+Implementațfolosind patternul Command un editor de diagrame foarte simplificat. Scheletul de cod conține o parte din clase și câteva teste.
  
-Detalii joc+Componentele principale ale programului
-* //Harta// +  * //DiagramCanvas// - reprezintă o diagramă care conține ​obiecte de tip DiagramComponent 
-   * 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 și monștrii). +  * //DrawCommand// - interfață pentru comenzile făcute asupra diagramei sau componentelor acesteia 
-   * este menținută în clasa ''​GameState''​. +  * //Invoker// - primește comenzile și le execută 
-* //Eroii//  +  * //Client// - entry-point-ul în program
-   * 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''​ - ataca un monstru cand se afla pe aceeasi pozitie cu el +
-     * ''​collect''​ - eroul ia comoara găsită în zona în care se află +
-* Entry-point-ul în joc îl consitituie clasa ''​Main''​. +
-* **(5p)** Folosiți design pattern-ul //Command// pentru ​implementa functionalitatea de undo si redo la comanda ''​move''​. +
-   ​Momentan, aveti erori de compilare in clasele Main si GameState. Dupa ce veti implementa acest exercitiu, se vor rezolva, nu modificati in mod direct acolo. +
-   * Va trebui sa completati clasa ''​MoveCommand''​ care implementeaza interfata ''​Command''​. Urmariti ​//TODO-urile// din aceasta clasa.  +
-   * //​__Hint__//:​ Pentru Undo, de exemplu, daca v-ati deplasat la dreapta, ar trebui sa va deplasati la stanga. Creati-va o metoda ajutatoare care trateaza astfel de cazuri. +
-   * Precum si clasa ''​CommandManager''​ care va tine evidenta comenzilor si ordinea lor.  +
-   * //__Hint__//: Amintiti-va de la cursul de Structuri de Date cum se implementeaza operatiile Redo si Undo. Folositi doua stive. +
-* **(5p)** Folosiți design pattern-ul //​Strategy//​ pentru a implementa logica de atac a unui monstru. +
-   * Pentru acest exercitiu va trebui sa implementati 2 strategii: **AttackStrategy** si **DefenseStrategy**. Ambele vor implementa Strategy si metodele aferente. Fiecare din aceste Strategy, va retine o referinta interna la un Hero. Abordarea este urmatoarea:​ +
-    * Exista 3 clase Hero, fiecare cu cate un DamageType aferent: Warrior - Blunt, Mage - Magic, Priest - Poison  +
-    * Fiecare monstru are un weakness (slabiciune la atacurile de tip Blunt, Magic sau Poison) +
-    * **AttackStrategy**:​ In metoda attack(), veti itera prin inventory-ul eroului si veti verifica daca exista un obiect Treasure care are DamageType-ul identic cu cel al eroului. Daca da, atunci damage-ul pe care il veti da unui obiect Monster este **3 x damage-ul treasure-ului**. Daca nu aveti un astfel de Treasure, atunci cautati un Treasure cu un DamageType identic cu cel al mob-ului (mobul poate fi vulnerabil la Magic, de ex.). Daca gasiti un astfel de Treasure, damage-ul pe care il veti imprima mob-ului este **2 x damage-ul treasure-ului**. Daca nu, veti scade din HP-ul mob-ului rezultatul apelului metodei getBaseDamage() asupra Hero-ului. +
-    * **DefenseStrategy**:​ In metoda attack(), veti itera prin inventory-ul eroului si veti verifica, la fel, daca exista un obiect Treasure care are DamageType-ul identic cu cel al eroului. Daca da, atunci veti da un boost de HP egal cu treasure.getBoostHp() + getBaseHpBoost() eroului (getBaseHpBoost este implementata in clasa Hero). Daca nu, veti da doar un boost egal cu ce va intoarce getBaseHpBoost(). Damage-ul imprimat mobului va fi, de asemenea, egal cu ce va intoarce getBaseDamage(). Adaugati log-uri in clasa DefenseStrategy,​ pentru a va asigura ca i se mareste viata eroului. +
-    * Urmariti si TODO-urile din cele doua clase +
-   * Implementati metoda //attack// din clasa **Hero** astfel incat, daca eroul are mai mult de **50HP**, folositi strategia **AttackStrategy**. Altfel, folositi **DefenseStrategy**. Urmariti TODO-urile din cod. +
-* (**Bonus 2p**) Implementați coliziunile cu obstacolele de pe harta +
-  * Va trebui sa creati un nou obiect ''​Obstacle''​ precum si un ''​ObstacleObserver''​ +
-  * Cand eroul ajunge pe un obstacol se va printa un mesaj ''​Can'​t move there !''​ si se va apela automat undo pe ultima comanda de move pentru a reveni in pozitia anterioara coliziunii. Acest feature de wall collision va fi implementat in ''​ObstacleObserver''​+
  
 +** Task 1 - Implementarea comenzilor (4p) **
  
 +Implementați 5 tipuri de comenzi, pentru următoarele acțiuni:
 +  * Draw rectangle - crează o DiagramComponent și o adaugă în DiagramCanvas
 +  * Resize - modifică width și height al unei DiagramComponent pe baza unui procent dat
 +  * Change color - modifică culoarea unei DiagramComponent
 +  * Change text - modifică textul unei DiagramComponent
 +  * Connect components - conectează o DiagramComponent la alta
 +
 +Comenzile primesc în __constructor__ referința către DiagramCanvas și alte argumente necesare lor. De exemplu, comanda pentru schimbarea culorii trebuie sa primească și culoarea nouă și indexul componentei.
 +
 +Pentru acest task nu este nevoie să implementați și metoda //undo()//, doar //​execute()//​.
 +
 +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.
 +
 +** Task 2 - Testarea comenzilor (2p) **
 +
 +Scheletul conține în clasa ''​Test''​ metode pentru a testa comportamentul comenzilor. O parte sunt deja implementate,​ iar o parte trebuie implementate.
 +
 +** Task 3 - Undo/redo (2p) **
 +
 +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.
 +
 +** Task 4 - Test undo/redo (2p) **
 +
 +Scheletul conține în clasa Test o metodă pentru o verificare simplă a corectitudinii implementării undo și redo. Completați metoda //​testComplexUndoRedo//​ în care să faceți multiple undo-uri și redo-uri pentru diverse tipuri de comenzi.
 +
 +
 +/*
 +
 +TODO pt 2020 (acum nu am avut timp):
 +- builder pattern in textul labului
 +- un exercitiu care sa ia exemplul de cod pus la partea de teorie si sa il ruleze si sa ii aplice doar o mica modificare
 +
 +*/
  
 == Resurse == == Resurse ==
  
-* {{.:​design-patterns:​src.zip|Schelet}} +* {{ :​laboratoare:​design-patterns:​design-patterns-part2-skel.zip |Schelet}} 
-{{:laboratoare:​design-patterns:​lab11-sol.zip|Soluție}} +[[laboratoare:​old-exercises|Exerciții din alți ani]]
-* <​html><​a class="​media mediafile mf_pdf"​ href="/​poo/​laboratoare/​design-patterns2?​do=export_pdf">​PDF laborator</​a></​html>​+
  
 == Referințe == == Referințe ==
  
-* [[http://​www.codeproject.com/​Articles/​15207/​Design-Patterns-Command-Pattern |Descriere Command Pattern si exemplu pentru operatii undo/​redo]] +* [[https://​sourcemaking.com/​design_patterns/​command | Command ​design ​pattern]]
-* [[https://​sourcemaking.com/​design_patterns/​command | O resursa buna pentru a intelege cele mai des intalnite ​design ​patterns]]+
 * [[https://​stackoverflow.com/​questions/​32597736/​why-should-i-use-the-command-design-pattern-while-i-can-easily-call-required-met | De ce avem nevoie de Command Pattern? ]] * [[https://​stackoverflow.com/​questions/​32597736/​why-should-i-use-the-command-design-pattern-while-i-can-easily-call-required-met | De ce avem nevoie de Command Pattern? ]]
-* [[https://​stackoverflow.com/​questions/​1710809/​when-and-why-should-the-strategy-pattern-be-used | De ce avem nevoie de Strategy Pattern? ]]+
  
laboratoare/design-patterns2.txt · Last modified: 2020/01/05 19:42 by Adriana Draghici