At the recent PHP UK Conference in London, one speaker described the Singleton Pattern as being evil. The main point brought up was that it brought global variables in through the back door, including all the problems associated with them.
Well, this is true, but only to a point. Just like eating ice-cream or junk food all the time would be bad for you, so would relying on the Singleton Pattern. However, it is still a very good pattern when used in specific instances, and when used sparingly, with discipline.
So, when could we consider using it?
A Preferences Class
Let’s imagine we’ve got a system that can be configured, and it has a Preferences
class that stores all the settings we require. These settings could be anything from database connection strings and configuration switches, to common URL’s and paths.
Ideally, we want our application to use the same Preferences
object, so simply typing $pref = new Preferences()
whenever we needed it is not good enough, because that won’t carry the data around that we need, or the data that was changed elsewhere in your application. We need some other mechanism.
Now, typically, if you wanted this available throughout your system, you would have to create the object once, and then physically pass it around to those objects that needed it. So, for example, assuming we’ve created an object somewhere called $pref
:
$someClass = new SomeClass( $pref );
But this is cumbersome. Every time you need to access your preferences, you’d need to modify the calling class to accept the Preferences
object.
Worse, this introduces coupling: you could be forced to make classes accept this Preferences
class even though they don’t use it themselves, because they need to then pass it on to an object that class uses.
Alternatively, you could consider using a global variable to store the object, but this introduces another problem where the global variable can be overwritten at any time.
So, in summary, ideally our Preferences
object:
- needs to be available throughout our system;
- must not be stored in a global constant and be overwritten
- should only ever be instantiated once.
Enter the Singleton Pattern. Let’s see some code.
A Simple Singleton Pattern
Here’s an example of a very simple example of a Singleton Pattern:
<?php class Preferences { private $arrProperties=array(); private static $objInstance=null; private function __construct() { } public function setProperty( $key, $val ) { $this->arrProperties[$key] = $val; } public function getProperty( $key ) { return $this->arrProperties[$key]; } public static function getInstance() { if ( empty( self::$objInstance ) ) { self::$objInstance = new self(); } return self::$objInstance; } } ?>
Okay, so what’s going on here?
First, you’ll notice that the __construct()
is set to private
. This means you cannot instantiate the class using the new
declaration i.e. $pref = new Prefereces();
The only way to instantiate this class is by calling the static method, getInstance()
, like this: $pref = Preferences::getInstance();
Now, if you look at Preferences::getInstance()
, you’ll see that it checks whether or not self::$objInstance
has been set. If it has, it returns that object. If it hasn’t, it creates a new object of itself, and stores it into self::$objInstance
. It then returns that.
So, essentially, what you’re doing is making sure this object is only ever created once. No matter where you call $pref = Preferences::getInstance();
, it will always return the same object to you.
As an added benefit, we’ve made sure that it can never be over written.
And, finally, this is how we would use it:
$pref = Preferences::getInstance(); $pref->setProperty( "maxPages", 10 ); $pref->setProperty( "db", "database" ); $pref->setProperty( "user", "someuser" ); echo $pref->getProperty( "user" );
It’s worth mentioning that you could be extremely strict and make the setProperty()
and getProperty()
methods private
, and then create get
and set
methods to access specific parameters. A possible example would be something like this added to your Preferences
object:
public function setDb( $val ) { $this->setProperty( "db", $val ); } public function getDb( ) { return $this->getProperty( "db" ); }
Other Useful Uses
There is another reason why this pattern is useful, and that’s because it can be used to store objects that can then be reused throughout your system.
So, let’s consider a class ObjectRegistry
, which uses the Singleton Pattern, and will store objects and make them available to other parts of the system.
For example, we may have a User
object that stores details about the logged in user, and you want it available everywhere in your system. We could then use the Singleton Method to achieve this:
$objs = ObjectRegistry::getInstance(); $objs->setProperty( "user", new User( "craig" ) );
I’ve now given my system the ability to re-use common objects that I only want created once, rather than having to pass them around, by simply doing the following:
$objs = ObjectRegistry::getInstance(); $user = $objs->getProperty( "user" );
But, let’s not talk of benefits. What about the drawbacks?
Drawbacks
Now, as I’ve said before, this method should only be used in very specific cases. It does have most, if not all, the problems associated with global variables. The article I’ve linked to is very good and worth reading to understand the drawbacks of global variables, but the two essential points to understand in relation to the Singleton Pattern are:
- Singleton properties can be set and over-written anywhere in your system. This is a problem because you can struggle to find where a particular value was set. You can mitigate this problem in the case of a
Preferences
object by making sure the class has nopublic
set methods, and instead all values are read from a config file somewhere in your system. However, for other uses, they can likely be set from anywhere. - There are no real constraints to using a Singleton class. You can’t restrict its usage to one particular part of your system so, even though you may be disciplined in how and where you use it, there is no way to enforce this usage. This can also be a security problem.
In Summary
While the Singleton Pattern has a number of drawbacks you should be aware of, there are some benefits. Used in moderation, and with discipline, it can be a valuable asset to your code toolbox.