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

The Singleton pattern ensure a class only has one instance, and provide a global point of access to it.

Con il singleton pattern è quindi possibile creare una classe che abbia una sola istanza fornendo un punto di accesso globale per comunicare con gli altri elementi del nostro software. Il singleton deve avere due elementi fondamentali:

  • il costruttore privato (o protetto) per evitare la creazione di oggetti da classi esterne,
  •  un metodo statico che funge da getter per accedere all’unica istanza dell’oggetto.

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
  • Gestione della cache
  • Log / Debug
  • Router
  • Loader
  • Connessione al DB durante una chiamata

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

Singleton::getInstance();
Singleton::getInstance();

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

Il lato oscuro del 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:

  • Nasconde le dipendenze dell’applicazione.
  • Viola il Single Responsibility Principle (SRP) poiché crea delle dipendenze nei componenti che lo utilizzano. L’SRP vorrebbe infatti ogni componente assegnato ad una singola funzione o responsabilità.
  • Ostacola il debug e l’esecuzione dei test sui componenti che fanno uso dell’istanza del Singleton poiché il suo contenuto può variare.
  • 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.