php goodness

Singleton (and multiton) with a different approach

Posted in Long by phpgoodness on July 21, 2010

Is this familiar to you?

class SingletonClass
{
	protected static $instance = null;

	protected function __construct()
	{
		// ...
	}

	final protected function __clone()
	{
	}

	public static function getInstance()
	{
		$classname = __CLASS__;
		if ( !self::$instance || !self::$instance instanceof $classname )
		{
			self::$instance = new self();
		}

		return self::$instance;
	}
}

Yes, the good old Singleton in the classic implementation; static getInstance, protected (maybe final) constructor (and clone magic method), and storing the only-one instance within the class. And there is, of course, the Multiton as well (what a lame name anyway), which normally has very similar implementation.

I personally don’t always prefer coding Singleton like this.

Somehow this logic doesn’t belong to what the class should do, the class should only take care about its job and no more. Also, imagine yourself one day waking up and deciding to change an existing class of yours to Singleton (or a Singleton class to non-singleton). It’s gonna be a tough going. And third, it’s against the DRYYI methodology – that is, Don’t Repeat Yourself You Idiot.

So what do I do?

If you have ever worked with PHP frameworks (or mayhap developed one) you are going to be familiar to the concept of not using new keyword, instead, you use a special method to instantiate an object. In our case, there will be a class that takes care of instantiating objects based on their type.

The goal is something like this:

$singleton_1 = Object::get( 'SomethingSingleton', $param_1, $param_2 );
$singleton_2 = Object::get( 'SomethingSingleton', $param_1, $param_2 );

$normal_1 = Object::get( 'SomethingNormal', $param_1, $param_2 );
$normal_2 = Object::get( 'SomethingNormal', $param_1, $param_2 );

And that’s all. We don’t have to care about anything when we get the instance of an object. In the example above the two singleton variables will refer to the same instance of ‘SomethingSingleton’, and the two normal variables will point to two different instances accordingly.

To “mark” a class as Singleton we are going to use interfaces. The cool thing about interfaces is that they can be used as “tags” or “labels” for a class indicating one certain behaviour.

/**
 * Singleton interface used as "tag" to mark singleton classes.
 */
interface Singleton
{
}

/**
 * Multiton interface used as "tag" to mark singleton classes.
 */
interface Multiton
{
	/**
	 * GetFace in the class should return a scalar that is used to differentiate between class instances.
	 * 
	 * For example: database names for DB classes.
	 * It is called with the construct parameters of the class.
	 */
	public static function getFace();
}

So we defined our interfaces, one for Singleton, one for Multiton. More on the getFace later. Now let’s see our …

Magic class

/**
 * Basic object defining object instantiating processes.
 */
class Object
{
	/**
	 * We store the object references of singleton classes here.
	 *
	 * Class name is the key, the object pointer is the value.
	 *
	 * @var array
	 */
	static $singletons = array();

	/**
	 * We store the object references of multiton classes here.
	 *
	 * Class names are the keys, the values are sub-arrays containing different instances (values) for diffferent "faces" (keys).
	 * For example:
	 * array(
	 * 	'Database' => array(
	 *		'db_server_1' => $db_object_for_server_1,
	 *		'db_server_2' => $db_object_for_server_2,
	 *	),
	 *	'AnotherCclass' => array()
	 * );
	 *
	 * @var array
	 */
	static $multitons = array();

	/**
	 * Method to instantiate objects in general.
	 *
	 * The logic decides if the class is a singleton, multiton or normal object,
	 * and returns instance accordingly.
	 * Passing additional parameters will cause the object constructors to be
	 * called with these parameters. It only occurs, when the the method needs
	 * to create a new instance.
	 *
	 * @param string $class_name The class name to intantiate the object from.
	 * @return object The new or already existing instane of the object.
	 */
	static function get( $class_name )
	{
		// If the class is in the singleton array, we are sure that we don't need another instance.
		if ( isset( Object::$singletons[$class_name] ) )
		{
			return Object::$singletons[$class_name];
		}

		// Parameters to call constructor (in case) and multiton's getFace (in case) with.
		$construct_params = func_get_args();
		array_shift( $construct_params );

		// If the class is in the multiton array with the same "face" scalar, we don't need another instance.
		if
		(
				isset( Object::$multitons[$class_name] )
				&&
				isset( Object::$multitons[$class_name][$multiton_param_processed = call_user_func_array( array( $class_name, 'getFace' ), $construct_params )] )
		)
		{
			return Object::$multitons[$class_name][$multiton_param_processed];
		}

		// Here's the point where we have to instanceate the class anyway.
		$reflection = new ReflectionClass( $class_name );
		
		if ( null === $reflection->getConstructor() )
		{
			// If the object doesn't have constructor, we just instantiate it.
			// It might be quicker to use - new $class_name -.
			$new_instance = $reflection->newInstance();
		}
		else
		{
			// If the object does have constructor, we pass the additional parameters we got in thos method.
			$new_instance = $reflection->newInstanceArgs( $construct_params );
		}

		// If it's a singleton, we have to keep track of it.
		// If might be quicker to use - $new_instance instanceof Singleton -.
		if ( $reflection->isSubclassOf( 'Singleton' ) )
		{
			return Object::$singletons[$class_name] = $new_instance;
		}
		// If it's a multiton, we have to keep track of it with its "face".
		// If might be quicker to use - $new_instance instanceof Singleton -.
		if ( $reflection->isSubclassOf( 'Multiton' ) )
		{
			if ( !isset( $multiton_param_processed ) )
			{
				$multiton_param_processed = call_user_func_array( array( $class_name, 'getFace' ), $construct_params );
			}
			return Object::$multitons[$class_name][$multiton_param_processed] = $new_instance;
		}
		return $new_instance;
	}
}

Okay, everybody hates reading someone else’s code, so…

Let’s do it together

We have two object pools defined as static properties of the class. The first will serve as storage for Singleton objects, the second (guess what?) for Multitons. In both cases the array keys for the pool will be the class name. For Object::$multitons we also define a sub-hierarchy where the keys are the “faces” of the multiton objects. By “faces” I mean a scalar that identifies an object instance and makes it exclusive.

When the Object::get method executes, first it checks for the class name in the singleton pool. If it’s there, it means that we already created in once, and that it’s singleton, so we can just return it and it’s the end of the story.

It’s a bit more complicated for the multiton pool. First it looks up for the class name just like for the Singleton, but if it founds it, it needs to look for the class “face” as well in the sub-hierarchy. Here is when the getFace steps to the stage.

The implemented getFace method of our multiton class is getting called with all the parameters of the Object::get, except for the first one, which is the class name. This static method of the multiton should define the “face” of the object, that is, a scalar used to mark its different instances. We are going to see later, that the same parameter set is used to construct the object (if needed).

If the face returned by getFace is found in the multiton pool, the according object instance gets returned.

Are you still with me?

I hope so. If you reached this point in the method, it means that you have to construct the new object instance (because even if the class is singleton or multiton, it doesn’t have its first instance). To do this, we are going to use the reflection abilities of PHP, which is pretty cool though not very well-known.

How does it work? We create a reflection object for our class so thus with this object we will be able to get some information about the code of the class, and do some operations on it. Like, in our case, we first ask the reflection object if the class has any constructor (yes, it works for the ancestor classes too). If so, we call the constructor with the parameters passed to Object::get (except the first, eh). If there’s no constructor, we simply create a new instance of the object.

OK, now we have our object instance. All we have to do is to check if it’s Singleton or Multiton (or neither), and if so, save it to the according object pool. To do this, we can use our reflection object again (though instanceof operator might be faster). Of couse, in case of multiton, we also have to calculate the “face”, if we still did not.

At the end, we just return the new object. Huh. Do you like it?

Pros and cons

The clear benefit here is that the object instantiating logic got placed into a specific class and doesn’t taint our classes and makes it all maintainable. If you use Object::get method every time you create an object, changing normal classes and singleton classes back and forth will be very easy. On the performance side, using reflection makes it a little bit expensive, but actually I did not do any benchmarking. I don’t think this means serious overhead, and reflection is widely used in some popular frameworks.

Applause!

What else? Enhancing Object class? For example, this class could be responsible for lazy-loading. Another good idea is to extend it to a lookup class for your object instances. The number of possibilities are infinite.

At last, here is an example of using getFace for those who still don’t get it.

/**
 * Database connection class implementing multiton.
 */
class DatabaseConnection implements Multiton
{
	public function __construct( $database_server, $database_user, $database_password, $database_port = 3306 )
	{
		// Do what you do.
	}
	
	public static function getFace()
	{
		// Concatenating the database host, and user.
		// This will differentiate between the instances of this class.
		return func_get_arg( 0 ) . ":" . func_get_arg( 1 );
	}
}

$connection1 = Object::get( 'DatabaseConnection', '127.0.0.1', 'mrbuzzk', 'abcdefgh' );
$connection2 = Object::get( 'DatabaseConnection', '127.0.0.1', 'mrbuzzk', 'abcdefgh', 3306 );
$connection3 = Object::get( 'DatabaseConnection', '192.168.120.1', 'mrbuzzk', 'abcdefgh', 3306 );

// Connection 1 == Connection 2.
// Connection 1 != Connection 3.
// Connection 2 != Connection 3.

2 Responses

Subscribe to comments with RSS.

  1. Singleton pattern | Honey Mercy said, on April 8, 2011 at 3:18 am

    […] Singleton and Multiton with a different approach, July 21, […]

  2. Lance E Sloan said, on June 25, 2011 at 10:56 pm

    I followed a link from the Wikipedia article about singletons to this page. I like the approach, but I bet it doesn’t work well with Eclipse word completion.


Leave a reply to Singleton pattern | Honey Mercy Cancel reply