Object Oriented Design The Singleton Posted by: John in ProgrammingPHPObject Oriented ProgrammingDesign Patterns on
One of the many annoyances of programming is a variables scope. Generally variables can be declared within the global namespace or the local namespace. When working with methods and classes global variables appear to be a good idea, however overuse of the global namespace can cause variables to clash, it also undermines encapsulation. Moreover, a class which depends on a global variable is coupled with the project rendering it impossible to reuse (unless the global variable is declared). This will lead to unwanted debugging. Using the singleton design pattern, you can make a class seemingly “global.” Although the code in this article is PHP, the concepts apply across almost every programming language.


Problem:
While designing my framework I found access to the database object was required by many classes. Since I only intend on connecting to a single database server, only one object of my database class would ever need to be instantiated. A novice programmer would create a database class which would look like the following:
 
  1. class database {
  2.        
  3.     private $host;
  4.     private $username;
  5.     private $password;
  6.     private $database;
  7.  
  8.     public function __construct(array $config) {
  9.         $this->host = $config['host'];
  10.         $this->username = $config['username'];
  11.         $this->password = $config['password'];
  12.         $this->database = $config['database'];
  13.     }
  14.    
  15.     public function connect() {
  16.         mysql_connect($this->host, $this->username, $this->password);
  17.     }
  18.    
  19.     public function query() {
  20.         //...code here
  21.     }
  22.    
  23.     //...more code here
  24. }
  25.  
  26. ?>
To access any one of the methods you must instantiate the class then call the method:
 
  1. $db = new database($config);
  2. $database->query(“SELECT * FROM users WHERE id = 1);


Obviously the code above (assuming the methods have been implemented)will query the database. In most instances the code above is an acceptable practice. However, this will lead you down one of three roads: you will declare $db in the global namespace, you will instantiate a new database object each time it is needed, or you will end up passing $db to object X so when it creates object Y the database object can be passed. Alas, the learned programmer will choose to use the Singleton design pattern.

Solution:
The solution is to create a class that can only be instantiated itself. Moreover, it should not instantiate more than once instance of itself. We start by making the constructor private, creating a new static method which I will call getInstance(), and a new static variable which I will call $instance.
 
  1. class database {
  2.        
  3.     private $host;
  4.     private $username;
  5.     private $password;
  6.     private $database;
  7.     private static $instance;
  8.    
  9.    
  10.     private function __construct(array $config) {
  11.         $this->host = $config['host'];
  12.         $this->username = $config['username'];
  13.         $this->password = $config['password'];
  14.         $this->database = $config['database'];
  15.     }
  16.  
  17.     static function getInstance() {
  18.  
  19.     }
  20.    
  21.     public function connect() {
  22.         mysql_connect($this->host, $this->username, $this->password);
  23.     }
  24.    
  25.     public function query() {
  26.         //...code here
  27.     }
  28.    
  29.     //...more code here
  30. }
  31.  
  32. ?>
Since the constructor is private, to convey the configuration settings to the database call we will need to pass them to the getInstance() function. Here is where the magic happens. The static variable $instance is used to hold the instance of this class (if one exists). Since we only want one instance to ever be created, we will check to see if $instance has already been set, if it has not, we will set $instance to a new instance of the object then return it. If it has been set, we will simply return the already created object. Confused? Take a look at the code " it should clear things up:

 
  1. class database {
  2.        
  3.     private $host;
  4.     private $username;
  5.     private $password;
  6.     private $database;
  7.     //store the instance of this object if one exists
  8.     private static $instance;
  9.    
  10.    
  11.     private function __construct(array $config) {
  12.         $this->host = $config['host'];
  13.         $this->username = $config['username'];
  14.         $this->password = $config['password'];
  15.         $this->database = $config['database'];
  16.     }
  17.  
  18.     static function getInstance(array $config = null) {
  19.         if( empty( self::$instance ) )
  20.         {
  21.             //create a new instance of this class
  22.             self::$instance = new database($config);
  23.         }
  24.         //return the instance
  25.         return self::$instance;
  26.     }
  27.    
  28.     public function connect() {
  29.         mysql_connect($this->host, $this->username, $this->password);
  30.     }
  31.    
  32.     public function query() {
  33.         //...code here
  34.     }
  35.    
  36.     //...more code here
  37. }
  38.  
  39. ?>
To use this class, we use PHP's scope resolution operator, which allows us to access a static function of a class, without instantiating it.
 
  1. $db = database::getInstance($config_info);
  2. $db->connect();
  3. $db->query(“SELECT * FROM users WHERE id = 1);
We can now call the database::getInstance($config_info) from any class as many times as we want, without creating a new object. Moreover, we can access the same exact object created in a different location of our application essentially providing “global” access.

Trackback(0)
feed4 Comments
Jordan
July 17, 2008
Votes: +0

Design patterns was interesting to learn, IMO. It looks like you forgot to end your code block in the right place though. Take a look at your second block of code (you have text that isn't code or comment).

report abuse
vote down
vote up
John
July 17, 2008
Votes: +0

Fixed the code problem. smilies/grin.gif

report abuse
vote down
vote up
Jordan
July 17, 2008
Votes: +0

You sure? It looks like it is still there to me.

report abuse
vote down
vote up
Xav
July 18, 2008
Votes: +0

Maybe you should deduct some points. Heh heh heh. smilies/cheesy.gif

report abuse
vote down
vote up

Write comment
 
 
quote
bold
italicize
underline
strike
url
image
quote
quote
smile
wink
laugh
grin
angry
sad
shocked
cool
tongue
kiss
cry
smaller | bigger
 

security image
Write the displayed characters


busy