Wzorzec projektowy – Singleton

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