This is an old revision of the document!


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);
  • 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).
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");
    }
}

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.

public void modifyValue (int x) {
    SingletonClass.getInstance().value = x;
    // se modifica valoarea lui value din clasa
}
 
SingletonClass.getInstance().show();

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

public class SingletonClass {
    private static SingletonClass obj = new SingletonClass();
    private SingletonClass() {}
 
    // eager instantiation - merge la threaduri
    public static SingletonClass getInstance() {
        return obj;
    }
}

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.

Exemplu (pizzas)

În exemplul de mai jos, utilizatorul cere un tip de pizza prin nume; dacă tipul există, primește informații despre pizza.

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;
    }
}
1.3 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.

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

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.

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

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
    }
}
 
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);
    }
}
poo/breviare/breviar-11.1765706564.txt.gz · Last modified: 2025/12/14 12:02 by george.tudor1906
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