Categorie
PHP

PHP – Design Pattern Singleton

I design pattern sono degli schemi che mostrano una soluzione progettuale flessibile per problemi di programmazione comuni.
Ogni pattern descrive un problema ricorrente indicando la soluzione per risolverlo nel modo migliore possibile.
Non si tratta di componenti software veri e propri (come per esempio una libreria) ma piuttosto, come dice Wikipedia di:

…una descrizione o un modello da applicare per risolvere un problema che può presentarsi in diverse situazioni durante la progettazione e lo sviluppo del software.

Vista la loro natura, i pattern non sono le legati ad uno specifico linguaggio di programmazione, anzi ne sono totalmente indipendenti, l’unico requisito necessario è che il linguaggio supporti la programmazione ad oggetti. PHP dalla versione 5 lo fa in maniera più che dignitosa.

Il Pattern Singleton

Questo pattern, dall’implementazione piuttosto semplice, è utilizzato per limitare ad una sola le istanze di un oggetto, prevedendo un punto di accesso globale ad essa.

Implementazione

Per implementare questo pattern la prima cosa da fare è rendere privato il costruttore per impedire che possa essere creata una nuova istanza bloccando quindi l’istanziazione diretta della classe. Poi si dovrà creare una proprietà statica privata che conterrà l’unica istanza della classe consentita. Quindi un metodo statico privato che restituisca l’istanza contenuta nella proprietà precedente, o che la crei salvandone il riferimento nella proprietà statica in caso l’istanza ancora non esista.
Il diagramma UML del pattern è il seguente:
Singleton UML

Esempio

Vediamo ora un esempio di implementazione del pattern singleton in PHP:

<?php
/**
 * Singleton Pattern
 *
 * @author Simone
 */
class Singleton {
    private static $_instance;
    private $count = 0;

    private function __construct() {}

    public static function getInstance() {
        if (!isset(self::$_instance)) {
            $class = __CLASS__;
            self::$_instance = new $class;
        }

        return self::$_instance;
    }

    public function increment() {
        return $this->count++;
    }

    public function __clone() {
        trigger_error('La clonazione non è consentita', E_USER_ERROR);
    }

}

$obj1 = Singleton::getInstance();
echo $obj1->increment() . PHP_EOL; //0

$obj2 = Singleton::getInstance();
echo $obj2->increment() . PHP_EOL; //1

$obj3 = clone $obj1;
?>

Possiamo notare:

  1. Come il costruttore sia stato dichiarato privato.
  2. Come il getter dell’istanza getInstance() (metodo statico) si limiti a restituire l’unica istanza della classe memorizzata nella proprietà statica $_instance.
  3. Che il metodo magico __clone generi un errore nel caso venga chiamato. Questa è solo una variante al rendere privato il metodo, proprio come il costruttore difatti si vuole evitare che siano generate nuove istanze della classe.

Il risultato del metodo increment() serve proprio a dimostrare che sia $obj1 che $obj2 contengono un riferimento alla stessa istanza.

Nota: Le proprietà statiche (come i metodi statici) non appartengono a nessuna istanza in particolare, ma sono di fatto componenti statiche di proprietà della classe stessa. Come con le costanti, non si può richiamare le proprietà statiche attraverso le istanze ma si dovrà usare l’operatore :: oppure la keyword self.

Singleton deprecato

Negli ultimi anni sono state mosse numerose critiche a questo pattern al punto da farlo considerare una “bad practice”. Tra i principali appunti troviamo:

  1. Nasconde le dipendenze dell’applicazione nel codice, invece di esporle attraverso le interfacce.
  2. Viola il Single Responsibility Principle.
  3. Rende problematico il testing ed il debug del codice.

3 risposte su “PHP – Design Pattern Singleton”

Ciao, complimenti innanzitutto per gli articoli che scrivi, molto chiari ed esaustivi.
Riusciresti ad integrare questo articolo sul Singleton con degli esempio pratici di utilizzo?
Sarebbe bello anche chiarire come per esempio è possibile trasportare lo stato del singleton attraverso le sessioni per mantenere l’istanza per tutta la sessione di navigazione dell’utente.

Grazie mille

Innanzitutto grazie per i complimenti.
Il tempo non è mai abbastanza… quando scrivo un articolo cerco, nei limiti del possibile, di rivolgermi al pubblico più vasto possibile, inserendo spiegazioni che a volte per molti possono sembrare scontate. Riguardo agli esempi è mancanza di fantasia.
Tornando alla tua richiesta di rendere persistente la classe per tutta la sessione è molto banale, basta modificare i metodo statico getInstance() così:

//..
    public static function getInstance() {
        if (!isset(self::$_instance)) {
            if (isset($_SESSION['singleton'])) {
                self::$_instance = $_SESSION['singleton'];
            } else {
                $class = __CLASS__;
                self::$_instance = new $class;
                $_SESSION['singleton'] = self::$_instance;
            }       
        }
 
        return self::$_instance;
    }
//..

In pratica se la proprietà statica non contiene l’istanza allora la si cerca nella variabile di sessione prima di crearne una. Se la var di sessione non esiste allora si crea l’istanza della classe assegnandola naturalmente anche alla sessione.
Naturalmente prima dovrà essere avviata la sessione con

session_start()

Spero di esserti stato di aiuto. Ciao.

Grazie mille, immagino che il tempo per coprire tutti gli aspetti di un argomento sia sempre poco 🙂 grazie per la spiegazione avevo immaginato una cosa del genere ma non essendo esperto non ne ero sicuro.

Complimenti ancora

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.