Singleton – wzorzec czy antywzorzec?
Wśród programistów, często spotykam się z opinią, że Singleton stanowi antywzorzec. Niestety, rzadko też słyszę dobre argumenty podpierające tą tezę. Fakt, jest on jednym z najpowszechniejszej stosowanych wzorców projektowych i jednym z pierwszych jaki poznaje każdy programista. Konsekwencjami tego jest olbrzymia ilość błędnych implementacji, które krążą po repozytoriach i potrafią przyprawić o prawdziwy zawrót głowy podczas czytania kodu i poszukiwania przyczyny problemów.
Do czego właściwie to służy ? Opisując najkrócej jak się da posłużę się cytatem z książki GoF (Gang of Four):
Gwarantuje, że klasa będzie miała tylko jeden egzemplarz, i zapewnia globalny dostęp do niego.
I to tyle… proste prawda? Okazuje się, że nie. Przede wszystkim wzorzec jest masowo nadużywany, a o tym kiedy go stosować, a kiedy unikać można napisać spory artykuł na pewno nie nadający się do WeeklyJavaSnippets. Gdy coś implementujesz, a nie masz pewności czy powinieneś/powinnaś użyć singletona skontaktuj się z lekarzem lub farmac… Wróć! Z seniorem lub architektem oczywiście 🙂
Jak wygląda zatem poprawna i szybka implementacja wzorca Singleton?
public class MySingleton {
private MySingleton(){ } // wyłączamy konstruktor domyślny
private static class SingletonHolder {
private static final MySingleton INSTANCE = new MySingleton();
}
public static MySingleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
Prywatna, wewnętrzna i statyczna klasa SingletonHolder sprawia, że obiekt klasy MySingleton jest tworzony dopiero przy pierwszym wywołaniu getInstance(). Ponadto w przypadku tej implementacji nie ma możliwości utworzenia dwóch instancji przy współbieżnym dostępie, nie ma też dodatkowych opóźnień związanych z synchronizacją.
Wśród błędnych implementacji często pojawiają się różne wariacje checked-locked, double-checked locked, a można je łatwo rozpoznać po pewnej ilości if’ów mających zapewnić lazy-loading. Zainteresowanych tymi wariantami odsyłam np. do wiki. Z góry ostrzegam, że obecnie nawet automaty często oznaczają je jako błędne.
Inną ciekawą implementacją singletona jest oparcie go o enum. Wersję taką zaproponował Joshua Bloch w swojej książce:
public enum MyEnumSingleton {
INSTANCE;
private MyEnumSingleton(){ }
public void doWork(){ }
}
To tyle na dzisiaj. Bawcie się dobrze używając singletona i przede wszystkim pamiętajcie o tym, aby go nie nadużywać! Standardowo, dzięki wszystkim którzy dotarli aż tutaj 🙂