User Tools

Site Tools


Problem constructing authldap
laboratoare:design-patterns2

This is an old revision of the document!


Design patterns - Command, Builder

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.

Introducere

Î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ă.

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.

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).

Fig. 1: Diagrama de stări pentru CommandPattern

Tipuri de componente (roluri):

  • Invoker - comandantul
    • apelează acțiuni pe comenzi (invocă metode oferite de obiectele de tip Command)
    • poate menține, dacă e cazul, o listă a tutoror comenzilor aplicate pe obiectul (obiectele) comandate. Este necesară reținerea acestei liste de comenzi atunci când implementăm un comportament de undo/redo al comenzilor.
    • primește clase Command pe care să le invoce
  • Receiver - comandatul
    • este clasa asupra căreia se face apelul
    • conține implementarea efectivă a ceea ce se dorește executat
  • Command - obiectele pentru reprezentarea comenzilor implementează această interfață/o extind dacă este clasă abstractă
    • concrete command - ne referim la implementări/subclasele acesteia
    • de obicei conțin metode cu nume sugestiv pentru executarea acțiunii comenzii (e.g. execute()). Implementările acestora conțin apelul către clasa Receiver.
    • în cazul implementării unor acțiuni undoable adăugăm metode pentru undo și/sau redo.
    • țin referințe către comandați (receivers) pentru a aplica/invoca acțiunea ce reprezintă acea comandă

Î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.

Implementare

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

Builder

Soon…

Exerciții

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)

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 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 a 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.

Resurse

Referințe

laboratoare/design-patterns2.1578237726.txt.gz · Last modified: 2020/01/05 17:22 by Adriana Draghici