Cosa sono i Design Patterns?

  



Nel mondo della programmazione, i design pattern rappresentano soluzioni ricorrenti a problemi comuni nello sviluppo software. Sono best practice documentate che aiutano i programmatori a scrivere codice più organizzato, riutilizzabile e manutenibile.

🔗 Ti piace Techelopment? Dai un’occhiata al sito per tutti i dettagli!

Origini e la Gang of Four

Il concetto di design pattern è stato reso popolare dal libro Design Patterns: Elements of Reusable Object-Oriented Software, pubblicato nel 1994 da Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides. Questi quattro autori sono noti come la Gang of Four (GoF). Nel loro lavoro, hanno catalogato 23 design pattern fondamentali, suddividendoli in tre categorie principali:

  1. Creazionali – si concentrano sulla gestione della creazione degli oggetti, migliorando la flessibilità e la riusabilità del codice.

    • Singleton: Garantisce che una classe abbia una sola istanza e fornisce un punto di accesso globale a quella istanza.

    • Factory Method: Definisce un'interfaccia per creare un oggetto, ma lascia che le sottoclassi decidano quale classe istanziare. Aiuta a disaccoppiare la creazione dell'oggetto dalla sua implementazione.

    • Abstract Factory: Fornisce un'interfaccia per creare famiglie di oggetti correlati senza specificarne le classi concrete. Utile per creare prodotti compatibili tra loro.

    • Builder: Separa la costruzione complessa di un oggetto dalla sua rappresentazione, permettendo la creazione di oggetti diversi con lo stesso processo di costruzione.

    • Prototype: Permette di creare nuovi oggetti clonando un oggetto esistente, riducendo la necessità di creare nuovi oggetti da zero e migliorando le performance in alcuni casi.

  2. Strutturali – riguardano la composizione delle classi e degli oggetti, facilitando la progettazione di strutture più efficienti.

    • Adapter: Converte l'interfaccia di una classe in un'altra interfaccia che il client si aspetta. Consente a classi con interfacce incompatibili di lavorare insieme.

    • Bridge: Disaccoppia un'interfaccia da una sua implementazione, permettendo che entrambe possano variare indipendentemente.

    • Composite: Consente di trattare oggetti singoli e composizioni di oggetti in modo uniforme. Utilizzato per strutture ad albero, come le gerarchie di file o componenti grafici.

    • Decorator: Aggiunge dinamicamente funzionalità a un oggetto, estendendo il suo comportamento senza modificarne la struttura.

    • Facade: Fornisce un'interfaccia semplificata per un insieme complesso di classi o librerie. Aiuta a ridurre la complessità nascondendo i dettagli di implementazione.

    • Flyweight: Condivide oggetti costosi da creare, utilizzando una quantità minima di memoria per migliorare le performance quando ci sono molti oggetti simili.

    • Proxy: Controlla l'accesso a un altro oggetto, agendo come un sostituto o un "intermediario". Può implementare il controllo di accesso, la protezione, il caching, e altro.

  3. Comportamentali – si occupano dell'interazione e della comunicazione tra gli oggetti.

    • Chain of Responsibility: Permette di passare una richiesta lungo una catena di handler, con la possibilità che ogni handler decida di gestire o passare la richiesta al successivo.

    • Command: Incapsula una richiesta come un oggetto, permettendo di parametrizzare i client con diverse richieste, ritardare l'esecuzione e tenere traccia delle richieste.

    • Interpreter: Fornisce un modo per rappresentare e interpretare un linguaggio. È utile per costruire interpreti di linguaggi o espressioni.

    • Iterator: Fornisce un modo per accedere agli elementi di una collezione senza esporne la struttura sottostante.

    • Mediator: Definisce un oggetto che incapsula come un insieme di oggetti interagisce, riducendo la necessità di comunicazione diretta tra oggetti, migliorando il disaccoppiamento.

    • Memento: Permette di salvare e ripristinare lo stato interno di un oggetto senza violare l'incapsulamento, utile per implementare funzionalità di undo/redo.

    • Observer: Consente a un oggetto (soggetto) di notificare ad altri oggetti (osservatori) quando il suo stato cambia, senza che il soggetto debba conoscere i dettagli degli osservatori.

    • State: Permette a un oggetto di comportarsi in modo diverso in base al suo stato interno, cambiando dinamicamente il comportamento dell'oggetto.

    • Strategy: Definisce una famiglia di algoritmi, incapsulandoli e rendendoli intercambiabili. Permette di modificare l'algoritmo usato da un oggetto senza modificarne la struttura.

    • Template Method: Definisce la struttura di un algoritmo, lasciando alcuni passi a sottoclassi che possono implementare o sovrascrivere i dettagli.

    • Visitor: Consente di aggiungere nuove operazioni a una struttura di oggetti senza modificare le classi degli oggetti stessi, visitando ciascun elemento della struttura e applicando l'operazione desiderata.

Perché usare i Design Pattern?

L'uso dei design pattern porta diversi vantaggi, tra cui:

  • Riutilizzabilità: aiuta a evitare la riscrittura di codice per problemi già risolti.

  • Manutenibilità: rende il codice più comprensibile e facile da modificare.

  • Scalabilità: semplifica l'adattamento del codice a nuove funzionalità.

  • Comunicazione: facilita la comprensione del codice tra sviluppatori, grazie a un linguaggio comune basato su pattern consolidati.

I design pattern sono strumenti fondamentali per gli sviluppatori software, permettendo di affrontare problemi comuni con soluzioni già testate ed efficienti. Studiare e applicare i pattern della Gang of Four aiuta a scrivere codice più solido, leggibile e scalabile riducendo la complessità.

Vediamo qualche esempio di Design Pattern nel dettaglio

1. Singleton (Creazionale)

Il Singleton garantisce che una classe abbia una sola istanza e fornisce un punto di accesso globale ad essa.

Esempio in JavaScript:

class Singleton {
    constructor() {
        if (!Singleton.instance) {
            Singleton.instance = this;
        }
        return Singleton.instance;
    }
}

const instance1 = new Singleton();
const instance2 = new Singleton();
console.log(instance1 === instance2); // Output: true

2. Decorator (Strutturale)

Il Decorator permette di aggiungere dinamicamente funzionalità a un oggetto senza modificare la sua struttura originale.

Esempio in JavaScript:

class Coffee {
    cost() {
        return 5;
    }
}

class MilkDecorator {
    constructor(coffee) {
        this.coffee = coffee;
    }
    cost() {
        return this.coffee.cost() + 2;
    }
}

const simpleCoffee = new Coffee();
const coffeeWithMilk = new MilkDecorator(simpleCoffee);
console.log(coffeeWithMilk.cost()); // Output: 7

3. Observer (Comportamentale)

Il Observer definisce una dipendenza uno-a-molti tra oggetti in modo che quando un oggetto cambia stato, tutti i suoi osservatori vengano notificati automaticamente.

Esempio in JavaScript:

class Subject {
    constructor() {
        this.observers = [];
    }
    addObserver(observer) {
        this.observers.push(observer);
    }
    notify(data) {
        this.observers.forEach(observer => observer.update(data));
    }
}

class Observer {
    update(data) {
        console.log(`Notified with data: ${data}`);
    }
}

const subject = new Subject();
const observer1 = new Observer();
const observer2 = new Observer();

subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notify("New update");



Follow me #techelopment

Official site: www.techelopment.it
facebook: Techelopment
instagram: @techelopment
X: techelopment
Bluesky: @techelopment
telegram: @techelopment_channel
whatsapp: Techelopment
youtube: @techelopment