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:38]
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 ​familie de clase care sunt inrudite intre ele, prin faptul ca ele mostenesc aceeasi clasa abstracta sau ca implementeaza aceeasi interfata.  +ce implementează ​anumită interfață:
-{{ :​poo:​breviare:factory_pattern_uml_diagram.jpg | Diagrama exemplu Factory}}+
  
-Astfelprin exemplul de cod ilustrat mai josdaca utilizatorul doreste sa afle informatii despre un tip de pizzael va scrie la tastatura numele acelui tip de pizza sidaca tipul respectiv exista, el va primi informatii despre acel tip de pizza.+<code java> 
 +class Map<K extends NumberV extends Set<​K>>​ extends Association<​KV> { 
 +    public Map(K keyV value) { 
 +        super(keyvalue); 
 +    }
  
 +    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>​
  
-            interface IPizza { +=== 1.3 Wildcards ​===
-                void showStuff();​ +
-            } +
-            /* +
-             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 tomat, branza Mozzarella, sunca, pepperoni,​ +
-                       ​ciuperci,​ ardei. "); +
-                } +
-            } +
-            class PizzaPepperoni extends Pizza { +
-                public void showStuff() { +
-                    System.out.println("​Sos tomat, branza Mozzarella, dublu pepperoni."​);​ +
-                } +
-            } +
-           class PizzaHawaii extends Pizza { +
-                public void showStuff() { +
-                    System.out.println("​Sos tomat, branza Mozzarella, sunca, dublu 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 ====+
  
 +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ă.
  
-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.+<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; 
 +}
  
-=====  Behavioral Patterns =====+// Cu wildcard - flexibil (orice Collection) 
 +void boolean containsAll(Collection<?>​ c) { 
 +    for (Object e : c) { 
 +        if (!this.contains(e)) { 
 +            return false; 
 +        } 
 +    } 
 +    return true; 
 +
 +</​code>​
  
 +<note important>​
 +O limitare care intervine este că NU putem adăuga elemente arbitrare într-o
 +colecție cu wildcard-uri.
 +</​note>​
  
-====     ​Observer Pattern ====+<code java> 
 +class TestClass { 
 +    public static void main(String args[]) { 
 +        // Operație permisă 
 +        Collection<?>​ c new LinkedList<​Integer>​();​ 
 +        // Eroare la compilare! 
 +        c.add(new Object());​ 
 +    } 
 +
 +</​code>​
  
-=== Descriere ===+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.
  
-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.+=== 1.4 Bounded Wildcards ===
  
-{{ :​poo:​breviare:​observer.jpg?300 | Diagrama Observer}}+Mecanismul bazat pe **Bounded Wildcards** permite introducerea unor restricții 
 +asupra tipurilor ce pot înlocui un wildcard.
  
-==== Strategy Pattern ====+  * **<? extends Number>​** - tipul trebuie să fie Number sau o subclasă a acesteia 
 +  * **<? super AbstractMethodError>​** - tipul trebuie să fie AbstractMethodError sau o superclasă
  
 +<code java>
 +class TestClass {
 +    public static void printType(Set<?​ extends Number> set) {
 +        for (Number item : set) {
 +            System.out.println(item.getClass());​
 +        }
 +    }
  
-=== Descriere ===+    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>​
  
-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 aleaga. Acest 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 folosi. De 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.+=== 1.5 Metode generice ===
  
-{{ :​poo:​breviare:​strategy.png?100 | Diagrama Strategy }}+Java ne oferă posibilitatea scrierii de metode generice (având un tip-parametru) 
 +pentru a facilita prelucrarea unor structuri generice.
  
-==== Visitor Pattern ====+<code java> 
 +class TestClass { 
 +    // Metoda 1 - CORECTĂ 
 +    public <T> void metoda1(T[] a, Collection<​T>​ c) { 
 +        for (T o : a) { 
 +            // Operație permisă 
 +            c.add(o); 
 +        } 
 +    }
  
 +    // Metoda 2 - EROARE la compilare
 +    public void metoda2(Object[] a, Collection<?>​ c) {
 +        for (Object o : a) {
 +            // Eroare la compilare!
 +            c.add(o);
 +        }
 +    }
 +}
 +</​code>​
  
-=== Descriere ===+Exemplu cu bounded wildcards în metode generice:
  
-Acest design pattern ofera posibilitatea de a separa un algoritm de structura de date pe care acesta opereaza, astfel noua ne este usor sa adaugam functii noi care opereaza peste o 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 { 
 +    List<​Number>​ dest;
  
-In cadrul acestui design patternavem 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 VisitorIn 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.+    public <T> void copy(Set<​T>​ destList<? extends T> src) { 
 +        for (T : src
 +            dest.add(o); 
 +        } 
 +    }
  
-{{ :​poo:​breviare:​visitor.jpg?300 | Diagrama Visitor}}            // insert image visitor.jpg+    public <T extends Number> boolean addAll(Collection<​T>​ c) { 
 +        for (T o : c) { 
 +            dest.add(o); 
 +        ​} 
 +        return true; 
 +    ​} 
 +
 +</code>
  
-Un exemplu de Visitor este implementarea de operatii pentru fisiere precum cat si lsIn 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:+==== 2Decorator ====
  
-package laborator.pkg12;+=== 2.1 Introducere ===
  
-import java.util.*;+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ă.
  
-            interface Visitor { +**Exemplu:** Lucrați la un joc de tipul shooter. Aveți o listă de arme pe care un user 
-                void visit (Director f); +le poate achiziționa și utiliza. La un moment dat observați că încasările din joc încep 
-                void visit (Fisier f); +să scadă odată ce alternativele ​de pe piață oferă mai multe feature-uri pentru armele lor.
-            } +
-            class Ls implements Visitor { +
-                public void visit (Director f) { +
-                    System.out.println(f.getName());​ +
-                    for (Repository repof.getChildren()) { +
-                        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<​Repository>​ children = new ArrayList<>​(); ​             +
-                public List<​Repository>​ getChildren() { +
-                    return children; +
-                } +
-                public void accept (Visitor f) { +
-                    f.visit(this);​ +
-                } +
-            }+
  
 +**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.1544474285.txt.gz · Last modified: 2018/12/10 22:38 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