Zastosowanie
Singleton
(ang. Singleton
) (wzorzec kreacyjny) ma za zadanie przede wszystkim ograniczenie tworzenia obiektów danej klasy do jednej instancji oraz zapewnieniu do niego globalnego dostępu. Swoje pochodzenie zawdzięcza C++
, gdzie pełnił rolę następnika zmiennej globalnej używanej przez preprocesor. Jest wzorcem, który wzbudza wiele kontrowersji - z pozoru prosty, łatwy w implementacji, jednakże źle wykorzystany stanowi popularny antywzorzec. Dzieje się tak, gdy jego rola jest sprowadzona do zmiennej globalnej bez uwzględnienia kontekstu wykorzystania. Singleton
można zastosować tam, gdzie rzeczywiście istnieje pewność występienia tylko jednej współdzielonej instancji klasy.
Ograniczenia
Singleton
tworzony jest w oparciu o prywatny konstruktor, który uniemożliwia rozszerzenie klasy. Poza zarządzaniem swoim cyklem życia pełni rolę logiki biznesowej czym łamie zasadę SRP
- pojedynczej odpowiedzialności. Co więcej łamie zasadę OCP
- otwarte/zamknięte, która mówi, że klasy powinnyć być otwarte na rozszerzenie lecz zamknięte na modyfikacje. Singleton
utrudnia testowanie ponieważ przed wywołaniem testów należy pamiętać, aby był on właściwie zainicjonowany. Ze względu na architekturę systemu Android
, Singleton
nie jest zalecanym wzorcem. Singleton Mutable
może utracić swój stan, gdy system potrzebuje zwolnić zasoby pamięci. Natomiast Singleton Immutable
może być często zastąpiony klasami typu Utility
(z metodami statycznymi). W środowisku wielowątkowym wymaga specjalnego traktowania.
Użycie
Wzorzec ten jest wykorzystywany w dostępie do SharedPreferences
. Popularną praktyką jest również jego implementacja dla obiektów typu Logger
czy API dostępu do danych np.: Retrofit
.
Implementacja
Istnieje wiele implementacji tego wzorca. Jedna z najprostszych opiera się na inicjalizowaniu obiektu poprzez publiczną metodę getInstance
dopiero w momencie jego pierwszego wywołania. Konstruktor jest niewidoczna spoza klasy.
Poniższy listing przedstawia najprostszą implementacje Singleton
. Rodzi ona jednak problemy w środowisku wielowątkowym.
Aby Singleton
mógł zostać użyty w wielowątkowych aplikacjach należy skorzystać z podwójnego sprawdzania i blokowania.
Optymalnym rozwiązaniem jest Singleton Holder
, który zapewnia leniwe tworzenie instancji oraz nie wymaga synchronizacji.
Przykład
Aplikacja ma za zadanie przechowywać oraz wyświetlać dane użytkownika. Dostęp do danych powinien być możliwy z różnych miejsc aplikacji. Do realizacji można wykorzystać SharedPreferences
, który umożliwia przechowywanie ustawień aplikacji. Dostęp do odczytu i zapisu zapewnia instancja klasy, a operacje te są od niej niezależne. Zatem prawdopodobnie nie ma potrzeby przechowywania w pamięciu wielu instancji SharedPreferences
. Dostęp do ustawień aplikacji może być wykorzystywany w wielu miejscach dlatego zastosowanie wzorca Singleton
wydaje się być uzasadnione. SharedPreferences
w sposób niejawny korzysta ze wzorca Singleton
. Poniższy listing przedstawia jego jawną implementacje.
Biblioteki
Właściwym wykorzystaniem Singleton
jest zastosowanie frameworka Dagger 2
, implementującego wzorzec Wstrzykiwanie Zależności
. Przykładem wykorzystania wzorca Singleton
w standardowym pakiecie Java
jest klasa System
z metodą getSecurityManager
.