This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
laboratoare:design-patterns2 [2020/01/04 12:19] Adriana Draghici [Resurse] |
laboratoare:design-patterns2 [2020/01/05 19:42] (current) Adriana Draghici [Exerciții] fixed typo |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | = Design patterns - Command, Builder = | + | = 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 a soluției sau un template ce poate fi aplicat pentru rezolvarea problemei, nu 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 obiecte, fă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 a 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ă o 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 inca, cand 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 o 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 lista, tine 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 == | ||
- | Coming soon... | + | 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: | ||
+ | * //DiagramCanvas// - reprezintă o diagramă care conține obiecte de tip DiagramComponent | ||
+ | * //DrawCommand// - interfață pentru comenzile făcute asupra diagramei sau a componentelor acesteia | ||
+ | * //Invoker// - primește comenzile și le execută | ||
+ | * //Client// - entry-point-ul în program | ||
+ | ** Task 1 - Implementarea comenzilor (4p) ** | ||
- | == Resurse == | + | 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 == | ||
+ | * {{ :laboratoare:design-patterns:design-patterns-part2-skel.zip |Schelet}} | ||
+ | * [[laboratoare:old-exercises|Exerciții din alți ani]] | ||
== 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? ]] | ||