In software engineering, a design pattern is a
general solution to a common problem in software design. A design
pattern isn't a finished design that can be transformed directly into
code, it is a description or template ..
The Singleton Design Pattern for PHP
Introduction
In software engineering, a design pattern is a general solution to a
common problem in software design. A design pattern isn't a finished
design that can be transformed directly into code, it is a description
or template for how to solve a problem that can be used in many
different situations.
One of these design patterns is called the Singleton. It's purpose can be described quite briefly as follows:
Ensure a class has only one instance and provide a global point of access to it.
It
achieves this by only creating a new instance the first time it is
referenced, and thereafter it simply returns the handle to the existing
instance.
Why should you want this facility in the processing of a web page? There are several reasons:
- You
have references to an object in multiple places, and you do not want
the overhead of creating a new instance of that object for each
reference.
- You may wish to pass the object's state from one reference to another rather than always starting from an initial state.
As
with all design patterns there is more than one way in which it can be
implemented, so in the following sections I will document some of the
methods that I have encountered.
You should also be aware that the code samples in this document will work in both PHP 4 and PHP 5.
A non-class Helper function
This method uses a separate non-class function as a "helper". It contains code similar to the following:
function getDateObject ()
{
static $instance;
if (!is_object($instance)) {
require_once 'std.datevalidation.class.inc';
$instance = new DateClass;
}
return $instance;
}
It is referenced with code similar to the following:
$dateobj = getDateObject();
This method has the following advantages:
- Because
it is designed to work with a single specific class it can also take
the responsibility of ensuring that the relevant class file is loaded
when necessary.
This method has the following disadvantages:
- It requires a separate helper function for each different class.
A separate Helper method within each class
This method requires the addition of a getInstance() (or similar) method within each each and every class, similar to the following:
function getInstance ()
{
static $instance;
if (!isset($instance)) {
$c = __CLASS__;
$instance = new $c;
}
return $instance;
}
It is referenced with code similar to the following:
require_once 'std.datevalidation.class.inc';
$dateobj = DateClass::getInstance();
This method has the following advantages:
- It provides a consistent method across all classes.
This method has the following disadvantages:
- It requires that the class definition be pre-loaded.
- It
must be defined within each and every (sub)class where it will be
needed. Due to the way that it works it cannot be inherited from a
superclass because a static method in a superclass does not know of the
existence of any subclasses therefore cannot be used to instantiate a
subclass.
A single Helper method for all classes
This requires the creation of a separate class (called singleton in this example) with a single method (called getInstance() in this example).
class singleton
{
function getInstance ($class)
{
static $instances = array();
if (!array_key_exists($class, $instances)) {
$instances[$class] =& new $class;
}
$instance =& $instances[$class];
return $instance;
}
}
It is referenced with code similar to the following:
require_once 'std.datevalidation.class.inc';
$dateobj = singleton::getInstance('DateClass');
This method has the following advantages:
- It provides a single method which will work for any and all classes and subclasses.
- It does not require a method to be duplicated within each class or subclass.
This method has the following disadvantages:
- It
requires that the class definition be pre-loaded. If the file name
could be deduced or constructed from the class name then could be built
into the getInstance() method.
- Although the class names used with the new
command are case-insensitive, when used as keys within an associative
array they are not. This means that 'classname', 'ClassName' and
'CLASSNAME' would be treated as different keys in the array, therefore
would return different instances.
Conclusion
As you can see there is no single way to implement this design pattern,
and this is the most simple pattern there is! Just think of the
possible variations for the more complex patterns.
|