This shows you the differences between two versions of the page.
|
poo:breviare:breviar-11 [2020/12/16 07:20] mihai.nan [Breviar 11] |
poo:breviare:breviar-11 [2025/12/14 17:41] (current) george.tudor1906 |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | |||
| ====== Breviar 11 ====== | ====== Breviar 11 ====== | ||
| + | === Introducere în Design Patterns === | ||
| + | |||
| + | == 1. Ce reprezintă design patterns? == | ||
| + | |||
| + | Design patterns reprezintă soluții generale, reutilizabile, pentru probleme des întâlnite în cadrul software design. | ||
| + | Un design pattern este o schemă a unei soluții pentru o problemă de design (nu reprezintă un algoritm sau o bucată de cod care poate fi aplicată direct), | ||
| + | ce poate ușura munca programatorului și poate duce la simplificarea și eficientizarea arhitecturii unei aplicații. | ||
| + | |||
| + | == 2. Tipuri de design patterns == | ||
| + | |||
| + | Există 3 tipuri de design patterns, în funcție de aspectul și de funcționalitatea obiectelor: | ||
| + | |||
| + | * **Behavioral Patterns**: tratează modul de interacționare între obiecte (Observer, Strategy, Visitor, Command); | ||
| + | * **Creational Patterns**: tratează modul de creare a obiectelor (Factory, Singleton, Builder); | ||
| + | * **Structural Patterns**: tratează modul cum este reprezentat un obiect și cum sunt relațiile între entități (Decorator, Composite, Proxy, Facade, Adapter). | ||
| + | |||
| + | === 1. Creational Patterns === | ||
| + | |||
| + | == 1.1 Singleton Pattern == | ||
| + | |||
| + | === Descriere === | ||
| + | |||
| + | Uneori ne dorim să avem un obiect care să apară doar o singură dată într-o aplicație (de exemplu conducătorul unei țări). | ||
| + | De aceea folosim **Singleton**, un mod prin care restricționăm numărul de instanțieri ale unei clase: clasa are o singură instanță, | ||
| + | care va fi folosită în întreg proiectul. | ||
| + | |||
| + | Pentru a asigura restricționarea: | ||
| + | |||
| + | * constructorul clasei de tip Singleton este **privat** (astfel blocăm instanțierea multiplă din exterior); | ||
| + | * avem un membru **static** și **privat** care reține instanța unică; | ||
| + | * avem o metodă **statică** și **publică** prin care returnăm instanța (se creează dacă nu există deja). | ||
| + | |||
| + | Există două abordări frecvente: | ||
| + | * **lazy instantiation** (instanța se creează la prima cerere); | ||
| + | * **eager instantiation** (instanța se creează imediat, la încărcarea clasei). | ||
| + | |||
| + | <code java> | ||
| + | public class SingletonClass { | ||
| + | /* | ||
| + | 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"); | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | === Avantaje / Dezavantaje === | ||
| + | |||
| + | Un avantaj este accesul ușor la instanța globală, fără a avea nevoie să o transmitem ca parametru sau să o instanțiem manual. | ||
| + | |||
| + | <code java> | ||
| + | public void modifyValue (int x) { | ||
| + | SingletonClass.getInstance().value = x; | ||
| + | // se modifica valoarea lui value din clasa | ||
| + | } | ||
| + | |||
| + | SingletonClass.getInstance().show(); | ||
| + | </code> | ||
| + | |||
| + | <note warning> | ||
| + | Singleton poate fi dezavantajos la testare, deoarece leagă dependențe între clase și îngreunează izolarea acestora. | ||
| + | De asemenea, varianta lazy nu este thread-safe în multithreading; în astfel de situații se preferă eager instantiation (sau alte variante thread-safe). | ||
| + | </note> | ||
| + | |||
| + | <code java> | ||
| + | public class SingletonClass { | ||
| + | private static SingletonClass obj = new SingletonClass(); | ||
| + | private SingletonClass() {} | ||
| + | |||
| + | // eager instantiation - merge la threaduri | ||
| + | public static SingletonClass getInstance() { | ||
| + | return obj; | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | === Utilizări === | ||
| + | |||
| + | Utilizări frecvente: | ||
| + | |||
| + | * înlocuirea variabilelor globale (instanța Singleton este „globală"); | ||
| + | * obiecte care reprezintă resurse partajate (de exemplu logger); | ||
| + | * implementarea de Factory (vezi mai jos). | ||
| + | |||
| + | == 1.2 Factory Pattern == | ||
| + | |||
| + | === Descriere === | ||
| + | |||
| + | Uneori suntem nevoiți să creăm obiecte în funcție de preferința utilizatorului sau de alte necesități. | ||
| + | De aceea folosim **Factory**, prin care alcătuim o familie de clase înrudite (prin moștenirea aceleiași clase abstracte sau implementarea aceleiași interfețe), | ||
| + | iar crearea obiectului concret este delegată către o metodă de tip factory. | ||
| + | |||
| + | {{:poo:breviare:factory_pattern_uml_diagram.jpg?500|}} | ||
| + | |||
| + | === Exemplu (pizzas) === | ||
| + | |||
| + | În exemplul de mai jos, utilizatorul cere un tip de pizza prin nume; dacă tipul există, primește informații despre pizza. | ||
| + | |||
| + | <code java> | ||
| + | interface IPizza { | ||
| + | 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; | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | == 1.3 Builder Pattern == | ||
| + | |||
| + | === Descriere === | ||
| + | |||
| + | Acest pattern este folosit în restaurantele de tip fast food care furnizează meniul pentru copii. | ||
| + | Un meniu pentru copii constă de obicei într-un fel principal, unul secundar, o băutură și o jucărie. | ||
| + | Pot exista variații în ceea ce privește conținutul meniului, dar procesul de creare este același. | ||
| + | Fie că la felul principal se alege un hamburger sau un cheeseburger, procesul va fi același. | ||
| + | Vânzătorul le va indica celor din spate ce să pună pentru fiecare fel de mâncare, pentru băutură și jucărie. | ||
| + | Toate acestea vor fi puse într-o pungă și servite clienților. | ||
| + | |||
| + | {{:poo:breviare:img.jpg?500|}} | ||
| + | |||
| + | Acest șablon realizează **separarea construcției de obiecte complexe de reprezentarea lor** astfel încât | ||
| + | același proces să poată crea diferite reprezentări. Builder-ul creează părți ale obiectului complex | ||
| + | de fiecare dată când este apelat și reține toate stările intermediare. Când produsul este terminat, | ||
| + | clientul primește rezultatul de la builder. | ||
| + | |||
| + | În acest mod, se obține un control mai mare asupra procesului de construcție de noi obiecte. | ||
| + | Spre deosebire de alte pattern-uri din categoria creational, care creau produsele într-un singur pas, | ||
| + | pattern-ul **Builder construiește un produs pas cu pas** la comanda coordonatorului. | ||
| + | |||
| + | === Exemplu (User cu atribute required și optional) === | ||
| + | |||
| + | <code java> | ||
| + | public class User { | ||
| + | private final String firstName; // required | ||
| + | private final String lastName; // required | ||
| + | private final int age; // optional | ||
| + | private final String phone; // optional | ||
| + | private final String address; // optional | ||
| + | |||
| + | private User(UserBuilder builder) { | ||
| + | this.firstName = builder.firstName; | ||
| + | this.lastName = builder.lastName; | ||
| + | this.age = builder.age; | ||
| + | this.phone = builder.phone; | ||
| + | this.address = builder.address; | ||
| + | } | ||
| + | |||
| + | public String getFirstName() { | ||
| + | return firstName; | ||
| + | } | ||
| + | |||
| + | public String getLastName() { | ||
| + | return lastName; | ||
| + | } | ||
| + | |||
| + | public int getAge() { | ||
| + | return age; | ||
| + | } | ||
| + | |||
| + | public String getPhone() { | ||
| + | return phone; | ||
| + | } | ||
| + | |||
| + | public String getAddress() { | ||
| + | return address; | ||
| + | } | ||
| + | |||
| + | public String toString() { | ||
| + | return "User:" + this.firstName + " " + this.lastName + " " + | ||
| + | this.age + " " + this.phone + " " + this.address; | ||
| + | } | ||
| + | |||
| + | public static class UserBuilder { | ||
| + | private final String firstName; | ||
| + | private final String lastName; | ||
| + | private int age; | ||
| + | private String phone; | ||
| + | private String address; | ||
| + | |||
| + | public UserBuilder(String firstName, String lastName) { | ||
| + | this.firstName = firstName; | ||
| + | this.lastName = lastName; | ||
| + | } | ||
| + | |||
| + | public UserBuilder age(int age) { | ||
| + | this.age = age; | ||
| + | return this; | ||
| + | } | ||
| + | |||
| + | public UserBuilder phone(String phone) { | ||
| + | this.phone = phone; | ||
| + | return this; | ||
| + | } | ||
| + | |||
| + | public UserBuilder address(String address) { | ||
| + | this.address = address; | ||
| + | return this; | ||
| + | } | ||
| + | |||
| + | public User build() { | ||
| + | return new User(this); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | public static void main(String[] args) { | ||
| + | User user1 = new User.UserBuilder("Lokesh", "Gupta") | ||
| + | .age(30) | ||
| + | .phone("1234567") | ||
| + | .address("Fake address 1234") | ||
| + | .build(); | ||
| + | |||
| + | User user2 = new User.UserBuilder("Jack", "Reacher") | ||
| + | .age(40) | ||
| + | .phone("5655") | ||
| + | //no address | ||
| + | .build(); | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | == 1.4 Singleton Factory == | ||
| + | |||
| + | O clasă de tip Factory poate fi utilizată în mai multe locuri în cadrul unui proiect. Pentru a economisi resurse, | ||
| + | putem folosi pattern-ul **Singleton** pentru Factory, astfel încât să existe o singură instanță a clasei Factory. | ||
| + | |||
| + | |||
| + | === 2. Behavioral Patterns === | ||
| + | |||
| + | == 2.1 Observer Pattern == | ||
| + | |||
| + | === Descriere === | ||
| + | |||
| + | Acest design pattern stabilește o relație **one-to-many** între obiecte. | ||
| + | Avem un obiect numit **subiect**, căruia îi este asociată o colecție (listă) de **observatori**. | ||
| + | Observatorii sunt obiecte dependente de subiect și sunt notificate automat de către subiect atunci când în subiect are loc o acțiune | ||
| + | sau o modificare a stării. | ||
| + | |||
| + | {{:poo:breviare:observer_breviar.png?500|}} | ||
| + | |||
| + | == 2.2 Strategy Pattern == | ||
| + | |||
| + | === Descriere === | ||
| + | |||
| + | **Strategy** este un design pattern behavioral ce oferă o familie de algoritmi (strategii), | ||
| + | încapsulate în clase care oferă o interfață comună de folosire. | ||
| + | Clientul (utilizatorul) poate alege dinamic strategia care va fi folosită. | ||
| + | |||
| + | {{:poo:breviare:strategy_breviar.png?500|}} | ||
| + | |||
| + | Exemplu de motivare: la căutarea unui element într-o colecție, putem alege algoritmul în funcție de proprietăți ale colecției | ||
| + | (de exemplu, dacă este sortată: căutare binară; altfel: iterare liniară). | ||
| + | |||
| + | == 2.3 Visitor Pattern == | ||
| + | |||
| + | === Descriere === | ||
| + | |||
| + | Acest design pattern oferă posibilitatea de a separa un algoritm de structura de date pe care acesta operează, | ||
| + | astfel încât să putem adăuga ușor funcții noi care operează peste o familie de clase fără să modificăm structura acestora. | ||
| + | |||
| + | Pe scurt, folosim **Visitor** dacă avem tipuri diferite și dorim să adăugăm/schimbăm operații fără să modificăm clasele. | ||
| + | |||
| + | {{:poo:breviare:visitor.jpg?500|}} | ||
| + | |||
| + | În cadrul pattern-ului: | ||
| + | |||
| + | * avem o interfață **Visitor**, care reprezintă operația aplicată; | ||
| + | * avem o interfață/clasă abstractă **Visitable** (numită în unele scheme **Element**), care reprezintă obiectele peste care se aplică operațiile; | ||
| + | * Visitor are metode de forma `visit(...)`; | ||
| + | * Visitable are metoda `accept(Visitor v)`. | ||
| + | |||
| + | === Exemplu (Ls/Cat peste Repository: Director/Fisier) === | ||
| + | |||
| + | <code java> | ||
| + | interface Visitor { | ||
| + | void visit (Director f); | ||
| + | void visit (Fisier f); | ||
| + | } | ||
| + | |||
| + | class Ls implements Visitor { | ||
| + | public void visit (Director f) { | ||
| + | System.out.println(f.getName()); | ||
| + | for (Repository repo: f.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 | ||
| + | } | ||
| + | } | ||
| - | <HTML> | + | abstract class Repository { |
| - | <iframe src="https://docs.google.com/file/d/1Y2H7korhd2qB0r49upMLef3mhTz2AVy-/preview" width="640" height="720"></iframe> | + | private String name; |
| - | </HTML> | + | // 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); | ||
| + | } | ||
| + | } | ||
| + | </code> | ||