Tutorials

Design Patterns - Singleton and Singleton Registry

Views: 40081
Rating: 5/5
Votes: 2

Introduction

The Singleton pattern is probably the most famous, or should I say infamous, software Design Pattern in existence. If you have read OO PHP Part 2, possibly you will know why it is so hated by some after reading about its implementation, in any case, I'll discuss it briefly later in this chapter.

The Singleton pattern ensures that you are always dealing with the same, single instance, wherever in your application.

The Registry pattern usually utilizes the Singleton pattern (hence “Singleton Registry”) to make the same ‘globalness’ apply to objects who’s classes weren’t necessarily designed to, as well as to provide a central access point for often reused objects.

But in it's purest form, as simply a way for clients to find common objects, a Registry doesn't need to be Singleton. Use of Registry objects local to a specific part, layer or domain of an application is common as well. In this function, a Registry is little more than a well-known hash map to store references to common objects. We won't be going into that right now. It's pretty straight forward anyway.

Problem

  1. How to ensure that only a single instance of a class is available, and make it easily accessible?
  2. How to create a well-known service to fetch commonly used objects from, while avoiding excessive passing of objects?

Solution

The Singleton pattern uses the following approach to ensure only one instance can exist:

  1. Access to the constructor is restricted.
  2. A static instance is kept in the class scope.
  3. A method for retrieving the instance, setting it if it doesn't exist yet, is provided.

Additionally, PHP allows you to declare the magic method “__clone()” private, which prevents the object from being cloned.

Example of a typical PHP Singleton:

/**
 * A typical Singleton in PHP
 *
 */
class Singleton 
{	
	/**
	 * Static instance
	 * 
	 * @var SingletonRegistry
	 */
	private static $_instance;
	
	/**
	 * Private constructor
	 *
	 */
	private function __construct()
	{}
	
	/**
	 * Get the single instance
	 *
	 * @return Singleton
	 */
	public static function getInstance()
	{
		if(self::$_instance === null)
		{
			//First and only construction.
			self::$_instance = new self();
		}
		return self::$_instance;
	}
	
	/**
	 * Disallow cloning
	 *
	 */
	private function __clone()
	{}
}

$singleton = Singleton::getInstance();

Because the instance can be obtained statically, it is globally available, making it a convenient way to store objects that are constantly needed throughout your application. It is also impossible to create more than one instance of the class, useful in some cases, a hindrance in other. For example, I see a lot of people making database connection wrapper classes Singletons. While it's perfectly possible that you'll need two connections, to different servers for example.

The Singleton solution has the serious drawback of causing 'static coupling' (see part 2 – good practices), which is why it is so hated by some. Yet, you get what you want: easily accessible makes it easy to couple different components. But with a little common sense and a touch of discipline, you can limit coupling to objects that are actually related to the Singleton, albeit in a different part of your application. In general, try to avoid using Singletons, just be aware that this solution exists.

If all you want to do is ensure that only one instance of a class exists, but you do not want the instance to be statically available, using a static flag is a better option. Say I only wanted to allow one Cache object (whatever the implementation):

/**
 * Abstract Cache supertype
 *
 */
abstract class Cache
{
	/**
	 * Instantiation flag
	 *
	 * @var bool
	 */
    private static $instantiated = false;
    
    /**
     * Constructor
     * 
     * @final
     * @return void
     *
     */
    final public function __construct()
    {
        if(self::$instantiated)
        {
            throw new Exception('There can be only one cache.');

        }
        $this->init();
        
        self::$instantiated = true;
    }
    
    /**
     * Initialization hook for child classes
     *
     */
    abstract public function init();
}

Using this approach, only one implementation of Cache may be instantiated. Let's look at a simple implementation of a Singleton Registry.

/**
 * A simple Singleton Registry in PHP
 *
 */
class SingletonRegistry
{	
	/**
	 * Static instance
	 * 
	 * @var SingletonRegistry
	 */
	private static $_instance;
	
	/**
	 * Object hash map
	 *
	 * @var array
	 */
	private $_map;
	
	/**
	 * Private constructor
	 *
	 */
	private function __construct()
	{}
	
	/**
	 * Get the single instance
	 *
	 * @return SingletonRegistry
	 */
	public static function getInstance()
	{
		if(self::$_instance === null)
		{
			//First and only construction.
			self::$_instance = new self();
		}
		return self::$_instance;
	}
	
	/**
	 * Get an object by key
	 *
	 * @param string|int $key
	 * @return object
	 */
	public function get($key)
	{
		return $this->_map[$key];
	}
	
	/**
	 * Set an object by key
	 *
	 * @param string|int $key
	 */
	public function set($key, $object)
	{
		return $this->_map[$key] = $object;
	}
	
	/**
	 * Disallow cloning
	 *
	 */
	private function __clone()
	{}
}

It's mostly the same as the Singleton example, wrapped around a basic hash map with key based getting and setting. Many people prefer a Singleton Registry over multiple Singletons. In the past, I have been an advocate of this as well, on the argument that it is better to have your globally accessible objects in one place, rather than “floating around”. It sounds like a strong argument, but in time I've come to realize that it is a mute point. A client that fetches an object from a Singleton Registry, is not just coupled to the Registry, it is coupled to the object it fetches as well (it doesn't just fetch it to look how pretty it is). To make matters worse, if the key is chosen by a class different than the “registrant”, we actually make the client dependent on that class well (“maybe today I'll call this object “foo” instead of “bar”).

As a result I strongly discourage the use of a Singleton Registry.