Singleton jest wzorcem konstrukcyjnym, który nie pomaga przy tworzeniu obiektów lecz wręcz odwrotnie – przeszkadza. Jego zadaniem jest tworzenie obiektu, który jest tylko w jednej instacji w całej aplikacji. Często bywa że w aplikacji potrzeba coś jedynego w swoim rodzaju, np. kolejka drukowania lub zarządzanie połączeniami z bazą danych. Do tych celów nadaje się wzorzec Singleton.
Singleton posiada konstruktor prywatny co zapobiega przypadkowemu jego wywołaniu, referencję do obiektu otrzymujemy przez wywołanie statycznej metody getInstance().
Klasa wzorca singleton wygląda tak:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class Singleton { private static Singleton ourInstance; public static Singleton getInstance() { if (ourInstance == null) { ourInstance = new Singleton(); } return ourInstance; } private Singleton() { } //inne metody } |
Przez niektórych ten wzorzec jest uznawany za antywzorzec – jest tak ponieważ Singleton w niektórych okolicznościach może działać niezgodnie z zamierzeniem.
Pierwszym takim przypadkiem jest środowisko wielowątkowe, które może spowodować równoległy dostęp do obiektu przez dwa wątki. Aby zapobiec utracie danych musimy zaopatrzyć metodę getInstance() w słowo kluczowe synchronized
.
Może to trochę spowolnić dostęp do Singletona, ale zabezpieczy go przez środowiskiem wielowątkowym.
Innym sposobem jest oznaczenie zmiennej ourInstance jako finalna podczas wywoływania.
Drugim przypadkiem jest wywołanie metody przez kilka obiektów z różnych ClassLoaderów. W środowisku serwerowym kilka instancji ClassLoaderów jest zjawiskiem normalnym, więc gdy Singleton ma działać powinniśmy zabezpieczyć się w taki sposób, aby ładować Klasę Singletona zawsze z tego samego ClassLoadera. Nie jest to łatwe zadanie ale możliwe do zrealizowania, np w ten sposób:
1 2 3 4 5 6 7 8 |
private static Class getClass(String classname) throws ClassNotFoundException { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); if (classLoader == null) classLoader = Singleton.class.getClassLoader(); return (classLoader.loadClass(classname)); } |
Trzecim problemem który może zakłócić funkcje Singletona może być klonowanie jego. Jest to możliwe gdy implementuje interfejs Cloneable (lub dziedziczy po klasie, która go implementuje). W takiej sytuacji musimy nadpisać metodę clone(), która nie pozwoli na klonowanie Singletona.
.