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.
În laboratoarele precedent am prezentat pattern-uri ce vă ajută în realizarea unei arhitecturi mai decuplate, modulare și extensibile a aplicațiilor:
În acest laborator vom mai prezenta înca două patternuri, Builder, folosit foarte mult, mai ales în API-uri, pentru construirea obiectelor, și Command, un pattern comportamental care decuplează obiectele care execută anumite acțiuni de obiectele care le invocă.
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…