Differences

This shows you the differences between two versions of the page.

Link to this comparison view

poo-ca-cd:arhiva:laboratoare:2024:design-patterns [2025/09/27 10:43] (current)
florian_luis.micu created
Line 1: Line 1:
 +===== Laboratorul 7: Design patterns - Factory, Strategy, Observer, Command, Builder =====
  
 +**Video introductiv:​** [[https://​www.youtube.com/​watch?​v=khRqWZrz1YU | link]] + [[https://​www.youtube.com/​watch?​v=F2HMN4mvVYY | link]]
 +
 +
 +==== 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 - //​Factory//,​ //​Strategy//,​ //​Observer//,​ //Command// și //​Builder//​.
 +
 +
 +==== 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.
 +
 +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:
 +  * **"​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).
 +Î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:
 +  * **Creational Patterns** - definesc mecanisme de creare a obiectelor
 +    * 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.
 +
 +<note important>​
 +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>​
 +
 +
 +În laboratoarele precedente au fost descrise patternurile [[https://​ocw.cs.pub.ro/​courses/​poo-ca-cd/​laboratoare/​constructori-referinte|Singleton]] și [[https://​ocw.cs.pub.ro/​courses/​poo-ca-cd/​laboratoare/​visitor|Visitor]]. //​Singleton//​ este un pattern creațional,​ simplu, a cărui folosire este controversată (vedeți în [[:​poo-ca-cd:​laboratoare:​static-final#​singleton_pattern|laborator]] explicația cu anti-pattern). //Visitor// 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.
 +
 +
 +==== Factory ====
 +
 +Patternurile de tip Factory sunt folosite pentru obiecte care generează instanțe de clase înrudite (implementează aceeași interfață,​ moștenesc aceeași clasă abstractă). Acestea sunt utilizate atunci când dorim să izolăm obiectul care are nevoie ​ de o instanță de un anumit tip, de creearea efectivă acesteia. În plus clasa care va folosi instanța nici nu are nevoie să specifice exact subclasa obiectului ce urmează a fi creat, deci nu trebuie să cunoască toate implementările acelui tip, ci doar ce caracteristici trebuie să aibă obiectul creat. Din acest motiv, Factory face parte din categoria //​Creational Patterns//, deoarece oferă o soluție legată de creearea obiectelor.
 +
 +
 +
 +<note tip>
 +**Aplicabilitate:​**
 +
 +    * În biblioteci/​API-uri,​ utilizatorul este separat de implementarea efectivă a tipului și trebuie sa folosească metode factory pentru a obține anumite obiecte. Clase care oferă o astfel de funcționalitate puteți găsi și in core api-ul de Java, in api-ul java.nio (e.g. clasa [[http://​docs.oracle.com/​javase/​8/​docs/​api/​java/​nio/​file/​FileSystems.html|FileSystems]]),​ în Android SDK (e.g. [[http://​developer.android.com/​reference/​javax/​net/​SocketFactory.html|clasa SocketFactory]]) etc.
 +    * **Atunci când crearea obiectelor este mai complexă** (trebuie realizate mai multe etape etc.), este mai util să separăm logica necesară instanțierii subtipului de clasa care are nevoie de acea instanță. :!: Asta înseamnă că puteți folosi metode factory care să vă construiască obiectul și dacă aveți doar un tip, nu mai multe.
 +
 +</​note>​
 +
 +=== Abstract Factory Pattern ===
 +
 +/* <​imgcaption af_image|Diagrama de clase pentru Abstract Factory>​*/​ {{ .:​design-patterns:​abstractfactory.png | Diagrama de clase pentru Abstract Factory}} /​*</​imgcaption>​ */
 +
 +Codul următor corespunde diagramei din /* <imgref af_image>​ */{{ .:​design-patterns:​abstractfactory.png?​linkonly | figure 1}} . În acest caz folosim interfețe pentru factory și pentru tip, însă în alte situații putem să avem direct //​SpecializedFooFactory,//​ fără a implementa interfața //​FooFactory//​. ​
 +
 +<code java>
 +
 +public interface Foo {
 +    public void bar();
 +}
 +public interface FooFactory {
 +    public Foo createFoo();​
 +}
 +public class SpecializedFoo implements Foo {
 +    ... 
 +}
 +public class SpecializedFooFactory implements FooFactory {
 +    public Foo createFoo() {
 +        return new SpecializedFoo();​
 +    }
 +}
 +</​code>​
 +
 +
 +=== Factory Method Pattern === 
 +
 +Folosind pattern-ul Factory Method se poate defini o interfață pentru crearea unui obiect. Clientul care apelează metoda factory nu știe/nu îl interesează de ce subtip va fi la runtime instanța primită.
 +
 +Spre deosebire de Abstract Factory, Factory Method ascunde construcția unui obiect, nu a unei familii de obiecte "​inrudite",​ care extind un anumit tip. Clasele care implementează Abstract Factory conțin de obicei mai multe metode factory.
 +
 +/* <​imgcaption fm_image|Diagrama de clase pentru Factory Method> */ {{ .:​design-patterns:​factorymethod.png?​510 | Diagrama de clase pentru Factory Method}}
 +/* </​imgcaption>​ */
 +
 +=== Exemplu ===
 +
 +Situația cea mai întâlnită în care se potrivește acest pattern este aceea când trebuie instanțiate multe clase care implementează o anumită interfață sau extind o altă clasă (eventual abstractă),​ ca în exemplul de mai jos. Clasa care folosește aceste subclase nu trebuie să "​știe"​ tipul lor concret ci doar pe al părintelui. Implementarea de mai jos corespunde pattern-ului Abstract Factory pentru clasa //​PizzaFactory//,​ și foloseste factory method pentru metoda ''​createPizza''​.
 +
 +<code java PizzaLover.java>​
 +abstract class Pizza {
 +    public abstract double getPrice();
 +}
 +class HamAndMushroomPizza extends Pizza {
 +    public double getPrice() {
 +        return 8.5;
 +    }
 +}
 +class DeluxePizza extends Pizza {
 +    public double getPrice() {
 +        return 10.5;
 +    }
 +}
 +class HawaiianPizza extends Pizza {
 +    public double getPrice() {
 +        return 11.5;
 +    }
 +}
 +
 +class PizzaFactory {
 +    public enum PizzaType {
 +        HamMushroom,​ Deluxe, Hawaiian
 +    }
 +    public static Pizza createPizza(PizzaType pizzaType) {
 +        switch (pizzaType) {
 +            case HamMushroom:​ return new HamAndMushroomPizza();​
 +            case Deluxe: ​     return new DeluxePizza();​
 +            case Hawaiian: ​   return new HawaiianPizza();​
 +        }
 +        throw new IllegalArgumentException("​The pizza type " + pizzaType + " is not recognized."​);​
 +    }
 +}
 +public class PizzaLover {
 +    public static void main (String args[]) {
 +        for (PizzaFactory.PizzaType pizzaType : PizzaFactory.PizzaType.values()) {
 +            System.out.println("​Price of " + pizzaType + " is " + PizzaFactory.createPizza(pizzaType).getPrice());​
 +        }
 +    }
 +}
 +</​code>​
 +<​code>​
 +Output:
 +Price of HamMushroom is 8.5
 +Price of Deluxe is 10.5
 +Price of Hawaiian is 11.5
 +</​code>​
 +
 +
 +=== Singleton Factory ===
 +
 +De obicei avem nevoie ca o clasă factory să fie utilizată din mai multe componente ale aplicației. Ca să economisim memorie este suficient să avem o singură instanță a factory-ului și să o folosim pe aceasta. Folosind pattern-ul Singleton putem face clasa factory un singleton, și astfel din mai multe clase putem obține instanță acesteia.
 +
 +Un exemplu ar fi Java Abstract Window Toolkit ([[http://​en.wikipedia.org/​wiki/​Abstract_Window_Toolkit|AWT]]) ce oferă clasa abstractă [[http://​docs.oracle.com/​javase/​8/​docs/​api/​java/​awt/​Toolkit.html | java.awt.Toolkit]] care face legătura dintre componentele AWT și implementările native din toolkit. Clasa //Toolkit// are o metodă factory ''​Toolkit.getDefaultToolkit()''​ ce întoarce subclasa de //Toolkit// specifică platformei. Obiectul //Toolkit// este un Singleton deoarece AWT are nevoie de un singur obiect pentru a efectua legăturile și deoarece un astfel de obiect este destul de costisitor de creat. Metodele trebuie implementate în interiorul obiectului și nu pot fi declarate statice deoarece implementarea specifică nu este cunoscută de componentele independente de platformă. ​
 +
 +==== Observer Pattern ====
 +
 +Design Pattern-ul //​Observer//​ definește o relație de dependență 1 la n între obiecte astfel încât când un obiect își schimbă starea, toți dependenții lui sunt notificați și actualizați automat. Folosirea acestui pattern implică existența unui obiect cu rolul de //​subiect//,​ care are asociată o listă de obiecte dependente, cu rolul de //​observatori//,​ pe care le apelează automat de fiecare dată când se întâmplă o acțiune. ​
 +  ​
 +Acest pattern este de tip //​Behavioral//​ (comportamental),​ deorece facilitează o organizare mai bună a comunicației dintre clase în funcție de rolurile/​comportamentul acestora. ​
 +
 +<note tip>
 +**Aplicabilitate**
 +
 +Observer se folosește în cazul în care mai multe clase(//​observatori//​) depind de comportamentul unei alte clase(//​subiect//​),​ în situații de tipul: ​  
 +  * o clasă implementează/​reprezintă logica, componenta de bază, iar alte clase doar folosesc rezultate ale acesteia (monitorizare).  ​
 +  * o clasă efectuează acțiuni care apoi pot fi reprezentate în mai multe feluri de către alte clase (view-uri ca în figură de mai jos). 
 +  ​
 +Practic în toate aceste situații clasele Observer **observă** modificările/​acțiunile clasei Subject. Observarea se implementează prin **notificări inițiate din metodele clasei Subject**.</​note> ​
 +
 +=== Structură ===
 +
 +Pentru aplicarea acestui pattern, clasele aplicației trebuie să fie structurate după anumite roluri, și în funcție de acestea se stabilește comunicarea dintre ele. În exemplul din /* <imgref obs_image>​ */ {{ .:​design-patterns:​observer.png?​linkonly ​ | figure 3}} , avem două tipuri de componente, //Subiect// și //​Observator//,​ iar //​Observator//​ poate fi o interfață sau o clasă abstractă ce este extinsă cu diverse implementări,​ pentru fiecare tip de monitorizare asupra obiectelor //​Subiect//​.
 +    * observatorii folosesc datele subiectului
 +    * observatorii sunt notificați automat de schimbări ale subiectului
 +    * subiectul cunoaște toți observatorii
 +    * subiectul poate adăuga noi observatori
 +
 +{{ .:​design-patterns:​observer.png | Diagrama de clase pentru Observer Pattern }}
 +
 +
 +**Subiect**
 +  * nu trebuie să știe ce fac observatorii,​ trebuie doar să mențină referințe către obiecte de acest tip
 +  * nu știe ce fac observatorii cu datele
 +  * oferă o metodă de adăugare a unui //​Observator//,​ eventual și o metodă prin care se pot deinregistra observatori ​
 +  * menține o listă de referințe cu observatori
 +  * când apar modificări (e.g. se schimbă starea sa, valorile unor variabile etc) notifică toți observatorii
 +
 +**Observator**
 +  * definește o interfață notificare despre schimbări în subiect ​
 +  * ca implementare: ​
 +    * toți observatorii pentru un anumit subiect trebuie să implementeze această interfață ​
 +    * oferă una sau mai multe metode care să poată fi invocate de către //Subiect// pentru a notifica o schimbare. Ca argumente se poate primi chiar instanța subiectului sau obiecte speciale care reprezintă evenimentul ce a provocat schimbarea. ​
 +   
 +**View/​ObservatorDerivat**
 +  * implementează interfața Observator
 +
 +Aceasta schemă se poate extinde, în funcție de aplicație, observatorii pot ține referințe catre subiect sau putem adauga clase speciale pentru reprezentarea evenimentelor,​ notificarilor.
 +
 +
 +=== Implementare ===
 +
 +Tookit-urile GUI, cum este și [[http://​en.wikipedia.org/​wiki/​Swing_%28Java%29|Swing]] folosesc acest design pattern, de exemplu apăsarea unui buton generează un eveniment ce poate fi transmis mai multor //​listeners//​ înregistrați acestuia.
 +
 +API-ul Java oferă clasele [[http://​docs.oracle.com/​javase/​8/​docs/​api/​java/​util/​Observer.html |Observer]] și [[http://​docs.oracle.com/​javase/​8/​docs/​api/​java/​util/​Observable.html |Observable]] care pot fi subclasate pentru a implementa propriile tipuri de obiecte ce trebuie monitorizate și observatorii acestora.
 +
 +Pentru cod complex, concurent, cu evenimente asincrone, recomandăm RxJava, care folosește Observer pattern: [[https://​github.com/​ReactiveX/​RxJava/​wiki|github]],​ [[https://​dzone.com/​articles/​rxjava-part-1-a-quick-introduction|exemplu]].
 +
 +==== Strategy Pattern ====
 +
 +Design pattern-ul //​Strategy//​ încapsulează algoritmii în clase ce oferă o anumită interfață de folosire, și pot fi selecționați la runtime. Ca și Command, acest pattern este //​behavioral//​ pentru ca permite decuplarea unor clase ce oferă un anumit comportament și folosirea lor independentă în funcție de situația de la runtime.
 +
 +Acest pattern este recomandat în cazul în care avem nevoie de un tip de algoritm (strategie) cu mai multe implementări posibile si dorim să alegem dinamic care algoritm îl folosim, fără a face sistemul prea strâns cuplat.
 +
 +**Exemple de utilizare**:​
 +  * sisteme de tip Layout Managers din API-urile pentru UI
 +  * selectarea in mod dinamic la runtime a unor algoritmi de sortare, compresie, criptare etc.
 +
 +**Structură**:​
 +  * trebuie să definiți o **interfață comună** pentru strategiile pe care le implementați (fie ca o <<​interface>>​ sau ca o clasa abstractă)
 +  * implementați strategiile respectând interfața comună
 +  * clasa care are nevoie să folosească strategiile **va ști doar despre interfața lor**, nu va fi legată de implementările concrete
 +
 +Denumirile uzuale în exemplele acestui pattern sunt: //​Strategy//​ (pt interfață sau clasa abstractă),​ //​ConcreteStrategy//​ pentru implementare,​ //​Context//,​ clasa care folosește/​execută strategiile.
 +
 +//​__Recomandare__//: ​ Urmăriți link-ul de la referințe către postul de pe Stack Overflow care descrie necesitatea pattern-ului Strategy. Pe lângă motivul evident de încapsulare a prelucrărilor/​algoritmilor (care reprezintă strategiile efective), se preferă o anumită abordare: la runtime se verifică mai multe condiții și se decide asupra strategiei. Concret, folosind mecanismul de polimorfism dinamic, se folosește o anumită instanță a tipului de strategie (ex. Strategy str = new CustomStrategy),​ care se pasează în toate locurile unde este nevoie de Strategy. Practic, în acest fel, utilizatorii unei anumite strategii vor deveni agnostici în raport cu strategia utilizată, ea fiind instanțiată într-un loc anterior și putând fi gata utilizată. Gândiți-vă la browserele care trebuie să detecteze dacă device-ul este PC, smartphone, tabletă sau altceva și în funcție de acest lucru să randeze în mod diferit. Fiecare randare poate fi implementată ca o strategie, iar instanțierea strategiei se va face într-un punct, fiind mai apoi pasată în toate locurile unde ar trebui să se țină cont de această strategie. ​
 +
 +==== Command ====
 +
 +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.  ​
 +  ​
 +Acest pattern este recomandat în următoarele cazuri:  ​
 +  * pentru a ușura crearea de structuri de delegare, de callback, de apelare intarziată ​
 +  * 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 ​
 +  ​
 +=== 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ă). ​
 +  ​
 +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 dependențe între ele, totul în funcție de context). ​
 +  ​
 +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ă. ​
 +
 +//​__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//​). ​
 +
 +{{ .:​design-patterns:​command.png |Diagrama de stări pentru Command Pattern}}
 +  ​
 + ​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 prima diagramă de mai jos, 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.
 +  ​
 +=== Exemplu ===
 +
 +Prima diagramă de secvență 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 //​ResizeCommand//​ 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. ​
 +
 +{{ .design-patterns:​imageeditor_example.png | Diagrama de secvență pentru comenzile de prelucrare a imaginilor}}
 +
 +Pornind de la această diagramă, putem realiza o implementare a pattern-ului Command. ​
 +Vom construi clasa //Image//, care va juca rolul Receiver-ului. Acesteia îi vom asocia un câmp //​blurStrength//,​ care ne va oferi informații despre intensitatea filtrului de blur, și încă două câmpuri //length// și //width// care ne vor spune ce dimensiune are imaginea. Valorile acestor câmpuri vor fi alterate în urma aplicării comezilor de //blur// și //resize//.
 +
 +<code java>
 +public class Image {
 +   ​private int blurStrength;​
 +   ​private int length;
 +   ​private int width;
 +
 +   ​public Image(int length, int width) {
 +       ​this.length = length;
 +       ​this.width = width;
 +   }
 +
 +   ​public int getBlurStrength() {
 +       ​return blurStrength;​
 +   }
 +
 +   ​public void setBlurStrength(int blurStrength) {
 +       ​this.blurStrength = blurStrength;​
 +   }
 +
 +   ​public int getLength() {
 +       ​return length;
 +   }
 +
 +   ​public void setLength(int length) {
 +       ​this.length = length;
 +   }
 +
 +   ​public int getWidth() {
 +       ​return width;
 +   }
 +
 +   ​public void setWidth(int width) {
 +       ​this.width = width;
 +   }
 +}
 +</​code>​
 +
 +//Command// va fi o interfață,​ căreia pe langă metoda de //​execute()//​ îi vom asocia și o metodă de //​undo()//​. ​
 +<code java>
 +interface Command {
 +   void execute();
 +
 +   void undo();
 +}
 +</​code>​
 +
 +//​BlurCommand//​ și //​ResizeCommand//​ vor implementa interfața //​Command//​. La apelul //​execute()//,​ //​BlurCommand//​ va modifica câmpul //​blurStrength//​ din clasa //Image//, iar //​ResizeCommand//​ va modifica dimensiunea,​ lungimea și înălțimea imaginii. Întrucât ne dorim să implementăm un mecanism de undo, este nevoie să reținem valoare anterioară.
 +
 +<code java>
 +// Concrete command
 +
 +public class BlurCommand implements Command {
 +   ​private final Image image;
 +   ​private int previousBlurStrength;​
 +   ​private int nextBlurStrength;​
 +
 +   ​public BlurCommand(Image image, int blurStrength) {
 +       ​this.image = image;
 +       ​this.nextBlurStrength = blurStrength;​
 +   }
 +
 +   ​@Override
 +   ​public void execute() {
 +       ​previousBlurStrength = image.getBlurStrength();​
 +       ​image.setBlurStrength(nextBlurStrength);​
 +   }
 +
 +   ​@Override
 +   ​public void undo() {
 +       ​nextBlurStrength = previousBlurStrength;​
 +       ​previousBlurStrength = image.getBlurStrength();​
 +       ​image.setBlurStrength(nextBlurStrength);​
 +   }
 +}
 +
 +public class ResizeCommand implements Command {
 +   ​private final Image image;
 +   ​private int previousWidth;​
 +   ​private int previousLength;​
 +   ​private int nextWidth;
 +   ​private int nextLength;
 +
 +   ​public ResizeCommand(Image image, int width, int length) {
 +       ​this.image = image;
 +       ​nextWidth = width;
 +       ​nextLength = length;
 +   }
 +
 +   ​@Override
 +   ​public void execute() {
 +       ​previousWidth = image.getWidth();​
 +       ​image.setWidth(nextWidth);​
 +
 +       ​previousLength = image.getLength();​
 +       ​image.setLength(nextLength);​
 +   }
 +
 +   ​@Override
 +   ​public void undo() {
 +       ​nextWidth = previousWidth;​
 +       ​previousWidth = image.getWidth();​
 +       ​image.setWidth(nextWidth);​
 +
 +       ​nextLength = previousLength;​
 +       ​previousLength = image.getLength();​
 +       ​image.setLength(nextLength);​
 +   }
 +}
 +</​code>​
 +Invoker-ul este clasa //Editor//. Aceasta va avea două metode, edit si undo, care vor fi apelate de către user. În plus, vom păstra în această clasă și o listă a comenzilor aplicate pe imagine. Cu acestă listă vom putea implementăm un comportament de undo.\\
 +Metoda edit va primi ca parametru o referiță la o instanță command, apoi va fi inițiată o cerere de către //Editor// prin apelarea metodei //​execute()//,​ cerând astfel execuția comenzii.
 +
 +<code java>
 +// Invoker
 +
 +public class Editor {
 +   // LinkedList este folosit ca stivă în Java
 +   ​private LinkedList<​Command>​ history = new LinkedList<>​();​ // păstrează comenzile aplicate pe imagine
 +
 +   ​public void edit(Command command) {
 +       ​history.push(command);​
 +       ​command.execute();​
 +   }
 +
 +   ​public void undo() {
 +       if (history.isEmpty()) return;
 +
 +       ​Command command = history.pop();​
 +       if (command != null) {
 +           ​command.undo();​
 +       }
 +   }
 +}
 +
 +</​code>​
 +  ​
 +Pe [[http://​en.wikipedia.org/​wiki/​Command_pattern | Wikipedia]] puteți analiza exemplul PressSwitch. Flow-ul pentru acesta este ilustrat în diagrama de mai jos
 +{{ .:​design-patterns:​command_switch_sequence.png | Diagrama de secvență pentru comenzile de aprindere/​stingere a switch-ului }}
 +
 +==== Builder ====
 +
 +Design pattern-ul Builder este un design pattern creațional,​ cu alte cuvinte, este utilizat pentru a crea și configura obiecte. Un builder este utilizat în mod obișnuit pentru eliminarea supraincarcarilor de constructori multipli și oferă o soluție mai flexibilă la crearea obiectelor complexe.
 +
 +=== Problema ===
 +
 +În Programarea Orientată pe Obiecte, cel mai adesea avem clase care dețin unele date pe care le setăm și le accesăm ulterior. Crearea instanțelor unor astfel de clase ar putea fi uneori cam laborioasă. Să luăm în considerare următoarea clasă de Pizza
 +
 +<code java Pizza.java>​
 +public class Pizza {
 +    private String pizzaSize;
 +    private int cheeseCount;​
 +    private int pepperoniCount;​
 +    private int hamCount;
 +    ​
 +    // constructor,​ getters, setters
 +}
 +</​code>​
 +
 +O clasă foarte simplă la prima vedere însă lucrurile se complică pe masură ce vom crea acest obiect. Oricare pizza va avea o dimensiune, cu toate acestea, atunci când vine vorba de topping-uri,​ unele sau toate pot fi prezente sau deloc, prin urmare, unele dintre proprietățile clasei noastre sunt opționale, iar altele sunt obligatorii.
 +
 +== Supraîncărcarea constructorilor ==
 +
 +Crearea unei instanțe noi //new Pizza("​small",​ 1, 0, 0)// de fiecare dată când vreau să obțin pur și simplu un obiect pizza cu brânză și nimic altceva nu mi se pare o idee bună. Și aici vine prima soluție comună - supraîncărcarea constructorului.
 +
 +<code java Pizza.java>​
 +public class Pizza {
 +    private String pizzaSize; // mandatory
 +    private int cheeseCount;​ // optional
 +    private int pepperoniCount;​ // optional
 +    private int hamCount; // optional
 +    ​
 +    public Pizza(String pizzaSize) {
 +        this(pizzaSize,​ 0, 0, 0);
 +    }
 +    ​
 +    public Pizza(String pizzaSize, int cheeseCount) {
 +        this(pizzaSize,​ cheeseCount,​ 0, 0);
 +    }
 +    ​
 +    public Pizza(String pizzaSize, int cheeseCount,​ int pepperoniCount) {
 +        this(pizzaSize,​ cheeseCount,​ pepperoniCount,​ 0);
 +    }
 +    ​
 +    public Pizza(String pizzaSize, int cheeseCount,​ int pepperoniCount,​ int hamCount) {
 +        this.pizzaSize = pizzaSize;
 +        this.cheeseCount = cheeseCount;​
 +        this.pepperoniCount = pepperoniCount;​
 +        this.hamCount = hamCount;
 +    }
 +    ​
 +    // getters
 +}
 +</​code>​
 +
 +Cu toate acestea, am rezolvat problema doar parțial. Nu putem, de exemplu, să creăm o pizza cu brânză și șuncă, dar fără pepperoni ca aceasta //new Pizza("​small",​ 1, 1)//, deoarece al treilea argument al constructorului este pepperoni. Și aici vine a doua soluție comună - și mai multă supraîncărcare de constructori. ​
 +
 +<code java Pizza.java>​
 +public class Pizza {
 +    private String pizzaSize; // mandatory
 +    private String crust; // mandatory
 +    private int cheeseCount;​ // optional
 +    private int pepperoniCount;​ // optional
 +    private int hamCount; // optional
 +    private int mushroomsCount;​ // optional
 +    ​
 +    public Pizza(String pizzaSize, String crust) {
 +        this(pizzaSize,​ crust, 0, 0, 0, 0);
 +    }
 +    ​
 +    public Pizza(String pizzaSize, String crust, int cheeseCount) {
 +        this(pizzaSize,​ crust, cheeseCount,​ 0, 0, 0);
 +    }
 +    ​
 +    public Pizza(String pizzaSize, String crust, int cheeseCount,​ int pepperoniCount) {
 +        this(pizzaSize,​ crust, cheeseCount,​ pepperoniCount,​ 0, 0);
 +    }
 +    ​
 +    public Pizza(String pizzaSize, String crust, int cheeseCount,​ int pepperoniCount,​ int hamCount) {
 +        this(pizzaSize,​ crust, cheeseCount,​ pepperoniCount,​ hamCount, 0);
 +    }
 +    ​
 +    public Pizza(String pizzaSize, String crust, int cheeseCount,​ int pepperoniCount,​ int hamCount, int mushroomsCount) {
 +        this.pizzaSize = pizzaSize;
 +        this.crust = crust;
 +        this.cheeseCount = cheeseCount;​
 +        this.pepperoniCount = pepperoniCount;​
 +        this.hamCount = hamCount;
 +        this.mushroomsCount = mushroomsCount;​
 +    }
 +    ​
 +    // getters
 +}
 +</​code>​
 +
 +<note tip>​Gândiți-vă ce se va întampla dacă se schimbă ordinea parametrilor. Acest lucru minor va strica funcționalitatea completă a creative unei instanțe de Pizza.</​note>​
 +
 +În concluzie, modelul de constructori supraincarcati funcționează,​ dar este greu de menținut dacă se schimbă funcționalitatea și introducem noi parametri, numărul constructorilor va crește, de asemenea.
 +
 +== Folosirea de getters și setters ==
 +
 +<code java Pizza.java>​
 +Pizza pizza = new Pizza();
 +
 +pizza.setPizzaSize("​small"​);​
 +pizza.setCrust("​thin"​);​
 +pizza.setMushroomsCount(1);​
 +pizza.setCheeseCount(1);​
 +
 +// do something with pizza
 +</​code>​
 +
 +Această soluție nu prezintă niciunul dintre dezavantajele modelului anterior. Este ușor să scalați clasa, mai ușor de instanțiat,​ mai ușor de citit și mai flexibil.
 +Modelul are însă dezavantaje grave. Construcția clasei este împărțită în apeluri multiple, prin urmare instanța poate fi într-o stare parțial construită / invalidă.
 +
 +== Folosirea builder pattern ==
 +
 +<code java Pizza.java>​
 +public class Pizza {
 +    private String pizzaSize;
 +    private String crust;
 +    private int cheeseCount;​
 +    private int pepperoniCount;​
 +    private int hamCount;
 +    private int mushroomsCount;​
 +    ​
 +    public static class Builder {
 +        private String pizzaSize; // mandatory
 +        private String crust; // mandatory
 +        private int cheeseCount = 0; // optional
 +        private int pepperoniCount = 0; // optional
 +        private int hamCount = 0; // optional
 +        private int mushroomsCount = 0; // optional
 +        ​
 +        public Builder(String pizzaSize, String crust) {
 +            this.pizzaSize = pizzaSize;
 +            this.crust = crust;
 +        }
 +        ​
 +        public Builder cheeseCount(int cheeseCount) {
 +            this.cheeseCount = cheeseCount;​
 +            return this;
 +        }
 +        ​
 +        public Builder pepperoniCount(int pepperoniCount) {
 +            this.pepperoniCount = pepperoniCount;​
 +            return this;
 +        }
 +        ​
 +        public Builder hamCount(int hamCount) {
 +            this.hamCount = hamCount;
 +            return this;
 +        }
 +        ​
 +        public Builder mushroomsCount(int mushroomsCount) {
 +            this.mushroomsCount = mushroomsCount;​
 +            return this;
 +        }
 +        ​
 +        public Pizza build() {
 +            return new Pizza(this);​
 +        }
 +    }
 +    ​
 +    private Pizza(Builder builder) {
 +        this.pizzaSize = builder.pizzaSize;​
 +        this.crust = builder.crust;​
 +        this.cheeseCount = builder.cheeseCount;​
 +        this.pepperoniCount = builder.pepperoniCount;​
 +        this.hamCount = builder.hamCount;​
 +        this.mushroomsCount = builder.mushroomsCount;​
 +    }
 +    ​
 +    // getters
 +}
 +</​code>​
 +
 +Am făcut constructorul privat, astfel încât clasa noastră să nu poată fi instanțiată direct. În același timp am adăugat o clasă static Builder cu un constructor care are parametrii noștri obligatori pizzaSize și crust, metode de setare a parametrilor opționali și, în final, o metodă //build()// metoda care va returna o nouă instanță a clasei Pizza. Metodele setter returnează instanța de builder în sine, oferindu-ne astfel o interfață fluentă cu metoda de înlănțuire.
 +
 +<code java Pizza.java>​
 +Pizza pizza = new Pizza.Builder("​large",​ "​thin"​)
 +    .cheeseCount(1)
 +    .pepperoniCount(1)
 +    .build();
 +</​code>​
 +
 +Este mult mai ușor să scrieți și, mai important, să citiți acest cod. La fel ca în cazul constructorului,​ putem verifica parametrii trecuți pentru orice încălcare,​ cel mai adesea în cadrul metodei //build()// sau a metodei setter, și putem arunca IllegalStateException dacă există încălcări înainte de a crea o instanță a clasei.
 +
 +Modelul Builder are unele dezavantaje minore. În primul rând, trebuie să creați un obiect Builder înainte de a crea obiectul clasei în sine. Aceasta ar putea fi o problemă în unele cazuri critice de performanță iar clasa devine puțin mai mare când vine vorba de liniile de cod.
 +
 +În ansamblu, modelul Builder este o tehnică foarte frecvent utilizată pentru crearea obiectelor și este o alegere bună de utilizat atunci când clasele au constructori cu parametri multipli (în special opționali) și este posibil să se schimbe în viitor. Codul devine mult mai ușor de scris și de citit în comparație cu constructorii supraincarcati,​ iar clasa este la fel de bună ca folosirea de getters și setters, dar este mult mai sigură.
 +
 +==== Summary ====
 +
 +Principii de design adresate de aceste patternuri:
 +  * [[https://​stackoverflow.com/​questions/​62539/​what-is-the-dependency-inversion-principle-and-why-is-it-important|Dependency Injection Principle]] - componentele trebuie să depindă de tipuri abstracte, nu de implementări
 +    * Factory respectă acest principiu, componentele depinzând de interfața pentru un tip, nu de un subtip anume
 +  * Separarea codului care se schimbă de cel care rămâne la fel - în cazul Strategy folosim o interfață pentru strategii, depindem de aceea, si putem schimba implementările fără a modifica codul care le folosește ([[https://​www.freecodecamp.org/​news/​the-strategy-pattern-explained-using-java-bc30542204e0/​|exemplu]])
 +  * [[http://​thephantomprogrammer.blogspot.com/​2015/​07/​strive-for-loosely-coupled-design.html|Loosly coupled design]] - în cazul Observer componentele sunt slab legate între ele
 +
 +
 +
 +==== Exerciții ====
 +<note important>​
 +Pentru a încărca soluția, va trebui să accesați link-ul https://​code.devmind.ro/​login,​ să intrați pe tab-ul Contests, unde veți găsi laboratorul grupei voastre.
 +</​note>​
 +
 +<note important>​
 +În cadrul acestui laborator, exercițiile valorează în total 18p. Pentru primirea punctajului maxim pe acest laborator, trebuie să acumulați 10p din rezolvarea exercițiilor,​ orice depășește 10p fiind contorizat ca și bonus.
 +</​note>​
 +
 +
 +**Task 1 - Observer, Strategy, Factory (8p) **
 +
 +**Part 1. (4p) - Strategy, Factory **
 +
 +În cadrul acestui exercițiu, dorim să implementăm un magazin, ce are disponibile mai multe modalități de plată pentru clienții săi:
 +
 +   * folosind un voucher
 +  *  IBAN-ul
 +  *  un card pe care îl posedă
 +
 +Cele 3 modalități de plată sunt reprezentate de clase ce implementează interfața **PaymentStrategy**.
 +
 +Trebuie să implementați:​
 +
 +  * metoda **pay** din cadrul fiecărei metode de plată.
 +  * metoda **getPaymentMethod**,​ ce primește un client ca parametru și instanțiază o tranzacție folosind o anumită metodă de plată, aleasă random.
 +
 +
 +**Part 2. (4p) - Observer **
 +
 +În acest context, **subscriber-ul/​observer-ul** este clasa Person. Această clasă implementează interfața **BalanceObserver**. Clasa Shop, care este **subiectul/​publisher-ul** care trebuie să implementeze interfața **TransactionPublisher** va avea rolul de a **notifica** un client când o tranzacție este efectuată.
 +
 +Trebuie să implementați:​
 +  * metoda **update** din cadrul clasei **Person**, ce preia notificarea faptului că un client are în cont mai puțini Lei decât o anumită limită, specifică fiecărui client în parte, printr-un mesaj adecvat (”$nume$ $prenume$, ramai fara bani, saracule!”);​
 +  * metoda **payBy** din cadrul clasei Person, ce întoarce valoarea de adevăr a reușitei efectuării unei plăți (dacă e posibil, efectuează plata), iar în caz negativ afisează și un mesaj adecvat (”$nume$ $prenume$, pleaca de aici, saracule!”);​
 +  * metoda **createTransaction** din cadrul clasei **Shop**, ce verifică dacă trebuie notificată persoana cu ajutorul metodei **update**, în urma unei tranzacții.
 +
 +
 +
 +**Task 2 - Builder pattern (2p)**
 +
 +
 +a) Examinați câmpurile din skeletul clasei **House** care includ câteva facilități obligatorii în construcția unei case, spre exemplu:
 +
 +   * locația construcției
 +  *    numărul de etaje
 +  *    camere
 +  *   unele **opționale** pe care le poate selecta sau nu clientul, cum ar fi:
 +    * electrocasnice
 +    *  piscină
 +    *  panouri solare
 +    *  securitate
 +
 +
 +Completați constructorul public și metoda toString.
 +
 +b) În clasa **HouseBuilder**,​ completați câmpurile, constructorul și metodele de adăugare a facilităților opționale.
 +
 +c) Finalizați metoda build și decomentați codul din Main pentru a putea testa corectitudinea funcționalității.
 +
 +**Task 3 - Command pattern (8p)**
 +
 +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
 +
 +(4p) Implementați 4 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
 +
 +Implementați pe Invoker metoda execute() care va executa comanda primită ca argument.
 +
 +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.
 +
 +(4p) 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. Metoda reset() de pe Invoker va avea ca scop resetarea tuturor membrilor acestuia.
 +
 +
 +
 + 
 +==== Resurse ====
 +
 +  * [[:​poo-ca-cd:​laboratoare:​old-exercises|Exerciții din alți ani]]
 +
 +==== Referințe ====
 +
 +  * [[http://​geekexplains.blogspot.ro/​2008/​06/​dynamic-binding-vs-static-binding-in.html | Dynamic-binding vs static binding]]
 +  * [[http://​www.javaworld.com/​article/​2077568/​learn-java/​java-tip-67--lazy-instantiation.html | Lazy Instantiation]]
 +  * [[http://​sourcemaking.com/​design_patterns/​observer/​java/​1 | Exemple simple pattern Observer]] ​
 +  * [[http://​sourcemaking.com/​design_patterns/​observer | Explicații pattern Observer]].
 +  * [[https://​stackoverflow.com/​questions/​1710809/​when-and-why-should-the-strategy-pattern-be-used | De ce avem nevoie de Strategy Pattern? ]]
 +  * [[https://​sourcemaking.com/​design_patterns/​command | Command design 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? ]]
 +
 +/* exercitii 2012
 +* (**4p**) Acest exercitiu are ca scop exemplificarea folosirii pattern-ului **Observer**.
 +** (2p) a) O clasa ''​MessageSet''​ tine o lista de mesaje (un text scurt) primite de la utilizator. Atunci cand se adauga un nou mesaj afisati acest eveniment. Decuplati primirea si stocarea mesajului de afisarea evenimentul folosind pattern-ul //​Observer//​. **Hint:** Un obiect //​observator//​ face afisarea evenimentului,​ //​subiectul//​ este ''​MessageSet''​.
 +** (2p) b) Extindeti exercitiul anterior astfel incat atunci cand se primeste un mesaj sa fie afisat in alte limbi. Folositi obiecte //​observator//​ care fac traducerea si afisarea mesajului in cate o alta limba. Pentru a usura testarea (nu este importanta traducerea in sine, ci design-ul aplicatiei) fixati-va cateva (<10) cuvinte de test a caror traducere o mentineti in observatori in structuri de tip [http://​docs.oracle.com/​javase/​6/​docs/​api/​java/​util/​HashMap.html HashMap] si testati pe mesaje continand doar acele cuvinte (daca un cuvant nu e in "​dictionar"​ il afisati netradus).
 +*** clasa ''​MessageSet''​ trebuie sa aiba metode pentru:
 +**** inregistrare observator(i)
 +**** primire mesaj
 +**** notificare observator(i)
 +*** pastrati o //lista de observatori//​ in clasa ''​MessageSet''​
 +*** conform descrierii pattern-ului,​ observatorii extind o clasa abstracta sau interfata ce reprezinta un observator.
 +* (**3p**) In cadrul unei aplicatii de editare imagini pentru a reprezenta actiunile efectuate in timpul editarii folosim obiecte pentru fiecare tip de comanda ce modifica imaginea. De exemplu o comanda pentru '​resize',​ alta pentru '​crop',​ alta pentru pentru aplicarea unui filtru. Pentru a decupla logica construirii acestor obiecte putem folosi pattern-ul **Factory**. Implementati o clasa ''​ImageCommandFactory''​ care creaza obiecte ce extind clasa abstracta ''​ImageCommand''​ (3 tipuri de comenzi, de exemplu ResizeCommand,​ BlurFilterCommad,​ CropCommand). ​
 +** clasa ''​ImageCommand''​ ofera metoda ''​execute()''​ ce afiseaza un mesaj cu explicatia acelei comenzi.
 +** clasele pentru comenzi pot sa aiba atribute diferite in functie de comanda.
 +** unul din motivele pentru care avem nevoie de clase diferite pentru a reprezenta comenzile este ca sa vedem aplicarea lor ca o lista de actiuni diferite si undoable. ​
 +* (**4p**) Extindeti exercitiul anterior astfel incat sa se poata face undo-redo ale acelor comenzi. Folositi pattern-ul** Command**. ​
 +** Modificati comenzile astfel incat sa aiba metodele de ''​undo/​redo''​. Unde faceti intai aceasta modificare? ​
 +** Creati o clasa ''​Image''​ pe care se aplica aceste comenzi. Aceste comenzi doar modifica niste atribute, ca sa "​afisati"​ imaginea trebuie de fapt sa afisati starea atributelor (de exemplu afisati: blurred: yes, size: 100x100 etc).
 +***contine si o metoda care aplica comanda (e.g. action()) care va modifica atributele si eventual va afisa un mesaj informativ ca s-a executat.
 +** Creati o clasa cu rolul de Invoker (comandant) care invoca comenzile
 +*** trebuie sa retina o lista de comenzi, iar cand se da ''​undo/​redo''​ pe imagine se apeleaza ''​undo/​redo''​ la rand pt comenzile aplicate.
 +
 +*/
 +
 +/* exercitii 2011
 +* (**10p**) Utilizati [[:​poo-ca-cd:​Media:​Lab7-sol.zip|rezolvarea]] exercitiului 1 din laboratorul 7, pentru a adauga clasei ''​MyArrayList''​ functionalitatea **undo/​redo** pentru operatiile ''​add''/''​remove''/''​set'',​ utilizand pattern-ul //​Command//​.
 +** Mai intai, adaugati urmatoarele 2 metode in clasa ''​MyArrayList'':​
 +*** ''​add(int,​ float)'',​ ce permite adaugarea unui element intr-o pozitie oarecare
 +*** ''​set(int,​ float)'',​ ce permite stabilirea elementului de la pozitia precizata
 +**: **Hint**: Utilizati ''​checkIndex''​ pentru verificarea pozitiei primite!
 +** Definiti o **interfata**,​ ''​Undoable'',​ ce constituie punctul de plecare pentru stocarea operatiilor efectuate asupra listei. Interfata va expune metodele ''​undo''​ si ''​redo''​ (fara parametri).
 +** Definiti **cate o clasa** ce implementeaza ''​Undoable'',​ pentru fiecare din cele 3 operatii pe lista (''​add''/''​remove''/''​set''​). In clase veti tine informatiile pe care le considerati necesare.
 +** Definiti o clasa, ''​UndoManager'',​ care va gestiona **istoricul** comenzilor executate, respectiv anulate (//​undone//​). **Hints**:
 +*** Puteti folosi o **lista** de obiecte ''​Undoable''​.
 +*** Pentru **deplasarea** eleganta, de-a lungul listei, puteti folosi un ''​[http://​java.sun.com/​javase/​7/​docs/​api/​java/​util/​ListIterator.html ListIterator]'',​ ce permite, in plus fata de un ''​[http://​download.oracle.com/​javase/​6/​docs/​api/​java/​util/​Iterator.html Iterator]''​ clasic, deplasarea inapoi si adaugarea in pozitia curenta a cursorului. Identificati in documentatie metodele ce realizeaza acest lucru.
 +*** Aveti in vedere faptul ca adaugarea unei comenzi la lista, dupa executarea a cel putin unui ''​undo'',​ conduce la **eliminarea** tuturor comenzilor memorate, pana la sfarsitul listei.
 +** Modificati implementarile metodelor din ''​MyArrayList'',​ astfel incat sa **stocati** comenzile realizate, utilizand clasele definite mai sus.
 +** Definiti in clasa ''​MyArrayList''​ metodele ''​undo''​ si ''​redo'',​ cu comportamentul asteptat.
 +<br />
 +*/
 +
 +
 +/*
 +
 +1. (5p) Sa se construiasca in mod dinamic un oras. Fiecare oras are un sediu central de arhitectura ce aproba construirea cladirilor si a spatiilor verzi. Recomandam utilizarea pattern-ului Singleton pentru a asigura unicitatea acestui sediu si clasa de baza institutii din care vor fi mostenite diferitele tipuri de cladiri si spatii verzi. Institutiile prezente in oras pot fi de mai multe tipuri: Muzee, Teatre, Opera, Cinematografe,​ Hoteluri, Facultati, Camine, Parcuri, Gradini Botanice, Gradini Zoo. Aceste institutii vor fi construite in mod aleator folosind pattern-ul factory (vor primi aprobarea sediului central de arhitectura). Orasul va avea maxim 100 de institutii.
 +
 +Afisati o statistica a fiecarui numar de institutii prezente in oras.
 +
 +2. (2p) Sa se extinda exercitiul 1 astfel incat sa apara o notificare de fiecare data cand se construieste o facultate. Pentru acest lucru recomandam folosirea pattern-ului Observer.
 +
 +3. (5p) Presupunem ca primaria are rolul de a comanda constructia anumitor tipuri de cladiri. Implementati o primarie folosind pattern-ul Command si scrieti comenzile pentru construirea unui parc odata ce a fost construita o facultate.
 +*/
poo-ca-cd/arhiva/laboratoare/2024/design-patterns.txt · Last modified: 2025/09/27 10:43 by florian_luis.micu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0