Administrativ
Laboratoare
Tema
Teste
Resurse utile
Alte resurse
Arhiva Teme
Administrativ
Laboratoare
Tema
Teste
Resurse utile
Alte resurse
Arhiva Teme
This is an old revision of the document!
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.
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.
Se consideră că există aproximativ 2000 de design patterns [2], iar principalul mod de a le clasifica este următorul:
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). Î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.
Pattern-urile GoF sunt clasificate în felul următor:
În laboratorul 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:
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:
Exemple de utilizare:
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.
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).
Fig. 1: Diagrama de stări pentru CommandPattern
Tipuri de componente (roluri):
execute()
). Implementările acestora conțin apelul către clasa Receiver.undo
și/sau redo
. În Java, se pot folosi atât interfețe cât și clase abstracte, pentru Command, depinzând de situație (e.g. clasă abstractă dacă știm sigur ca obiectele de tip Command nu mai au nevoie să extindă și alte clase).
În diagrama din figure 1, 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.
Diagrama de secvență din figure 2 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.
Fig. 2: Diagrama de secvență pentru comenzile de prelucrare a imaginilor
Pe wikipedia puteți analiza exemplul PressSwitch. Flow-ul pentru acesta este ilustrat în figure 3
Fig. 3: Diagrama de secvență pentru comenzile de aprindere/stingere a switch-ului
Soon…
Coming soon…