Il singleton è tra i design pattern creazionali più conosciuti e sul suo utilizzo nella programmazione ad oggetti (OOP) ci sono pareri contrastanti. Vediamo come implementarlo correttamente in PHP partendo dalla definizione data dalla Gang of Four (GoF).

Il Singleton pattern assicura che una sola istanza di una classe esista, fornendo un punto di accesso globale a tale istanza.

Il pattern Singleton impedisce la creazione di più istanze di una classe garantendo che ne esista solo una e fornisce un modo per accedere a quell’istanza unica da qualsiasi parte dell’applicazione.

Il singleton prevede tre elementi fondamentali:

  • Costruttore privato: impedisce la creazione di istanze della classe dal di fuori della classe stessa.
  • Variabile statica privata: mantiene l’istanza unica della classe. Deve essere privata per evitare che altri oggetti accedano ad essa direttamente.
  • Metodo statico di accesso all’istanza: restituisce l’istanza unica della classe. Se l’istanza non esiste ancora, il metodo crea l’istanza e la restituisce, altrimenti restituisce semplicemente l’istanza esistente.

Scenari di utilizzo

Il pattern singleton può essere utilizzato quando un certo oggetto deve essere “unico” all’interno del nostro applicativo, per un ruolo di coordinamento o per ottimizzare le risorse. Ecco alcuni esempi concreti di utilizzo:

  • Registro / Storage condiviso
  • Log / Debug
  • Connessione a DB persistente

Esempio di pattern Singleton in PHP

Vediamo ora il codice di base per implementare questo pattern.

class MySingleton {

    /**
     * Istanza unica del singleton
     * @var object
     */
    private static object $instance;

    /**
     * Costruttore privato per prevenire che venga istanziato da codice esterno.
     */
    private function __construct() {
        echo 'Faccio qualcosa...';
    }

    /**
     * Metodo pubblico per l'accesso all'istanza unica di classe.
     * @return object|MySingleton
     */
    public static function getInstance() {
        if ( !isset(self::$instance) ) {
            self::$instance = new MySingleton();
        }
        return self::$instance;
    }
}

Se proviamo ad istanziare due volte il singleton noteremo che il messaggio “Faccio qualcosa…” presente nel costruttore verrà mostrato una sola volta. poiché il sistema utilizzato per implementarlo garantisce l’unicità dell’istanza (oggetto).

Per rendere il codice più riutilizzabile possiamo apportare una piccola modifica all’interno del metodo getIstance() in modo che il nome della classe istanziata con new sia valorizzato automaticamente. Sfruttiamo la costante magica __CLASS__ che restituisce il nome della classe nella quale è richiamata.

if ( !isset(self::$instance) ) {
    $className = __CLASS__;
    self::$instance = new $className;
}

Vantaggi del design pattern singleton

  • Garantisce una sola istanza: Il pattern Singleton garantisce che una sola istanza di una classe esista in un determinato momento, il che può essere utile in alcune situazioni, ad esempio per gestire le connessioni al database o le impostazioni globali dell’applicazione.
  • Facile accesso globale: Il pattern Singleton fornisce un punto di accesso globale all’istanza unica, il che significa che possiamo accedere all’istanza ovunque e in qualsiasi momento senza dover preoccuparci di passare l’istanza esplicitamente a ogni parte dell’applicazione.
  • Riduzione del consumo di memoria: Poiché una sola istanza esiste in un determinato momento, il pattern Singleton può aiutare a ridurre il consumo di memoria del sistema.

Svantaggi del design pattern singleton

Alcuni puristi della programmazione ad oggetti (OOP) possono storcere il naso quando si parla di singleton definendolo un anti-pattern. Le principali critiche al singleton sono le seguenti:

  • Viola il Single Responsibility Principle (SRP) se crea delle dipendenze nei componenti che lo utilizzano. L’SRP vorrebbe infatti ogni componente assegnato ad una singola funzione o responsabilità.
  • Difficile da testare poiché l’istanza unica esiste in tutta l’applicazione e non può essere facilmente sostituita con una mock o una versione di prova.
  • Difficile da estendere poiché impone l’esistenza di una sola istanza di una classe in un determinato momento, può essere difficile estendere il pattern se si desidera introdurre più istanze della classe.
  • Crea accoppiamento forte: poiché ogni parte dell’applicazione che utilizza il Singleton dipende dal suo stato globale.
  • Viene utilizzato erroneamente per sostituire le variabili globali che, per definizione, non devono essere influenzate dall’applicativo.

Conclusioni

Il singleton si può rivelare molto utile in specifiche situazioni, ma non bisogna abusarne così come non bisogna abusare delle implementazioni statiche nella programmazione ad oggetti. Può essere utile valutare anche altri tipi di soluzioni come la Dependency Injectiono il pattern Factory.

Per un panoramica generale consiglio di leggere l’articolo introduttivo sui design patterns.