CodeGym /Blog Java /Random-PL /Klasa Java Singleton
Autor
Artem Divertitto
Senior Android Developer at United Tech

Klasa Java Singleton

Opublikowano w grupie Random-PL
Cześć! Dzisiaj zagłębimy się w szczegóły różnych wzorców projektowych, zaczynając od wzorca Java Singleton. Przyjrzyjmy się: co ogólnie wiemy o wzorcach projektowych? Wzorce projektowe to najlepsze praktyki, które możemy zastosować, aby rozwiązać szereg znanych problemów. Wzorce projektowe generalnie nie są powiązane z żadnym językiem programowania. Pomyśl o nich jako o zbiorze zaleceń, które pomogą Ci uniknąć błędów i wymyślania koła na nowo.Wzorce projektowe: Singleton - 1

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:
  1. Gwarantuje, że zawsze będzie tylko jedna instancja klasy.

  2. Zapewnia pojedynczy punkt globalnego dostępu do tej instancji.

Istnieją więc dwie cechy, które są charakterystyczne dla niemal każdej implementacji wzorca singleton:
  1. Prywatny konstruktor. Ogranicza to możliwość tworzenia obiektów klasy poza samą klasą.

  2. 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.

Teraz wady. Wymienimy czynniki, które stawiają implementację w złym świetle:
  • 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

Cons:
  • Brak leniwej inicjalizacji.
Próbując naprawić poprzednią wadę, otrzymujemy implementację numer dwa:

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.

Cons:
  • Nie jest bezpieczny dla wątków

Ta realizacja jest ciekawa. Możemy leniwie inicjować, ale straciliśmy bezpieczeństwo wątków. Bez obaw — synchronizujemy wszystko w implementacji numer trzy.

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

Cons:
  • Słaba wydajność wielowątkowa

Doskonały! W realizacji numer trzy przywracamy bezpieczeństwo wątków! Oczywiście jest to powolne... Teraz metoda getInstance jest zsynchronizowana, więc może być wykonywana tylko przez jeden wątek naraz. Zamiast synchronizować całą metodę, tak naprawdę musimy zsynchronizować tylko jej część, która inicjuje nową instancję. Ale nie możemy po prostu użyć zsynchronizowanego bloku do opakowania części odpowiedzialnej za utworzenie nowej instancji. Takie postępowanie nie zapewniłoby bezpieczeństwa nici. To wszystko jest trochę bardziej skomplikowane. Prawidłową synchronizację można zobaczyć poniżej:

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

Cons:
  • Nieobsługiwane we wcześniejszych wersjach Javy poniżej 1.5 (użycie słowa kluczowego volatile zostało naprawione od wersji 1.5)

Należy pamiętać, że aby ta opcja implementacji działała poprawnie, musi być spełniony jeden z dwóch warunków. Zmienna INSTANCE musi być ostateczna lub ulotna . Ostatnią implementacją, którą dziś omówimy, jest posiadacz klasy singleton .

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.

Cons:
  • 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 .

Ta implementacja jest prawie idealna. Jest leniwy, bezpieczny dla wątków i szybki. Ale ma niuans, jak wyjaśniono na liście wad. Porównanie różnych implementacji wzorca singleton:
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:
  1. Gwarantuje, że zawsze będzie tylko jedna instancja klasy.

  2. Zapewnia pojedynczy punkt globalnego dostępu do tej instancji.

Jednak ten wzór ma wady:
  1. Singleton narusza zasadę pojedynczej odpowiedzialności: oprócz swoich bezpośrednich obowiązków, klasa singleton kontroluje również liczbę instancji.

  2. Zależność zwykłej klasy od singletona nie jest widoczna w kontrakcie publicznym klasy.

  3. Zmienne globalne są złe. Ostatecznie singleton zamienia się w potężną zmienną globalną.

  4. Obecność singletonu zmniejsza testowalność aplikacji jako całości, aw szczególności klas korzystających z singletonu.

I to wszystko! :) Omówiliśmy z Tobą klasę Java Singleton. Teraz do końca życia, rozmawiając ze znajomymi programistami, możesz wspomnieć nie tylko o tym, jak dobry jest wzorzec, ale także kilka słów o tym, co czyni go złym. Powodzenia w opanowaniu tej nowej wiedzy.

Dodatkowa lektura:

Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION