Singleton Pattern

On May 15, 2008, in Design Patterns, by admin

Introduction

The Singleton is one of the simplest Patterns to understand. It’s common usage is to ensure that only one instance of a class is ever instantiated. The reason for wanting such behaviour varies but typically it is because only one object instantiated from the source class is required and you want the resulting object to be available throughout an application, i.e. globally available.

An example might be a class for storing Settings. A Settings class is a good candidate for a Singleton because its data is immutable (the only way to change settings data is to edit the settings file) and it is likely required in many areas of an application. Furthermore creating a new Settings object wherever it is needed is wasteful of resources – each is identical to all others.

viagra, candadian drugs

Definition

Definition: The Singleton Pattern proposes a static method for creating an instance of a class in such a way that once instantiated further calls to the same static method return a reference to the original instantiated object.

A PHP 5 Example

An example for PHP5 (without specific Settings methods implemented):

php
class Settings {

private $settings = array();

private static $_instance = null;

private function __construct() {
// private constructor restricts instantiaton to getInstance()
}

protected function __clone() {
// restricts cloning of the object
}

static public function getInstance() {
if(is_null(self::$_instance))
{
    self::$_instance = new self();
}
return self::$_instance;
}

public function import() {
// ...
}

public function get() {
// ...
}

}

Implementing the Singleton Pattern

The key to implementing a Singleton is utilising a static variable; a variable whose value remains constant even when execution leaves its variable scope. This stores the object originally instantiated between calls to the static method Settings::getInstance(), and returns a reference to it on each subsequent call to the method.

Also note that the constructor is usually made private. To ensure only one Settings object is ever used, we should restrict access to the constructor to prevent a new object being created in error. buy viagra online This specific restriction is not possible in PHP 4 so bear this in mind.

The Singleton also requires careful consideration before use. It is by nature very similar to a global variable – the static method is available from anywhere in the application once the file containing the class is included. This can create problems similar to those experienced with global variables – and so should be used with some degree of care. The near global nature of the Singleton has led to it being referred to on occassion as an “Anti Pattern” since it is easily misused as an alternative to making data global.

Using a Singleton is as simple as calling its static getInstance() method.

php
// fetch settings from INI file
$settings = Settings::getInstance();
$settings->import('/app/settings.ini');

// we  can now use the Settings object somewhere else, e.g. database class
class DB_Abstraction_MYSQLI {

private $settings = null;

public function __construct() {
// Settings already exists with imported settings data
// Fetch pre-existing object via Singleton method
$this->settings = Settings::getInstance();
}

public function connect() {
$conn = mysqli_connect(
    $this->settings->get('db.host'),
    $this->settings->get('db.username'),
    $this->settings->get('db.password'),
    $this->settings->get('db.database')
);
return $conn;
}

// ...

}

The alternative to using the Singleton is of course to instantiate a Settings object, and import the settings INI file again. However doing so only wastes resources since the data is immutable and identical to that held in all such objects. Why have a dozen identical Settings objects when you only need one?

Disadvantages

The Singleton is not without its disadvantages. We have already noted that its global nature can cause problems similar to using global variables and encourage misuse without care. It can also make the practice of Unit Testing more difficult. Unit Testing typically tests individual objects. In testing the DB_Abstraction class above, a programmer may wish to “mock” the Settings object (i.e. insert a fake object with known return values). This is useful in cases where the Settings class may not even yet exist, and we need to emulate it.

One solution, is to amend the class so that a developer may optionally pass a Settings object as a paramater into the constructor. The code for this may look similar to:

php
class DB_Abstraction_MYSQLI {

// ...

public function __construct($settings=null) {
$this->settings = is_null($settings) ? Settings::getInstance() : $settings;
}

// ...

}

This allows more flexible testing since a developer can optionally override the default behaviour of using the Settings class Singleton method, and replace it with any arbitrary Settings object they choose to facilitate testing.

Singletons for PHP 4

PHP 4 does not support static properties/variables in classes. As a result the method of implementing Singletons differ slightly. Rather than use a class property, we instead use a static variable from within the Singleton getInstance() method. In addition, PHP 4 does not support access keywords for class properties or methods. This means that PHP 4 constructors will always be available in addition to the Singleton method of instantiation.

An example of a PHP 4 Singleton implementation follows:

php
class Settings (

$settings = array();

function Settings() {
// no support for "private" keyword
}

function &getInstance() {
static $thisInstance;
if(!isset($thisInstance[0]))
{
    $thisInstance = array();
    $thisInstance[0] =& new Settings();
}
return $thisInstance[0];
}

function import() {
// ...
}

function get() {
// ...
}

}

You can retrieve the instance with

php
mySettings =& Settings::getInstance();

Other version

php
class MyClassUsedAsSingleton
{
var $_value;

function MyClassUsedAsSingleton ($directCall=true)
{
if ($directCall) {
    trigger_error("MyClassUsedAsSingleton is singleton!", E_USER_ERROR);
}
}

function &getInstance()
{
static $instance;
if (!is_object($instance)) {
    $instance = new MyClassUsedAsSingleton(false);
}
return $instance;
}

function set($value)
{
$this->_value = $value;
}

function get()
{
return $this->_value;
}

function isDefined()
{
if (is_null($this->_value)) {
    return false;
}
return true;
}
}
// static access
function setValue($value)
{
$object =& MyClassUsedAsSingleton::getInstance();
$object->set($value);
}

function getValue()
{
$object =& MyClassUsedAsSingleton::getInstance();
return $object->get();
}

function isValueDefined()
{
$object =& MyClassUsedAsSingleton::getInstance();
return $object->isDefined();
}
// use
function changeValue()
{
setValue("New Value");
}

if (!isValueDefined()) {
setValue("Default Value");
}
echo getValue();
changeValue();
echo getValue();

Note: Due to the way in which PHP 4 implements static variables, a static variable cannot hold a reference to a value throughout execution. However a static variable array element can. See References with global and static variables for more information.

Singleton Function

Another way to get a singleton of a class, without actually adding any additional code to the class is to use a function that will return or make a new instance of said class.

php
function singleton($class)
{
// Static Array Holding All Instances of Singleton Classes
static $Instances = array();
if(!isset($Instances[$class])){
$Instances[$class] = new $class();
}
return $Instances[$class];
}

Conclusion

Singletons as we have seen are a useful Design Pattern when used with care. They allow a single instance of any object to be globally available within an application. Implementation is possible in all current PHP versions, although constructor restriction is not possible in PHP 4. We have also covered how the Pattern can pose problems when unit testing, and how to overcome these with some small amendments (what some people like to call “loose wiring”).

The Singleton is not the only method for sharing objects across an application. Two other useful Patterns to examine are the Registry and ServiceLocator. Another method coming into popular discussion in PHP forums is Dependency Injection.

Tagged with:  

Comments are closed.