
Co to jest singleton w Javie?
Singleton to jeden z najprostszych wzorców projektowych na poziomie klasy. Czasami ludzie mówią „ta klasa to singleton”, co oznacza, że klasa implementuje wzorzec projektowy singleton. Czasami konieczne jest napisanie klasy, w której ograniczymy tworzenie instancji do pojedynczego obiektu. Na przykład klasa odpowiedzialna za logowanie lub łączenie się z bazy danych. Wzorzec projektowy singleton opisuje, w jaki sposób możemy to osiągnąć. Singleton to wzorzec projektowy, który robi dwie rzeczy:-
Gwarantuje, że zawsze będzie tylko jedna instancja klasy.
-
Zapewnia pojedynczy punkt globalnego dostępu do tej instancji.
-
Prywatny konstruktor. Ogranicza to możliwość tworzenia obiektów klasy poza samą klasą.
-
Publiczna metoda statyczna, która zwraca instancję klasy. Ta metoda nazywa się getInstance . Jest to punkt globalnego dostępu do instancji klasy.
Opcje wdrożenia
Wzorzec projektowy singleton jest stosowany na różne sposoby. Każda opcja jest dobra i zła na swój sposób. Jak zwykle nie ma tutaj idealnej opcji, ale powinniśmy do niej dążyć. Przede wszystkim zdecydujmy, co jest dobre, a co złe i jakie metryki wpływają na to, jak oceniamy różne implementacje wzorca projektowego. Zacznijmy od dobra. Oto czynniki, które sprawiają, że implementacja jest bardziej soczysta i atrakcyjna:-
Leniwa inicjalizacja: instancja nie jest tworzona, dopóki nie jest potrzebna.
-
Prosty i przejrzysty kod: ta metryka jest oczywiście subiektywna, ale jest ważna.
-
Bezpieczeństwo wątków: poprawność działania w środowisku wielowątkowym.
-
Wysoka wydajność w środowisku wielowątkowym: niewielkie blokowanie wątków lub brak blokowania wątków podczas udostępniania zasobu.
-
Brak leniwej inicjalizacji: gdy klasa jest ładowana, gdy aplikacja się uruchamia, niezależnie od tego, czy jest potrzebna (paradoksalnie w świecie IT lepiej być leniwym)
-
Złożony i trudny do odczytania kod. Ten wskaźnik jest również subiektywny. Jeśli twoje oczy zaczną krwawić, założymy, że implementacja nie jest najlepsza.
-
Brak zabezpieczenia nici. Innymi słowy, „niebezpieczeństwo nici”. Niepoprawne działanie w środowisku wielowątkowym.
-
Słaba wydajność w środowisku wielowątkowym: wątki blokują się nawzajem przez cały czas lub często podczas współdzielenia zasobu.
Kod
Teraz jesteśmy gotowi do rozważenia różnych opcji wdrożenia i wskazania zalet i wad:Prosty
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSTANCE;
}
}
Najprostsza implementacja. Plusy:
-
Prosty i przejrzysty kod
-
Bezpieczeństwo nici
-
Wysoka wydajność w środowisku wielowątkowym
- Brak leniwej inicjalizacji.
Leniwa inicjalizacja
public class Singleton {
private static final 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 dostęp
public class Singleton {
private static final 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ść wielowątkowa
Blokada podwójnie sprawdzona
public class Singleton {
private static final 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 we wcześniejszych wersjach Javy poniżej 1.5 (użycie słowa kluczowego volatile zostało naprawione od wersji 1.5)
Właściciel klasy
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.
-
Poprawne działanie wymaga gwarancji, że obiekt singleton zostanie zainicjowany bez błędów. W przeciwnym razie pierwsze wywołanie metody getInstance spowoduje wyjątek ExceptionInInitializerError , a wszystkie kolejne wywołania wygenerują błąd NoClassDefFoundError .
Realizacja | Leniwa inicjalizacja | Bezpieczeństwo nici | Wydajność wielowątkowa | Kiedy użyć? |
---|---|---|---|---|
Prosty | - | + | Szybko | Nigdy. Lub być może, gdy leniwa inicjalizacja nie jest ważna. Ale nigdy nie byłoby lepiej. |
Leniwa inicjalizacja | + | - | Nie dotyczy | Zawsze, gdy wielowątkowość nie jest potrzebna |
Zsynchronizowany dostęp | + | + | Powolny | Nigdy. Lub być może, gdy wydajność wielowątkowa nie ma znaczenia. Ale nigdy nie byłoby lepiej. |
Blokada podwójnie sprawdzona | + | + | Szybko | W rzadkich przypadkach, gdy trzeba obsłużyć wyjątki podczas tworzenia singletonu (gdy singleton posiadacza klasy nie ma zastosowania) |
Właściciel klasy | + | + | Szybko | Zawsze wtedy, gdy potrzebna jest wielowątkowość i jest gwarancja, że obiekt singleton powstanie bez problemów. |
Plusy i minusy formacji singleton
Ogólnie rzecz biorąc, singleton robi dokładnie to, czego się od niego oczekuje:-
Gwarantuje, że zawsze będzie tylko jedna instancja klasy.
-
Zapewnia pojedynczy punkt globalnego dostępu do tej instancji.
-
Singleton narusza zasadę pojedynczej odpowiedzialności: oprócz swoich bezpośrednich obowiązków, klasa singleton kontroluje również liczbę instancji.
-
Zależność zwykłej klasy od singletona nie jest widoczna w kontrakcie publicznym klasy.
-
Zmienne globalne są złe. Ostatecznie singleton zamienia się w potężną zmienną globalną.
-
Obecność singletonu zmniejsza testowalność aplikacji jako całości, aw szczególności klas korzystających z singletonu.
GO TO FULL VERSION