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";
}