Singleton jest to wzorzec projektowy, który pozwala na stworzenie tylko jednej instancji danej klasy w aplikacji oraz zapewnia do niej globalny dostęp. Stosowany jest gdy, chcemy mieć pewność ze dany obiekt zostanie stworzony tylko raz w naszej aplikacji. Proponowane podejście ma szczególne znaczenie gdy w naszej aplikacji może istnieć tylko jeden ogólnodostępny obiekt danej klasy np. klasy zawierające globalne configi lub połączenia z bazą danych. Poniżej przykłady implementacji:
1. Implementacja z „eager” inicjalizacją
public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {
// prywatny konstruktor
}
public static EagerSingleton getInstance() {
return instance;
}
}
Instancja singletona zostanie utworzona w momencie załadowania klasy przez wirtualną maszynę Javy.
W związku z tym nigdy nie występuje przypadek, gdy nie ma instancji danej klasy.
2. Implementacja z „lazy” inicjalizacją
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {
// prywatny konstruktor
}
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
W przypadku leniwej inicjalizacji singletona, instancja klasy zostaje utworzona dopiero w momencie pierwszego wywołania metody getInstance(). Synchronizacja zapewnia bezpieczeństwo w środowisku wielowątkowym.
3. Implementacja z „double-check”
public class ThreadSafeSingleton {
private static volatile ThreadSafeSingleton instance;
private ThreadSafeSingleton() {
// prywatny konstruktor
}
public static ThreadSafeSingleton getInstance() {
if (instance == null) {
synchronized (ThreadSafeSingleton.class) {
if (instance == null) {
instance = new ThreadSafeSingleton();
}
}
}
return instance;
}
}
W przypadku implementacji z double-check, singleton jest bezpieczny w środowisku wielowątkowym. Zmienna volatile zapewnia, że każdy wątek widzi najnowszą wartość singletona. Natomiast blok synchroniczny ponownie weryfikuje istnienie instancji klasy. Dzięki temu rozwiązaniu wątki są zakolejkowane i nie powstaje duplikat instancji singletona.
4. Implementacja jako Enum
public enum EnumSingleton {
INSTANCE;
}
Implementacja singletona jako enum jest krótka i bezpieczna w środowisku wielowątkowym. Enum w Javie jest tworzony tylko raz w czasie działania aplikacji, co zapewnia, że Singleton jest dostępny globalnie. Ponadto metody instancji mogą być dodane wewnątrz enuma.
5. Implementacja z zabezpieczeniem przed serializacją
public class SerializableSingleton implements Serializable {
private static Singleton instance = new Singleton();
private SerializableSingleton () {
// prywatny konstruktor
}
public static SerializableSingleton getInstance() {
return instance;
}
protected Object readResolve() {
return getInstance();
}
}
W powyższej implementacji, klasa SerializableSingleton implementuje interfejs Serializable, który jest wymagany do serializacji i deserializacji obiektów w Javie. Metoda readResolve() zapewnia, że deserializowana instancja singletona jest zawsze taka sama jak ta, która została utworzona podczas pierwszego wywołania metody getInstance(). W ten sposób, unikamy problemów związanych z tworzeniem wielu instancji singletona.
Istnieją różne sposoby implementacji wzorca Singleton. Jednak wybór odpowiedniej implementacji zależy ściśle od wymagań projektowych.