Singleton

Najmniejszy wzorzec projektowy, składa się tylko z jednej klasy. Ale tak jak inne wzorce wymaga przemyślanego użycia.
Singleton = jedna instancja obiektu, globalny punkt dostępu do tej instancji

Singleton — najprostsza wersja — to nie znaczy że najlepsza

package eu.saramak.wersja1;
public class Singleton
{
//Prywatna zmienna statyczna
private static Singleton instance;
//prywatny bezargumentowy konstruktor
private Singleton()
{ }

//
public static Singleton getInstance(){
if (instance==null)
{ instance = new Singleton();
}
return instance;
}
}

Rozpoznać singletona jest bardzo prosto, składa się z jednej klasy, która posiada:

  1. Prywatny konstruktor domyślny. Po to żeby nikt nie utworzył nową instancję naszego Singletona po przez new Singleton()
  2. prywatną zmienną statyczną, referencję do instancji naszego singletona.
  3. metodę, która zwraca singletona (referencję do instancji)

Ta wersja wzorca singleton jest bardzo często używana, ale nie jest najlepszym rozwiązaniem, posiada wady.
Np. nie obsługuje wielowątkowości.
Jeśli mamy dwa wątki i w pewnym momencie oba odwołają się do metody getInstance(), to będzie problem.
Prześledźmy go:

  1. Aplikacje się uruchamia i tworzy dwa wątki T1, T2
  2. T1 i T2 wywołuje metodę Singleton.getInstance() w tym samym momencie.
  3. T1 sprawdza warunek instance==null oraz T2 sprawdza ten sam warunek.
  4. Oba sprawdzenia dają wynik true co powoduje utworzenie dwóch instancji :(

To spowoduje bardzo, trudny błąd do wychwycenia, błąd będzie psuł działanie aplikacji i co najgorsze będzie występował rzadko, a jak wiemy najtrudniejszy błąd do wychwycenia to ten, który pojawia się raz na 100 uruchomień aplikacji.

Można spróbować, zawalczyć z tym problemem tworząc instancję singletona, na początku uruchomienia aplikacji. Tzn.

package eu.saramak.wersja2;
public class Singleton
{
//Prywatna zmienna statyczna
private static Singleton instance = new Singleton();
//prywatny bezargumentowy konstruktor
private Singleton()
{ }

//
public static Singleton getInstance(){
return instance;
}
}

Teraz już nie trzeba się martwić o wielowątkowość, metoda getInstance(), nie sprawdza żadnego warunku i nie tworzy instancji obiektu.
Ale ta metoda też ma swoje wady. Bo co jeśli utworzenie obiektu Singleton jest bardzo kosztowne ( np. w konstruktorze jest nawiązywane połączenie z 20 bazami danych), i wiemy że użytkownik, będzie korzystał z naszego obiektu, tylko raz na 1000 uruchomień.

Wtedy możemy zawalczyć o obsługę wielowątkowości i pozbyć się naszego problemu ciężkiego konstruktora tą metodą:

package eu.saramak.wersja3;
public class Singleton {
private static Singleton instance;
private Singleton() {
}

public static synchronized Singleton getInstance(){
if (instance==null)
{
instance= new Singleton();
}
return instance;
}
}

Słowo kluczowe synchronized — chroni metodę przed dostępem więcej niż jednego wątku jednocześnie.
Dzięki temu rozwiązaniu, nawet jeśli mamy ciężki konstruktor, to i tak instancja zostanie utworzona tylko wtedy gdy będzie naprawdę potrzebna, a dzięki synchronizowaniu metody jesteśmy pewni, że nasza funkcja utworzy jedną i tylko jedną instancję. Czyż nie pięknie.

Nie. :)
Bo utrzymanie synchronizacji kosztuje !!.
Z pomocą przychodzi:
Double-checked locking — metoda podwójnego blokowania.

package eu.saramak.wersja4;
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
}

public static Singleton getInstance(){
if (instance==null)
{
synchronized (Singleton.class) {
if (instance==null) {
instance = new Singleton();
}
}
}
return instance;
}
}

W metodzie getInstance(), korzystamy z synchronizacji tylko, gry ona jest nam potrzebna, czyli podczas tworzenia instancji.
Czy ta metoda ma jakąś wadę ? Tak.
Ponieważ słowo kluczowe volatile ma inną, implementację w javie 1.4 w dół, to metodę tą można używać tylko w javie 1.5 i wyżej. (ale to już chyba mały problem).

I na koniec jeszcze gratka, szablon do eclipsa:
Instalacja:
Window->Preferences:

A teraz import i wybieramy plik singleton4.xml

lub tworzymy szablon i wklejamy:

public class ${elemType} {
private static volatile ${elemType} ${field};
private ${elemType}() {
}
public static ${elemType} get${field}(){
if (${field}==null)
{
synchronized (${elemType}.class) {
if (${field}==null) {
${field} = new ${elemType}();
}
}
}
return ${field};
}
}

Jak widać metoda getinstance, nie ma zachowanych garbów, jeśli ktoś wie jak zrobić pierwszą literę większą niech da znać. Mam nadzieje że za tydzień opiszę wzorzec: Obserwator

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.