Introduction
Difficulty: 2/5
Practicality: 2/5
Wikipedia Description
In software engineering, the singleton pattern is a design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system. The concept is sometimes generalized to systems that operate more efficiently when only one object exists, or that restrict the instantiation to a certain number of objects. The term comes from the mathematical concept of a singleton.
Dayle's Description
A singleton pattern is used when we want for there to only ever be a single instance of an object. Any attempts to duplicate, or to instantiate additional instances of the class should fail.
Implementation
Let's begin by investigating the problem. We'll start with a database client called Database
. Here's an example of how the class can be used.
<?php
// Create our database client.
$db = new Database;
// Configure connection parameters.
$db->setHost('127.0.0.1');
$db->setPort(3306);
$db->setUsername('root');
$db->setPassword('cuddly_binturong');
// Use our database client.
$results = $db->select('users');
Here we have a database client. First, we create an instance, next we configure all the connection parameters. Finally, we're able to make use of our database connection. It's not a real client, but the usage should seem fairly familiar.
If we'd like to use the database client in multiple places within our codebase, then we have an option of wrapping it within another class, creating a global variable, or duplicating the connection instance.
It doesn't make any sense for us to duplicate the client. We'll always have to waste time setting up the credentials again and again, and we'll be using more memory, and more TCP connections. It's simply wasteful.
To prevent this from happening, let's create a PHP implementation of a singleton for this client, shall we? Let's start with the code.
<?php
class DB
{
/**
* Our single database client instance.
*
* @var Database
*/
private static $instance;
/**
* Disable instantiation.
*/
private function __construct
{
// Private to disabled instantiation.
}
/**
* Create or retrieve the instance of our database client.
*
* @return Database
*/
public static function getInstance()
{
if (is_null(static::$instance)) {
static::$instance = new Database;
static::$instance->setHost('127.0.0.1');
static::$instance->setPort(3306);
static::$instance->setUsername('root');
static::$instance->setPassword('cuddly_binturong');
}
return static::$instance;
}
/**
* Disable the cloning of this class.
*
* @return void
*/
final public function __clone()
{
throw new Exception('Feature disabled.');
}
/**
* Disable the wakeup of this class.
*
* @return void
*/
final public function __wakeup()
{
throw new Exception('Feature disabled.');
}
}
Here's our implementation of the singleton pattern. Before we discover how the class can be used, let's run through each feature one at a time. We'll begin with the static $instance
property.
/**
* Our single database client instance.
*
* @var Database
*/
private static $instance;
We know that we want only a single instance of our database client, so we're going to need a variable to store our instance. We don't want to create instances of our DB
class, since that would also for the creation of multiple instances of our database client. Therefore we'll make the value static.
Next, we must implement a constructor. Can you spot what's different about this one?
/**
* Disable instantiation.
*/
private function __construct
{
// Private to disabled instantiation.
}
Our constructor is private. In PHP, we almost never use a private constructor, but in the case of a singleton, this is a very important feature. You see, when a constructor is made private, you are no longer able to instantiate the class. The following becomes impossible.
$db = new DB;
This means that we'll never be able to hold multiple copies of our DB class, and therefore, there will not be multiple copies of the database instance held within.
Next, we have a very special method called getInstance()
. Let's take a closer look, shall we?
/**
* Create or retrieve the instance of our database client.
*
* @return Database
*/
public static function getInstance()
{
if (is_null(static::$instance)) {
static::$instance = new Database;
static::$instance->setHost('127.0.0.1');
static::$instance->setPort('3306');
static::$instance->setUsername('root');
static::$instance->setPassword('cuddly_binturong');
}
return static::$instance;
}
The getInstance()
method is static. This is mandatory since we made our constructor private and cannot create instances of the class. On the first line, we check whether the $instance
static class property has been set. If it hasn't been set, the value will be equal to null. If the value is null, we'll create a new instance of the Database
client, bootstrap its configuration, and set it to the static $instance
property on the class.
If the instance already exists, or has been created, it will then be returned as the result of the getInstance()
function. As you can see, the Database
client can only be created once. All subsequent calls to this method will result in returning the same instance of the client.
Let's take a look at the next method, shall we?
/**
* Disable the cloning of this class.
*
* @return void
*/
final public function __clone()
{
throw new Exception('Feature disabled.');
}
The __clone()
magic method is the exact opposite of what we want to achieve. It's sole purpose is to create a clone of our class. We don't want this. We definitely don't want this! Instead, we'll make the method final
so that it can't be overridden, and we'll have the method throw an exception so that it can't be used.
Finally, we'll take a look at the last method of the class.
/**
* Disable the wakeup of this class.
*
* @return void
*/
final public function __wakeup()
{
throw new Exception('Feature disabled.');
}
The __wakeup()
method is called when a PHP object is unserialized. We don't want this object to be serialisable because you could unserialise it multiple times to result in multiple instances of the class. For that reason, we'll have the wakeup method throw an exception.
Now that we've taken the time to examine our singleton class in detail let's see if we can work out how to use it. Go on, have a go!
<?php
// Retrieve the singleton client instance.
$db = DB::getInstance();
// Make use of our client instance.
$results = $db->select('users');
The first time we call to the getInstance()
static method, our database instance is created and returned. Any subsequent calls to this method will result in the same instance of the client being returned. What this means, is that we can make use of the getInstance()
method anywhere in our code, or in multiple places across our application, and we'll never have to worry about using too much memory or having to bootstrap the database client each time.
Observations
While the singleton pattern is often the first design pattern taught to student engineers, it's not without its faults. In fact, it's believed by many to be a form of an anti-pattern. A pattern which appears to be useful, but can lead to damaging consequences.
The singleton pattern is incredibly useful when there must be only one copy of a class instance. However, it's often used incorrectly as a centerpiece for application architecture. Many developers will use it to replace global variables, or to make their services accessible to all parts of their application.
Instead, it would be wiser to implement dependency injection, or a service location pattern. Be sure to check out some of the structural patterns defined in this book, to ensure that you don't fall into this trap!