Singleton

Da Wikipedia, l'enciclopedia libera.
bussola Disambiguazione – Se stai cercando l'insieme singleton, vedi Singoletto.

Nella programmazione ad oggetti, il singleton è uno dei pattern fondamentali descritti dalla "Gang of four" nel celebre libro Design Patterns.

Scopo[modifica | modifica wikitesto]

Il singleton è un design pattern creazionale che ha lo scopo di garantire che di una determinata classe venga creata una e una sola istanza, e di fornire un punto di accesso globale a tale istanza.

Diagramma UML di una classe singleton

Implementazione[modifica | modifica wikitesto]

L'implementazione più semplice di questo pattern prevede che la classe singleton abbia un unico costruttore privato, in modo da impedire l'istanziazione diretta della classe. La classe fornisce inoltre un metodo "getter" statico che restituisce una istanza della classe (sempre la stessa), creandola preventivamente o alla prima chiamata del metodo, e memorizzandone il riferimento in un attributo privato anch'esso statico. Il secondo approccio si può classificare come basato sul principio della lazy initialization (letteralmente "inizializzazione pigra") in quanto la creazione dell'istanza della classe viene rimandata nel tempo e messa in atto solo quando ciò diventa strettamente necessario (al primo tentativo di uso).

Esempio: Java[modifica | modifica wikitesto]

Il seguente frammento di codice descrive una classe strutturata secondo il pattern singleton nel linguaggio Java:

public class MioSingolo {
    private static MioSingolo istanza = null;
 
    //Il costruttore private impedisce l'istanza di oggetti da parte di classi esterne
    private MioSingolo() {}
 
    // Metodo della classe impiegato per accedere al singleton
    public static synchronized MioSingolo getMioSingolo() {
        if (istanza == null) 
            istanza = new MioSingolo();
        return istanza;
    }
}

Esempio: C++[modifica | modifica wikitesto]

Il seguente frammento di codice descrive una classe minimalista strutturata secondo il pattern singleton nel linguaggio C++:

#include <iostream>
 
class singleton {
private:
        // ecco il costruttore privato in modo che l'utente non possa istanziare direttamante
	singleton() { };
public:
	static singleton& get_instance() 
        {
                // l'unica istanza della classe viene creata alla prima chiamata di get_instance()
                // e verrà distrutta solo all'uscita dal programma
		static singleton instance;
		return instance;
	}
	bool method() { return true; };
};
 
 
int main() {
	std::cout << singleton::get_instance().method() << std::endl;
 
	return 0;
 
}

Esempio: C#[modifica | modifica wikitesto]

Il seguente frammento di codice descrive una classe strutturata secondo il pattern singleton nel linguaggio C#:

public class Singleton
{
    //..attributi membro di istanza....
    private static Singleton _instance=null;
 
    protected Singleton()
    {
        //...inizializzazione istanza...
    }
 
    public static Singleton GetInstance()
    {
        if(_instance==null) _instance=new Singleton();
        return _instance;
    }
 
    //...eventuali metodi pubblici, privati e protetti di istanza....
}

Esempio: Objective-C[modifica | modifica wikitesto]

Il seguente frammento di codice descrive una classe strutturata secondo il pattern singleton nel linguaggio Objective-C:

#import "Singleton.h"
 
@implementation Singleton
 
static Singleton *sharedSingleton =nil;
 
+ (Singleton *) sharedSingleton 
{ 
  if (sharedSingleton == nil) 
  { 
    sharedSingleton = [[super allocWithZone:NULL] init]; 
  } 
  return sharedSingleton; 
}
 
+ (id)allocWithZone:(NSZone *)zone 
{ 
 @synchronized(self) 
 { 
   if (sharedSingleton == nil) 
   { 
     sharedSingleton = [super allocWithZone:zone]; 
     return sharedSingleton; 
   } 
 } 
 return nil; 
}
 
- (id)copyWithZone:(NSZone *)zone 
{ 
 return self; 
}
 
- (id)retain 
{ 
 return self; 
}
 
- (NSUInteger)retainCount 
{ 
 return NSUIntegerMax; 
}
 
- (void) release 
{ 
}
 
- (id)autorelease 
{ 
 return self; 
} 
@end

Esempio: PHP[modifica | modifica wikitesto]

Il seguente frammento di codice descrive una classe strutturata secondo il pattern singleton nel linguaggio PHP:

class Singleton
{
    private static $instance = null;
 
    private function __construct()
    {
         //...inizializzazione istanza...
    }
 
    private function __clone()
    {
        // evita la clonazione dell'oggetto
    }
 
    public static function getInstance()
    {
        if (static::$instance === null) {
            static::$instance = new static();
        }
        return static::$instance;
    }
}

Implementazioni multi-thread[modifica | modifica wikitesto]

In applicazioni multi-thread l'utilizzo di questo pattern con la lazy initialization richiede un'attenzione particolare: se due thread tentano di eseguire contemporaneamente il costruttore quando la classe non è stata ancora istanziata, devono entrambi controllare se l'istanza esiste e soltanto uno deve creare la nuova istanza.

Sincronizzazione esplicita[modifica | modifica wikitesto]

Il modo più semplice per implementare una versione thread-safe è quello di usare un meccanismo di sincronizzazione come quello fornito dalla parola chiave synchronized di Java. Tuttavia questo approccio è inefficiente: infatti la sincronizzazione è utile solo per la prima inizializzazione, e costituisce un inutile overhead nelle successive chiamate al metodo getter.

Esempio: Java[modifica | modifica wikitesto]

public class MioSingolo{
    private static MioSingolo istanza = null;
 
    private MioSingolo() {}
 
    public static MioSingolo getMioSingolo() {
        synchronized (MioSingolo.class) { //equivalente a usare l'intero metodo synchronized
            if (istanza == null) {
                istanza = new MioSingolo();
            }
            return istanza;
        }
    }
}

Esempio: C#[modifica | modifica wikitesto]

Ed eccone la traduzione in codice C#. Anche questa implementazione è thread-safe, ed ugualmente inefficiente per l'utilizzo del lock sull'oggetto che funge da semaforo (verificando se l'istanza è null anche prima del lock[1], è possibile eliminare questa inefficienza):

public class Singleton 
{
    private static Singleton istanza=null;
    private static object semaforo = new object();
 
    private Singleton() {}
 
    public static Singleton Istanza 
    {
      get {
        lock(semaforo) {
          if(istanza==null) istanza=new Singleton();
            return istanza;
        }
      }
    }
}

Sincronizzazione implicita[modifica | modifica wikitesto]

In alcuni linguaggi è possibile evitare l'overhead di sincronizzazione sfruttando quelle peculiarità della lazy initialization che consentono di assicurarsi la presenza del singleton in memoria all'atto del suo utilizzo. Le modalità specifiche possono variare da linguaggio a linguaggio; ad esempio, in Java è possibile sfruttare il fatto che l'inizializzazione di una classe ed il suo caricamento in memoria, quando avvengono, sono operazioni thread-safe che comprendono l'inizializzazione di tutte le variabili statiche (attributi) della classe stessa.

Quello che segue è l'esempio più semplice, che tuttavia realizza la creazione dell'istanza al momento dell'inizializzazione della classe (ad esempio, invocando un metodo statico della classe stessa). Questo approccio è adatto nei casi più semplici, o in quei casi in cui la lazyness non è necessaria; ad esempio, è sconsigliato in applicazioni in cui sono presenti numerosi singleton dall'inizializzazione "pesante" dotati di metodi o attributi statici che potrebbero essere acceduti in largo anticipo rispetto all'effettiva necessità d'uso del singleton in quanto tale (tipicamente, all'avvio dell'applicazione).

Esempio di inizializzazione preventiva: Java[modifica | modifica wikitesto]

public class Singleton { 
 
   /**
    * Creato all'atto di caricamento in memoria della classe, thread-safe
    */
   private final static Singleton ISTANZA = new Singleton();
 
  /**
   * Costruttore privato, in quanto la creazione dell'istanza deve essere controllata.
   */
  private Singleton() {}
 
  /**
   * Punto di accesso al Singleton.
   * @return il Singleton corrispondente
   */
  public static Singleton getInstance() {
    return ISTANZA;
  }
}

Esempio di inizializzazione lazy: Java[modifica | modifica wikitesto]

Un approccio che rimanda la creazione del singleton al suo effettivo primo utilizzo è stato presentato per la prima volta da Bill Pugh, e sfrutta appieno la lazy initialization: l'idea è quella di includere nella classe che implementa il singleton una classe-contenitore avente, come attributo statico, una istanza del singleton stesso: il primo accesso a tale attributo statico (e la contestuale inizializzazione) verrà quindi effettuato durante l'inizializzazione della classe-contenitore, e quindi sempre in modo serializzato. In questo modo l'istanza del singleton viene creata solo alla prima chiamata del metodo getter, e non prima.

public class Singleton { 
 
  /**
   * Costruttore privato, in quanto la creazione dell'istanza deve essere controllata.
   */
  private Singleton() {}
 
  /**
   * La classe Contenitore viene caricata/inizializzata alla prima esecuzione di getInstance()
   * ovvero al primo accesso a Contenitore.ISTANZA, ed in modo thread-safe.
   * Anche l'inizializzazione dell'attributo statico, pertanto, viene serializzata.
   */
  private static class Contenitore { 
    private final static Singleton ISTANZA = new Singleton();
  }
 
  /**
   * Punto di accesso al Singleton. Ne assicura la creazione thread-safe
   * solo all'atto della prima chiamata.
   * @return il Singleton corrispondente
   */
  public static Singleton getInstance() {
    return Contenitore.ISTANZA;
  }
}

Critiche[modifica | modifica wikitesto]

Alcuni autori hanno criticato il pattern singleton, osservando che, con opportune modifiche strutturali, una istanza singola può entrare più efficacemente a far parte dell'Ambiente globale dell'applicazione[2][3].

Note[modifica | modifica wikitesto]

  1. ^ MSDN. Implementing Singleton in C#, Version 1.0.1
  2. ^ Scott Densmore. Why singletons are evil, May 2004
  3. ^ J.B. Rainsberger, IBM. Use your singletons wisely, July 2001

Voci correlate[modifica | modifica wikitesto]

Altri progetti[modifica | modifica wikitesto]

Collegamenti esterni[modifica | modifica wikitesto]