Singleton – konstrukcyjny wzorzec projektowy którego zadaniem jest ograniczenie ilości instancji obiektu w systemie oraz zapewnienie globalnego dostępu do stworzonego obiektu.
Wydawało by się, że prosto jest tylko stworzyć obiekt, ale ten wzorzec ma swoje zalety:
- Tworzenie instancji klasy jest niewidoczne dla użytkownika
- Lazy loading – obiekt jest tworzony kiedy następuje jego próba pierwszego użycia
- Klasa zaimplementowana jako singleton może kontrolować liczbę swoich instancji w systemie, jednak głównym założeniem tego wzorca jest to, że w systemie istnieje tylko jedna instancja tej klasy
i wady:
- Mała elastyczność – w kodzie ustalamy ilość instancji klasy
- Wielowątkowość – w środowisku wielowątkowym może dojść do sytuacji, że zostaną utworzone dwie instancje klasy zamiast jednej. W przykładach pokażę rozwiązanie tego problemu poprzez zastosowanie: synchronizacji, worzenia obiektu z wyprzedzeniem, podwójne blokowanie.
- ClassLoadery – w przypadku języka java wraz z używaniem wielu classLoaderów może dojść do powstanie wielu instancji klasy
- Utrudnione testowanie – wprowadza globalny obiekt
Zastosowanie tego wzorca:
- Globalne obiekty konfiguracyjne
- Globalne fabryki obiektów lub loadery danych
Z doświadczenia, tego wzorca trzeba używać z głową, zbyt częste używanie go może delikatnie popsuć aplikację. Ja jakoś wolę stosować klasy wyposażone w statyczne metody, ale niektórzy programiści powiedzą ze „to właśnie jest złe rozwiązanie”
Przykładowa implementacja:
Java, przedstawione 4 implementacje gdzie mamy próbę rozwiązań problemów z wielowątkowością:
- Prosta implementacja
- Synchronizacja
- Tworzenie obiektu z wyprzedzeniem
- Podwójne blokowanie
package pl.shad.net.blog; /** * Prosta implementacja wzorca Singleton */ class Singleton{ private static Singleton singletonInstance = null; private Singleton(){ } public static Singleton getInstance(){ if (singletonInstance == null){ singletonInstance = new Singleton(); } return singletonInstance; } } /** * Implementacja wzorca Singleton * z synchronizacją wątków, zasbnożerna przy każdym wyłowaniu * metody getInstance() następuje synchronizacja zasobu */ class SingletonSynchronized{ private static SingletonSynchronized singletonInstance = null; private SingletonSynchronized(){ } public static synchronized SingletonSynchronized getInstance(){ if (singletonInstance == null){ singletonInstance = new SingletonSynchronized(); } return singletonInstance; } } /** * Implementacja wzorca Singleton * z tworzeniem obiektu z wyprzedzeniem */ class SingletonPreLoading{ private static SingletonPreLoading singletonInstance = new SingletonPreLoading(); private SingletonPreLoading(){ } public static SingletonPreLoading getInstance(){ return singletonInstance; } } /** * Implementacja wzorca Singleton * z synchronizacją wątków opartą na podówjnym sprawdzaniu */ class SingletonDoubleCheckLocking{ private static SingletonDoubleCheckLocking singletonInstance; private SingletonDoubleCheckLocking(){ } public static SingletonDoubleCheckLocking getInstance(){ if (singletonInstance == null){ synchronized (SingletonDoubleCheckLocking.class){ singletonInstance = new SingletonDoubleCheckLocking(); } } return singletonInstance; } } /** * Klasa testująca */ public class DesignPatternsSingleton{ private SingletonSynchronized synchronizedTest1; private SingletonSynchronized synchronizedTest2; private SingletonSynchronized synchronizedTest3; private SingletonSynchronized synchronizedTest4; private SingletonPreLoading preLoad1; private SingletonPreLoading preLoad2; private SingletonPreLoading preLoad3; private SingletonPreLoading preLoad4; private SingletonDoubleCheckLocking doubleLock1; private SingletonDoubleCheckLocking doubleLock2; private SingletonDoubleCheckLocking doubleLock3; private SingletonDoubleCheckLocking doubleLock4; public static void main(String[] args){ DesignPatternsSingleton dpS = new DesignPatternsSingleton(); dpS.singleTest(); dpS.synchronizedTest(); dpS.preLoadTest(); dpS.doubleLockTest(); } /** * */ private void singleTest(){ Singleton singleSingleton = Singleton.getInstance(); if (singleSingleton instanceof Singleton){ System.out.println("Create single Singleton ... OK ..."); } else { throw new IllegalStateException("Single Singleton"); } } /** * */ private void synchronizedTest(){ new Thread(new Runnable(){ @Override public void run(){ synchronizedTest1 = SingletonSynchronized.getInstance(); } }).run(); new Thread(new Runnable(){ @Override public void run(){ synchronizedTest2 = SingletonSynchronized.getInstance(); } }).run(); new Thread(new Runnable(){ @Override public void run(){ synchronizedTest3 = SingletonSynchronized.getInstance(); } }).run(); new Thread(new Runnable(){ @Override public void run(){ synchronizedTest4 = SingletonSynchronized.getInstance(); } }).run(); try{ Thread.sleep(1000); if ( synchronizedTest1 instanceof SingletonSynchronized && synchronizedTest1 == synchronizedTest2 && synchronizedTest2 == synchronizedTest3 && synchronizedTest3 == synchronizedTest4 ){ System.out.println("Create synchronized Singleton ... OK ..."); } else { throw new IllegalStateException("Synchronized Singleton"); } // System.out.println("synchronizedTest1: " + synchronizedTest1 // + " synchronizedTest2: " + synchronizedTest2 // + " synchronizedTest3: " + synchronizedTest3 // + " synchronizedTest4: " + synchronizedTest4); } catch (InterruptedException e){ e.printStackTrace(); } } /** * */ private void preLoadTest(){ new Thread(new Runnable(){ @Override public void run(){ preLoad1 = SingletonPreLoading.getInstance(); } }).run(); new Thread(new Runnable(){ @Override public void run(){ preLoad2 = SingletonPreLoading.getInstance(); } }).run(); new Thread(new Runnable(){ @Override public void run(){ preLoad3 = SingletonPreLoading.getInstance(); } }).run(); new Thread(new Runnable(){ @Override public void run(){ preLoad4 = SingletonPreLoading.getInstance(); } }).run(); try{ Thread.sleep(1000); if ( preLoad1 instanceof SingletonPreLoading && preLoad1 == preLoad2 && preLoad2 == preLoad3 && preLoad3 == preLoad4 ){ System.out.println("Create preLoad Singleton ... OK ..."); } else { throw new IllegalStateException("PreLoad Singleton"); } // System.out.println("preLoad1: " + preLoad1 // + " preLoad2: " + preLoad2 // + " preLoad3: " + preLoad3 // + " preLoad4: " + preLoad4); } catch (InterruptedException e){ e.printStackTrace(); } } /** * */ private void doubleLockTest(){ new Thread(new Runnable(){ @Override public void run(){ doubleLock1 = SingletonDoubleCheckLocking.getInstance(); } }).run(); new Thread(new Runnable(){ @Override public void run(){ doubleLock2 = SingletonDoubleCheckLocking.getInstance(); } }).run(); new Thread(new Runnable(){ @Override public void run(){ doubleLock3 = SingletonDoubleCheckLocking.getInstance(); } }).run(); new Thread(new Runnable(){ @Override public void run(){ doubleLock4 = SingletonDoubleCheckLocking.getInstance(); } }).run(); try{ Thread.sleep(1000); if ( doubleLock1 instanceof SingletonDoubleCheckLocking && doubleLock2 == doubleLock1 && doubleLock3 == doubleLock2 && doubleLock4 == doubleLock3 ){ System.out.println("Create doubleLock Singleton ... OK ..."); } else { throw new IllegalStateException("DoubleLock Singleton"); } // System.out.println("preLoad1: " + preLoad1 // + " preLoad2: " + preLoad2 // + " preLoad3: " + preLoad3 // + " preLoad4: " + preLoad4); } catch (InterruptedException e){ e.printStackTrace(); } } } |
PHP:
< ?php class Singleton{ private static $singletonInstance; // Zapobieganie tworzeniu obiektu z zwenąrz klasy private function __construct(){ } private function __clone(){ } /** * @return Singleton */ public static function getInstance(){ if (self::$singletonInstance === null){ self::$singletonInstance = new Singleton(); } return self::$singletonInstance; } } // // Testowanie tworzenia // $singleton = Singleton::getInstance(); if ($singleton instanceof Singleton){ echo "create Singleton ... OK"; } else { echo "create Singleton ... Error"; } |
1 Pingback