Il singleton è un pattern che garantisce una sola istanza di una determinata classe. Per esempio quando si desidera avere solo un Window Manager oppure una sola Coda di Stampa oppure un unico accesso al database si può usare il pattern singleton.
Ci sono diversi modi per implementarlo in java.
Il più semplice è rendere una variabile statica dello stesso tipo della classe, il costruttore privato (quindi nessuno lo può chiamare, e di conseguenza nessuno può istanziare la classe, al di fuori di essa) e avere un metodo statico che restituisca l’unica istanza esistente della classe. Questo modo di implementare il pattern si chiama Pattern Singleton Eager.
Esempio
Ipotizziamo di avere una classe impiegato cosi composta:
public class Impiegato { private String nome = "Pippo"; public String getNome() { return nome; } }
Applicando il pattern diventa
public class Impiegato { private static final Impiegato unicaIstanza = new Impiega-to(); private String nome = "Pippo"; private Impiegato() { } public static Impiegato getInstance() { return unicaIstanza; } public String getNome() { return nome; } }
Vediamo come usarla:
public static void main(String[] args) { Impiegato a = Impiegato.getInstance(); Impiegato b = Impiegato.getInstance(); if(a == b) {// a e b si puntano allo stesso oggetto System.out.println("a e b sono la stessa istanza"); } }
Come si evince dall’output:
a e b sono la stessa unica istanza.
Un altro modo di implementare il pattern è istanziare l unico variabile non al momento della creazione della classe ma solo quando viene richiamato il metodo che la restituisce:
public class ImpiegatoDue { private static ImpiegatoDue unicaIstanza; private String nome = "Pippo"; private ImpiegatoDue() { } public static ImpiegatoDue getInstance() { if ( unicaIstanza == null ) unicaIstanza = new ImpiegatoDue(); return unicaIstanza; } public String getNome() { return nome; } }
Notare che, come detto, la variabile unicaIstanza viene inizializzata nel metodo getInstance(). Questa soluzione è nota come pattern singleton lazy.
Nessuna delle due soluzioni è thread-safe. Per fare ciò, il modo più semplice è rendere il metodo getInstance sincronizzato:
public class ImpiegatoDue { private static ImpiegatoDue unicaIstanza; private String nome = "Pippo"; private ImpiegatoDue() { } public static synchronized ImpiegatoDue getInstance() { if ( unicaIstanza == null ) unicaIstanza = new ImpiegatoDue(); return unicaIstanza; } public String getNome() { return nome; } }