Co to jest singleton?
Singleton to jeden z najprostszych wzorców projektowych, które można zastosować w klasie. Ludzie czasami mówią „ta klasa to singleton”, co oznacza, że ta klasa implementuje wzorzec projektowy singletonu. Czasami konieczne jest napisanie klasy, dla której można utworzyć tylko jeden obiekt. Na przykład klasa odpowiedzialna za logowanie lub łączenie się z bazą danych. Wzorzec projektowy Singleton opisuje, w jaki sposób możemy wykonać takie zadanie. Singleton to wzorzec projektowy, który spełnia dwie funkcje:-
Zapewnia gwarancję, że klasa będzie miała tylko jedną instancję klasy.
-
Zapewnia globalny punkt dostępu do instancji tej klasy.
-
Prywatny konstruktor. Ogranicza możliwość tworzenia obiektów klas poza samą klasą.
-
Publiczna metoda statyczna, która zwraca instancję klasy. Ta metoda nazywa się
getInstance
. Jest to globalny punkt dostępu do instancji klasy.
Opcje wdrożenia
Wzorzec projektowy singleton jest używany na różne sposoby. Każda opcja jest dobra i zła na swój sposób. Tutaj jak zawsze: nie ma ideału, ale trzeba do niego dążyć. Ale przede wszystkim zdefiniujmy, co jest dobre, a co złe i jakie metryki wpływają na ocenę wdrożenia wzorca projektowego. Zacznijmy od pozytywów. Oto kryteria, które nadają realizacji soczystość i atrakcyjność:-
Leniwa inicjalizacja: gdy klasa jest ładowana, podczas gdy aplikacja działa dokładnie wtedy, gdy jest potrzebna.
-
Prostota i przejrzystość kodu: metryka jest oczywiście subiektywna, ale ważna.
-
Bezpieczeństwo wątków: działa poprawnie w środowisku wielowątkowym.
-
Wysoka wydajność w środowisku wielowątkowym: wątki blokują się minimalnie lub wcale podczas udostępniania zasobu.
-
Inicjalizacja nieleniwa: kiedy ładowana jest klasa w momencie uruchamiania aplikacji, niezależnie od tego, czy jest to potrzebne, czy nie (paradoks, w świecie IT lepiej być leniwym)
-
Złożoność i słaba czytelność kodu. Wskaźnik jest również subiektywny. Zakładamy, że jeśli z oczu leci krew, realizacja jest taka sobie.
-
Brak bezpieczeństwa nici. Inaczej mówiąc, „zagrożenie gwintem”. Błędne działanie w środowisku wielowątkowym.
-
Słaba wydajność w środowisku wielowątkowym: wątki blokują się wzajemnie przez cały czas lub często podczas udostępniania zasobu.
Kod
Teraz jesteśmy gotowi rozważyć różne opcje wdrożenia, wymieniając zalety i wady:Proste rozwiązanie
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSTANCE;
}
}
Najprostsza implementacja. Plusy:
-
Prostota i przejrzystość kodu
-
Bezpieczeństwo nici
-
Wysoka wydajność w środowisku wielowątkowym
- Nie leniwa inicjalizacja.
Leniwa inicjalizacja
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
Plusy:
-
Leniwa inicjalizacja.
-
Nie jest bezpieczny dla wątków
Zsynchronizowany akcesor
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
Plusy:
-
Leniwa inicjalizacja.
-
Bezpieczeństwo nici
-
Słaba wydajność w środowisku wielowątkowym
getInstance
jest zsynchronizowana i można ją wprowadzić tylko pojedynczo. Tak naprawdę nie musimy synchronizować całej metody, a jedynie jej część, w której inicjujemy nowy obiekt klasy. Nie możemy jednak po prostu owinąć synchronized
części odpowiedzialnej za utworzenie nowego obiektu w bloku: nie zapewni to bezpieczeństwa wątku. To trochę bardziej skomplikowane. Poniżej podano poprawną metodę synchronizacji:
Podwójnie sprawdzone zamknięcie
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {
}
public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
Plusy:
-
Leniwa inicjalizacja.
-
Bezpieczeństwo nici
-
Wysoka wydajność w środowisku wielowątkowym
-
Nieobsługiwane w wersjach Java niższych niż 1.5 (słowo kluczowe volatile zostało poprawione w wersji 1.5)
INSTANCE
musi mieć wartość final
, lub volatile
. Ostatnią implementacją, którą dzisiaj omówimy, jest Class Holder Singleton
.
Posiadacz klasy Singleton
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
public static final Singleton HOLDER_INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.HOLDER_INSTANCE;
}
}
Plusy:
-
Leniwa inicjalizacja.
-
Bezpieczeństwo nici.
-
Wysoka wydajność w środowisku wielowątkowym.
-
Do poprawnego działania należy zagwarantować, że obiekt klasy
Singleton
zostanie zainicjowany bez błędów. W przeciwnym razie pierwsze wywołanie metodygetInstance
zakończy się błędemExceptionInInitializerError
, a wszystkie kolejne zakończą się niepowodzeniemNoClassDefFoundError
.
Realizacja | Leniwa inicjalizacja | Bezpieczeństwo nici | Szybkość wielowątkowości | Kiedy użyć? |
---|---|---|---|---|
Proste rozwiązanie | - | + | Szybko | Nigdy. Lub gdy leniwa inicjalizacja nie jest ważna. Ale nigdy lepiej. |
Leniwa inicjalizacja | + | - | Nie dotyczy | Zawsze, gdy wielowątkowość nie jest potrzebna |
Zsynchronizowany akcesor | + | + | Powoli | Nigdy. Albo gdy szybkość pracy przy wielowątkowości nie ma znaczenia. Ale nigdy lepiej |
Podwójnie sprawdzone zamknięcie | + | + | Szybko | W rzadkich przypadkach, gdy trzeba obsłużyć wyjątki podczas tworzenia singletonu. (kiedy posiadacz klasy Singleton nie ma zastosowania) |
Posiadacz klasy Singleton | + | + | Szybko | Zawsze wtedy, gdy potrzebna jest wielowątkowość i jest gwarancja, że obiekt klasy singleton zostanie utworzony bez problemów. |
Plusy i minusy wzorca Singletona
Ogólnie rzecz biorąc, singleton robi dokładnie to, czego się od niego oczekuje:-
Zapewnia gwarancję, że klasa będzie miała tylko jedną instancję klasy.
-
Zapewnia globalny punkt dostępu do instancji tej klasy.
-
Singleton narusza zasadę SRP (ang. Single Responsibility Principle) – klasa Singleton oprócz swoich bezpośrednich obowiązków kontroluje także liczbę swoich kopii.
-
Zależność zwykłej klasy lub metody od singletonu nie jest widoczna w kontrakcie publicznym klasy.
-
Zmienne globalne są złe. Singleton ostatecznie przekształca się w jedną potężną zmienną globalną.
-
Obecność singletonu zmniejsza ogólnie testowalność aplikacji, a w szczególności klas korzystających z singletonu.
GO TO FULL VERSION