Differences

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

Link to this comparison view

poo:breviare:breviar-12 [2018/12/10 22:31]
carmen.odubasteanu
poo:breviare:breviar-12 [2026/01/12 08:16] (current)
george.tudor1906 [2. Decorator]
Line 1: Line 1:
  
-====== ​Introducere in Design Patterns ​======+===== Breviar 12 ===== 
 +==== 1. Genericitate ​====
  
 +=== 1.1 Introducere ===
  
-Ce reprezinta design patterns? Ele reprezinta niste solutii generalecare pot fi reutilizate,​ +Tipurile generice simplifică lucrul cu colecțiipermițând tipizarea elementelor acestora
-ale unor probleme des intalnite in cadrul software design+Definirea unui tip generic se realizează prin specificarea între paranteze unghiulare 
-In principiu, un design pattern este de fapt o schema ​unei solutii pentru o problema ​de design  +unui tip de date Javaefectul fiind impunerea tipului respectiv pentru toate 
-(nu reprezinta un algoritm sau o bucata de cod ce poate fi aplicata direct intr-un cod-sursa),  +elementele colecției.
-care poate usura munca unui programator si care poate duce la simplificarea si eficientizarea  +
-arhitecturii unei aplicatii.+
  
-Exista 3 tipuri ​de design patternsin functie ​de aspectul si de functionalitatea obiectelor:+<note important>​ 
 +În cazul folosirii tipurilor generice, încercarea ​de a utiliza în cadrul unei 
 +colecții a unui element necorespunzător ca tip va produce o eroare la compilare, 
 +spre deosebire ​de varianta în care tipurile generice nu sunt utilizate, ce permite 
 +doar aruncarea unor excepții ​de tipul ClassCastException. 
 +</​note>​
  
-1) Behavioral Patterns: se trateaza modul de interactionare intre obiecte (Observer, Strategy, Visitor, Command)+=== 1.2 Definirea structurilor generice ===
  
-2) Creational Patterns: se trateaza modul de creare a obiectelor (FactorySingleton)+<code java> 
 +class Association<​KV> { 
 +    private K key; 
 +    private V value;
  
-3) Structural Patterns: se trateaza modul cum este reprezentat un obiect, cum sunt relatiile intre entitati ​(Decorator, Composite, Proxy, FacadeAdapter)+    public Association(K keyV value
 +        this.key = key; 
 +        this.value = value; 
 +    }
  
-====== Creational Patterns ======+    public K getKey() { 
 +        return this.key; 
 +    }
  
-=====     ​Singleton Pattern =====+    ​public V getValue() { 
 +        return this.value;​ 
 +    } 
 +
 +</​code>​
  
-==== Descriere ====+Sintaxa **<K, V>** este folosită pentru a defini **tipuri formale** în cadrul definiției 
 +clasei. Aceste tipuri pot fi folosite în mod asemănător cu tipurile uzuale. În momentul 
 +în care invocăm efectiv o structură generică, ele vor fi înlocuite cu **tipurile efective**.
  
 +<code java>
 +class Test {
 +    public static void main(String args[]) {
 +        Association<​String,​ String> map1;
 +        map1 = new Association<​String,​ String>​("​CC",​ "​POO"​);​
 +        String s1 = map1.getKey();​
  
-Uneori ne dorim sa avem un obiect care sa apara doar o singura data intr-o aplicatiede exemplu conducatorul unei tari (deoarece nu are sens sa existe mai multi in acelasi context). De aceea folosim Singleton, un mod prin care restrictionam numarul de instantieri a unei clase, ea avand o singura referinta ce va fi folosita in intreg proiectul. Astfel, pentru a asigura restrictionarea,​ clasa de tip Singleton va avea un constructor privat  +        Association<​StringInteger> map2; 
-(il facem privat prin a bloca instantierea multipla a clasei), care va fi apelat in interiorul claseiPentru a putea crea o instanta unica a clasei ​(prin care se asigura o economisire a memoriei), vom avea nevoie de un membru static si privat, care va avea acelasi tip cu clasa pe care vrem o sa instantiem, si o metoda statica si publica prin care este returnata instanta clasei (se va crea una noua in caz ca nu exista deja una - lazy instantiation - una dintre cele 2 variante de instantiere Singleton, cealalta fiind eager instantiation). ​+        map2 = new Association<​String,​ Integer>("​POO",​ 2015)
 +        String s2 = map2.getKey()
 +        int nr = map2.getValue()
 +    } 
 +
 +</​code>​
  
-            public class SingletonClass { +=== 1.2.1 Genericitatea în subtipuri ​===
-                /*   +
-                la inceput, inainte de prima si singura instantiere a clasei SingletonClass +
-                va avea valoarea null +
-                */ +
-                private static SingletonClass obj null; +
-                public int value 10; +
-                // lasam constructorul clasei privat pentru a nu fi accesat din exterior +
-                private SingletonClass() { +
-                    // do stuff +
-                    System.out.println("​Instantiam!"​);​ +
-                } +
-                // metoda prin care se creaza unica instanta a clasei +
-                // lazy instantiation +
-                public static SingletonClass getInstance() { +
-                    // daca clasa nu a fost instantiata inainte, o facem acum +
-                    if (obj == null) +
-                        obj new SingletonClass();​  +
-                    return obj; +
-                } +
-                public void show() { +
-                    System.out.println("​Singleton is magic"​);​ +
-                } +
-            }+
  
-Un avantaj il reprezinta faptul ca putem accesa metodele clasei de tip Singleton mai usor, neavand nevoie de instantiere sau sa dam un obiect care reprezinta instanta clasei ca parametru la o metoda (instanta clasei fiind un obiect vizibil la nivel global in cadrul unui proiect). +<code java> 
-             +class Test { 
-            public void modifyValue ​(int x) { +    public ​static ​void main(String args[]) { 
-                    ​SingletonClass.getInstance().value = x;  +        ​Association<​String,​ Integer> map1; 
-                    // se modifica valoarea lui value din clasa +        // Operația 1 
-            +        map1 = new Association<​String,​ Integer>("​POO",​ 2015); 
-             +        ​Association<​Object,​ Object> map2; 
-            ​SingletonClass.getInstance().show();​+        ​// Operația 2 
 +        map2 = map1;  // EROARE! 
 +    ​
 +} 
 +</​code>​
  
-Folosirea Singleton ​este dezavantajoasa in testare deoarece leaga dependente intre claseingreunand testarea acestoraUn alt dezavantaj il reprezinta folosirea acestuia in threaduri, nu este recomandat sa fie folosita varianta cu lazy instantiation in multithreading deoarece nu e thread-safe,​ in acest caz fiind recomandata folosirea variantei cu eager instantiation (mai multe despre multithreading veti afla la Algoritmi Paraleli si Distribuiti,​ in anul 3).+<note important>​ 
 +Dacă CType este un subtip (clasă descendentă sau subinterfață) al lui PType, 
 +atunci o structură generică GenericStructure<​CType>​ NU este un subtip al lui 
 +GenericStructure<​PType>​. 
 +Atenție la acest conceptîntrucât el nu este intuitiv! 
 +</​note>​
  
-            public class SingletonClass +=== 1.2.2 Restricționarea parametrilor ​===
-            { +
-                private static SingletonClass obj new SingletonClass(); ​           +
-                private Singleton() {} +
-                // eager instantiation - merge la threaduri +
-                public static SingletonClass getInstance() +
-                { +
-                    return obj; +
-                } +
-            } +
-         +
-==== Utilizari ====+
  
-Utilizarile acestui pattern sunt: +Există cazuri în care dorim să adăugăm într-o structură ​de date generică elemente 
-inlocuirea ​de variabile globale (un obiect al instantei clasei de tip Singleton este global) +care au un tip cu anumite proprietăți.
-- obiecte ce reprezinta surse partajate sau care sunt de tip logger, obiectele fiind accesate din mai multe locuri ale aplicatiei  +
-- implementarea de Factory (o sa vorbim mai jos despre acest subiect)+
  
-===== Factory =====+<code java> 
 +abstract class AVector<​E extends Number> extends AbstractList<​E>​ { 
 +    abstract public boolean add(E obj); 
 +    abstract public E get(int index); 
 +    abstract public Enumeration<​E>​ elements();​ 
 +    abstract public Iterator<​E>​ iterator();​ 
 +    abstract public ListIterator<​E>​ listIterator();​ 
 +
 +</​code>​
  
-==== Descriere ====+Sintaxa **<E extends Number>​** indică faptul că tipul E este o subclasă a lui Number 
 +(sau chiar Number). Această restricție face imposibilă instanțierea unui obiect care 
 +să conțină elemente de tip String, deoarece String nu este o subclasă a lui Number.
  
-Uneori suntem nevoiti sa cream obiecte in functiile de preferinta unui utilizator in cadrul unei aplicatii sau dupa alta necesitati.  +Cuvântul cheie **extends** este folosit și în cazul în care dorim să indicăm ​un tip 
-De aceea, folosim pattern-ul Factory, prin care alcatuim o familie de clase care sunt inrudite intre ele, prin faptul ca ele mostenesc aceeasi clasa abstracta sau ca implementeaza aceeasi interfata. Astfel, prin exemplul de cod ilustrat mai jos, daca utilizatorul doreste sa afle informatii despre ​un tip de pizza, el va scrie la tastatura numele acelui tip de pizza si, daca tipul respectiv exista, el va primi informatii despre acel tip de pizza.+ce implementează o anumită interfață:​
  
-{{ :​poo:​breviare:​factory_pattern_uml_diagram.jpg |}} +<code java> 
-            interface IPizza { +class Map<​K ​extends ​Number, V extends Set<​K>> ​extends ​Association<​KV> 
-                void showStuff();​ +    public ​Map(K keyV value) { 
-            } +        super(keyvalue); 
-            /* +    }
-             nu este neaparat sa avem o clasa abstracta ce implementeaza o interfata +
-             putem avea pur si simplu o clasa abstracta (fara sa implementeze o interfata)  +
-             care e extinsa de clasele normale sau o interfata ce e implementata direct de  +
-             ​clasele normale din Factory +
-            */ +
-            abstract class Pizza implements IPizza { +
-                public abstract void showStuff();​ +
-            } +
-            class PizzaMargherita ​extends ​Pizza { +
-                public void showStuff() { +
-                    System.out.println("​Sos tomat si branza Mozzarella."​);​ +
-                } +
-            } +
-            class PizzaQuattroStagioni ​extends ​Pizza { +
-                public void showStuff() { +
-                    System.out.println("​Sos tomatbranza Mozzarella, sunca, pepperoni,​ +
-                       ​ciuperci,​ ardei. "); +
-                } +
-            } +
-            class PizzaPepperoni extends Pizza +
-                public ​void showStuff() { +
-                    System.out.println("​Sos tomatbranza Mozzarella, dublu pepperoni."​)+
-                } +
-            } +
-           class PizzaHawaii extends Pizza +
-                ​public void showStuff() { +
-                    System.out.println("​Sos tomat, branza Mozzarella, suncadublu ananas."​); +
-                } +
-            } +
-            class PizzaFactory { +
-                public static Pizza factory (String pizzaName) { +
-                    if (pizzaName.equals("​Margherita"​)) +
-                        return new PizzaMargherita();​ +
-                    if (pizzaName.equals("​Hawaii"​)) +
-                        return new PizzaHawaii();​ +
-                    if (pizzaName.equals("​Quattro Stagioni"​)) +
-                        return new PizzaQuattroStagioni();​ +
-                    if (pizzaName.equals("​Pepperoni"​)) +
-                        return new PizzaPepperoni();​ +
-                    return null; +
-                } +
-            } +
-====         ​Singleton Factory ====+
  
 +    public static void main(String args[]) {
 +        Map<​Double,​ TreeSet<​Double>>​ obj;
 +        TreeSet<​Double>​ v = new TreeSet<​Double>​();​
 +        v.add(new Double(2.5));​
 +        obj = new Map<​Double,​ TreeSet<​Double>>​(1.2,​ v);
 +        Double x = obj.getKey();​
 +        System.out.println(x);​
 +        TreeSet<​Double>​ set = obj.getValue();​
 +        System.out.println(set);​
 +    }
 +}
 +</​code>​
  
-Un clasa de tip Factory poate fi utilizata in mai multe locuri in cadrul unui proiect si pentru a economisi resurse putem folosi pattern-ul Singleton, existand o singura instanta a clasei Factory.+=== 1.3 Wildcards ===
  
-=====  Behavioral Patterns =====+Wildcard-urile sunt utilizate atunci când dorim să întrebuințăm o structură generică 
 +drept parametru într-o funcție și nu dorim să limităm tipul de date din colecția 
 +respectivă.
  
 +<code java>
 +// Fără wildcard - restrictiv (doar Collection<​Object>​)
 +void boolean containsAll(Collection<​Object>​ c) {
 +    for (Object e : c) {
 +        if (!this.contains(e)) {
 +            return false;
 +        }
 +    }
 +    return true;
 +}
  
-====     ​Observer Pattern ====+// Cu wildcard - flexibil (orice Collection) 
 +void boolean containsAll(Collection<?>​ c) { 
 +    for (Object e : c) { 
 +        if (!this.contains(e)) { 
 +            return false; 
 +        } 
 +    } 
 +    return true; 
 +
 +</​code>​
  
-=== Descriere ===+<note important>​ 
 +O limitare care intervine este că NU putem adăuga elemente arbitrare într-o 
 +colecție cu wildcard-uri. 
 +</​note>​
  
-Acest design pattern stabileste o relatie one to many intre obiecte. Mai exact, avem un obiect pe care il numim subiect, caruia ii este asociat o colectie, o lista de observatori,​ care sunt obiecte dependente de subiect, notificate in mod automat de catre subiect, cand in cadrul subiectului are loc o actiune sau o modificare a starii subiectului. +<code java> 
-            // insert image observer.jpg+class TestClass { 
 +    public static void main(String args[]) { 
 +        ​// Operație permisă 
 +        Collection<?>​ c = new LinkedList<​Integer>​();​ 
 +        // Eroare la compilare! 
 +        c.add(new Object());​ 
 +    } 
 +
 +</​code>​
  
 +Singurul element care poate fi adăugat este **null**, întrucât acesta este membru
 +al oricărui tip referință. Operațiile de tip **getter** sunt posibile, întrucât
 +rezultatul acestora poate fi mereu interpretat drept Object.
  
-==== Strategy Pattern ====+=== 1.4 Bounded Wildcards ​===
  
 +Mecanismul bazat pe **Bounded Wildcards** permite introducerea unor restricții
 +asupra tipurilor ce pot înlocui un wildcard.
  
-=== Descriere ===+  * **<? extends Number>​** - tipul trebuie să fie Number sau o subclasă a acesteia 
 +  * **<? super AbstractMethodError>​** - tipul trebuie să fie AbstractMethodError sau o superclasă
  
-Strategy reprezinta un design pattern behavioural ce ofera o familie de algoritmi, adica de strategii, incapsulate in clase ce ofera o interfata de folosire ​(in cazul exemplului de mai jos - intefata Strategy), din care clientii / utilizatorii pot sa aleagaAcest design pattern este recomandat daca este nevoie de un tip de strategie / algoritm cu mai multe implementari posibile si se doreste sa se aleaga in mod dinamic un algoritm pentru a-l folosiDe exemplu, daca ne dorim sa cautam un element intr-o colectie putem sa vedem mai intai daca este sortata colectia sau nu, ca sa vedem de algoritm de cautare folosim. Daca este colectia sortata, putem aplica algoritmul de cautare binara in colectie, altfel putem sa facem o simpla iterare prin colectie ca sa vedem cautam elementul dorit+<code java> 
-            // insert image strategy.png+class TestClass { 
 +    public static void printType(Set<? extends Number> set
 +        for (Number item : set) { 
 +            System.out.println(item.getClass());​ 
 +        } 
 +    }
  
-==== Visitor Pattern ====+    public static void main(String args[]) { 
 +        Set<​Number>​ set new HashSet<​Number>​();​ 
 +        set.add(new Integer(5));​ 
 +        set.add(new Double(7.2));​ 
 +        set.add(new Float(10.5));​ 
 +        TestClass.printType(set);​ 
 +    } 
 +
 +</​code>​
  
 +=== 1.5 Metode generice ===
  
-=== Descriere ===+Java ne oferă posibilitatea scrierii de metode generice (având un tip-parametru) 
 +pentru a facilita prelucrarea unor structuri generice.
  
-Acest design pattern ofera posibilitatea de separa un algoritm de structura de date pe care acesta opereazaastfel noua ne este usor sa adaugam functii noi care opereaza peste familie de clase, fara sa modificam structura acestora. Pe scurt, folosim Visitor daca avem tipuri diferite si dorim sa adaugam sau sa schimbam operatii fara sa modificam clasele.+<code java> 
 +class TestClass { 
 +    // Metoda 1 - CORECTĂ 
 +    public <T> void metoda1(T[] ​a, Collection<​T>​ c) { 
 +        for (T : a) { 
 +            // Operație permisă 
 +            c.add(o); 
 +        } 
 +    }
  
-In cadrul acestui design pattern, avem o interfata Visitor, care reprezinta operatia aplicata, si o interfata ​clasa abstracta Visitable ​(numita in imagine Element)care reprezinta obiectele pe care se aplicatii operatiile din clasele de tip Visitor. In clasele Visitor, vom avea metoda visit, prin care Visitorul "​viziteaza"​ un obiect tinta, care este reprezentat ca fiind de tip Visitable, o clasa de tip Visitable continand o metoda accept, care accepta "​vizita"​ Visitor-ului asupra sa. +    ​// Metoda 2 - EROARE la compilare 
-            // insert image visitor.jpg+    public void metoda2(Object[] aCollection<?>​ c) { 
 +        for (Object ​: a) { 
 +            // Eroare la compilare! 
 +            c.add(o); 
 +        } 
 +    } 
 +
 +</​code>​
  
-Un exemplu de Visitor este implementarea de operatii pentru fisiere precum cat si ls. In cadrul acestui exemplu, vom avea ca Visitori clasele Ls si Cat, iar ca Visitable vom avea clasele Fisier si Folder, care mostenesc o clasa abstracta numita Repository. Vom exemplifica mai jos, pentru o mai buna intelegere:+Exemplu cu bounded wildcards în metode generice:
  
-package laborator.pkg12;+<code java> 
 +class TestClass { 
 +    List<​Number>​ dest;
  
-import java.util.*;+    public <T> void copy(Set<​T>​ dest, List<? extends T> src) { 
 +        for (T o : src) { 
 +            dest.add(o); 
 +        } 
 +    }
  
-            interface Visitor { +    ​public ​<T extends Number> boolean addAll(Collection<​T>​ c) { 
-                void visit (Director f); +        for (T o c) { 
-                void visit (Fisier f); +            dest.add(o); 
-            } +        
-            class Ls implements Visitor { +        return ​true
-                ​public ​void visit (Director f) { +    
-                    ​System.out.println(f.getName());​ +
-                    ​for (Repository repof.getChildren()) { +</code>
-                        ​System.out.println("​\t"​ + repo.getName());  +
-                        // afisam numele unui repo (fisier / folder) +
-                    ​}  +
-                } +
-                public void visit (Fisier f) { +
-                    System.out.println("​Not a folder"​);​ +
-                    /* comanda Ls (in acest exemplu) este specifica doar folderelor,  +
-                    in acest caz este evidentiat un dezavantaj al  Visitor-ului,​  +
-                    faptul ca noi trebuie sa implementam metode de care nu avem nevoie  +
-                    in acest caz - se incalca Interface Segregation Principle ​ */ +
-                } +
-            } +
-            class Cat implements Visitor { +
-                public void visit (Director f) { +
-                    // avertisment ca avem folder, nu fisier +
-                } +
-                public void visit (Fisier f) { +
-                    // citire fisier, folosind numele fisierului +
-                } +
-            } +
-            abstract class Repository { +
-                private String name; +
-                 // numele unui fisier sau folder (de fapt, calea acestuia) +
-                public String getName() { +
-                    ​return ​name+
-                +
-                ​public abstract void accept (Visitor f); +
-            ​+
-            class Fisier extends Repository { +
-                public void accept (Visitor f) { +
-                    f.visit(this);​  +
-                    ​// Visitor-ul "​viziteaza"​ fisierul, adica acesta  +
-                    //​efectueaza o operatie asupra fisierului +
-                } +
-            } +
-            class Director extends Repository { +
-                private List<​Repositorychildren = new ArrayList<>​(); ​             +
-                public List<​Repository>​ getChildren() { +
-                    return children; +
-                } +
-                public void accept (Visitor f) { +
-                    f.visit(this);​ +
-                } +
-            }+
  
 +==== 2. Decorator ====
 +
 +=== 2.1 Introducere ===
 +
 +De multe ori în cadrul unei aplicații apare următorul scenariu: aveți de implementat
 +o anumită funcționalitate care trebuie să poată fi extinsă fără să știți în momentul
 +proiectării în ce mod va fi extinsă.
 +
 +**Exemplu:​** Lucrați la un joc de tipul shooter. Aveți o listă de arme pe care un user
 +le poate achiziționa și utiliza. La un moment dat observați că încasările din joc încep
 +să scadă odată ce alternativele de pe piață oferă mai multe feature-uri pentru armele lor.
 +
 +**Rezolvarea:​** Creați o clasă abstractă wrapper peste o instanță de armă. Dacă de exemplu
 +vreți ca toate armele voastre să poată fi mortale dar silențioase,​ creați clasa
 +WeaponSilencer ce implementează interfața de bază a armelor și are un obiect de tip
 +armă intern. În metoda fire, în decorator, setați eventual sunetul jocului mai încet.
 +
 +{{:​poo:​breviare:​img1.jpg?​600|}}
 +
 +=== 2.2 Exemplu cu fluxuri Java ===
 +
 +<code java>
 +CipherOutputStream cos = new CipherOutputStream(new FileOutputStream("​file"​),​ c);
 +PrintWriter pw = new PrintWriter(new OutputStreamWriter(cos));​
 +pw.println("​Stand and unfold yourself"​);​
 +pw.close();
 +</​code>​
 +
 +  * La bază avem un flux de ieșire care va scrie în fișierul "​file"​ niște octeți
 +  * **CipherOutputStream** este o clasă decorator și adaugă peste fluxul de octeți proprietatea că acesta este criptat
 +  * **OutputStreamWriter** și **PrintWriter** sunt decoratoare care ne permit să adăugăm text în fluxul de ieșire
 +
 +=== 2.3 Structura Decorator ===
 +
 +{{:​poo:​breviare:​img2.jpg?​600|}}
 +
 +**Participanți:​**
 +  * **Component** - definește interfața pentru obiectele care pot avea responsabilități adăugate dinamic (metoda Operation())
 +  * **ConcreteComponent** - definește un obiect căruia i se pot adăuga responsabilități suplimentare
 +  * **Decorator** - menține o referință către un obiect Component și definește o interfață conformă cu interfața lui Component (delegă apelul către component->​Operation())
 +  * **ConcreteDecoratorA** - adaugă stare suplimentară (addedState) componentei
 +  * **ConcreteDecoratorB** - adaugă comportament suplimentar (AddedBehavior()) și apelează Decorator::​Operation()
 +
 +<note important>​
 +Atenție: Dacă în tot lanțul de decorări nu există un decorator care să aibă la bază
 +o componentă concretă, construcția nu va funcționa.
 +</​note>​
  
-{{:​poo:​breviare:​lab12_var_final.pdf|}} 
poo/breviare/breviar-12.1544473887.txt.gz · Last modified: 2018/12/10 22:31 by carmen.odubasteanu
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