Czym jest singleton?
Singleton od słowa single czyli pojedynczy.
Wyobraźmy sobie, że chcemy stworzyć świat w którym żyjemy. Nie chcemy przecież, żeby ktoś z zewnątrz, bądź my przez naszą nieuwagę skonstruowali kolejny, bo jak miałoby to działać? Gwarantujemy, żeby wszyscy użytkownicy (inni programiści) operowali na jednym i tym samym świecie, tworząc w momencie wywołania obiekt klasy ,,World”, a jeśli taki już wcześniej istniał zwracamy świat z powrotem zamiast tworzyć nowy po raz kolejny.
Implementacja
Na potrzeby przykładu korzystać będę z języka Java.
Nasz punkt startowy czyli klasa Main:
package application;
public class Main {
public static void main(String[] args) {}
}Tworzymy klasę którego obiekt będzie reprezentował nasz świat:
package application.singleton;
public class World {
private int worldSize = 400;
private static World instance;
private World() {
}
}Dodajemy zabezpieczenia przed stworzeniem kolejnej instancji klasy świat:
package application.singleton;
public class World {
private int worldSize = 400;
private static World instance;
private World() {
}
public static World getInstance() {
if(instance == null) {
synchronized (World.class) {
if(instance == null) {
instance = new World();
}
}
}
return instance;
}
}Wyjaśniając pojawienie się synchronized – może dojść do sytuacji gdy następują dwa wywołania getInstance() w bliskich odstępach czasu i oby dwie instrukcje przejdą warunek if() tworząc tym samym dwie instancję kompletnie zatracając sens istnienia singletona. Dlatego użyliśmy synchronized z zagnieżdżonym warunkiem sprawdzającym uniemożliwiając, aby do tego doszło.
W mainie natomiast końcowo wygląda to w sposób następujący:
package application;
import path.World;
public class Main {
public static void main(String[] args) {
World world = World.getInstance();
}
}Czy da się zrobić prościej?
Jasne że tak:
package application.singleton;
public class World {
private int worldSize = 400;
private static World instance = new World();
private World() {
}
public static World getInstance() {
return instance;
}
}Już w momencie wczytywania klasy przez class-loader zostaje utworzony nasz świat który będzie zwyczajnie zwracany dla użytkowników, nie potrzebujemy wtedy martwić się o warunki sprawdzające.
Zalety:
- Jeden z prostszych wzorców projektowych
- Zabezpiecza przed stworzeniem wielu instancji danej klasy
- Łatwo rozpoznawalny przez innych programistów
Wady:
- Nie zabezpiecza w momencie gdy jest więcej niż jeden class-loader (wtedy należy skorzystać z implementacji opartej o enum)
- tzw. ukrywanie zależności