Advertisement
  1. Code
  2. Coding Fundamentals
  3. OOP

Design Patterns: The Singleton Pattern

Scroll to top
Read Time: 7 min

In this article you are going to learn how to implement the Singleton design pattern, and why and when to use this pattern in your application. As the name "Singleton" suggests, this method allows us to create one and only one object of a class. 

Let's see what we have on Wikipedia about this design pattern:

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.

As mentioned in the above definition, when we want to make sure that one and only one object needs to be created for any class, then we should implement the Singleton pattern for that class.

You might ask why we should implement such a class which allows us to create only one object of it. I would say there are many use cases where we can apply this design pattern. These include: configuration class, session class, database class, and many more.

I will take the example of a database class for this article. First we will see what the problem can be if a Singleton pattern is not implemented for such a class. 

The Problem

Imagine a very simple database connection class which creates a connection with the database once we create an object of that class. 

1
class database {
2
    
3
	private $dbName = null, $dbHost = null, $dbPass = null, $dbUser = null;
4
	
5
	public function __construct($dbDetails = array()) {
6
		
7
		$this->dbName = $dbDetails['db_name'];
8
		$this->dbHost = $dbDetails['db_host'];
9
		$this->dbUser = $dbDetails['db_user'];
10
		$this->dbPass = $dbDetails['db_pass'];
11
12
		$this->dbh = new PDO('mysql:host='.$this->dbHost.';dbname='.$this->dbName, $this->dbUser, $this->dbPass);
13
		
14
	}
15
	
16
}

In the above code example, you can see that it will make a connection to the database every time you create an object of this class. So if a developer has created an object of this class in multiple places, imagine the number of (identical) database connections it will create with the database server.

So unknowingly the developer is making mistakes which lead to a huge impact on the speed of the database and the application server. Let's see the same thing via creating a different object of that class.

1
$dbDetails = array(
2
    	'db_name' => 'designpatterns',
3
		'db_host' => 'localhost',
4
		'db_user' => 'root',
5
		'db_pass' => 'mysqldba'
6
);
7
8
$db1 = new database($dbDetails);
9
var_dump($db1);
10
$db2 = new database($dbDetails);
11
var_dump($db2);
12
$db3 = new database($dbDetails);
13
var_dump($db3);
14
$db4 = new database($dbDetails);
15
var_dump($db4);
16
17
// Output

18
object(database)[1]
19
  private 'dbName' => string 'designpatterns' (length=14)
20
  private 'dbHost' => string 'localhost' (length=9)
21
  private 'dbPass' => string 'mysqldba' (length=8)
22
  private 'dbUser' => string 'root' (length=4)
23
  public 'dbh' => object(PDO)[2]
24
object(database)[3]
25
  private 'dbName' => string 'designpatterns' (length=14)
26
  private 'dbHost' => string 'localhost' (length=9)
27
  private 'dbPass' => string 'mysqldba' (length=8)
28
  private 'dbUser' => string 'root' (length=4)
29
  public 'dbh' => object(PDO)[4]
30
object(database)[5]
31
  private 'dbName' => string 'designpatterns' (length=14)
32
  private 'dbHost' => string 'localhost' (length=9)
33
  private 'dbPass' => string 'mysqldba' (length=8)
34
  private 'dbUser' => string 'root' (length=4)
35
  public 'dbh' => object(PDO)[6]
36
object(database)[7]
37
  private 'dbName' => string 'designpatterns' (length=14)
38
  private 'dbHost' => string 'localhost' (length=9)
39
  private 'dbPass' => string 'mysqldba' (length=8)
40
  private 'dbUser' => string 'root' (length=4)
41
  public 'dbh' => object(PDO)[8]

If you see the output of the above code and output, you can see that each object has a new resource ID assigned, so all objects are completely new reference, hence it allocates separate memory as well. So unknowingly our application will occupy resources which are really not required.

The Solution

It's not in our control how developers use our base framework. It is in our control after the code review process takes place, but during development we cannot stand behind them all the time.

To overcome such a situation, we should make our base class in such a way that it is not able to create multiple objects of a class; instead it will give an already created object if any. This is the case where we should consider developing a Singleton pattern for our base classes.

While implementing this pattern, our aim will be to allow the creation of an object of a class one and only one time. Allow me to add the class code below and then we will go through each portion of this class.

1
class database {
2
    
3
	private $dbName = null, $dbHost = null, $dbPass = null, $dbUser = null;
4
	private static $instance = null;
5
	
6
	private function __construct($dbDetails = array()) {
7
		
8
		// Please note that this is Private Constructor

9
		
10
		$this->dbName = $dbDetails['db_name'];
11
		$this->dbHost = $dbDetails['db_host'];
12
		$this->dbUser = $dbDetails['db_user'];
13
		$this->dbPass = $dbDetails['db_pass'];
14
15
		// Your Code here to connect to database //

16
		$this->dbh = new PDO('mysql:host='.$this->dbHost.';dbname='.$this->dbName, $this->dbUser, $this->dbPass);
17
	}
18
	
19
	public static function connect($dbDetails = array()) {
20
		
21
		// Check if instance is already exists 		

22
		if(self::$instance == null) {
23
			self::$instance = new database($dbDetails);
24
		}
25
		
26
		return self::$instance;
27
		
28
	}
29
	
30
	private function __clone() {
31
		// Stopping Clonning of Object

32
	}
33
	
34
	private function __wakeup() {
35
		// Stopping unserialize of object

36
	}
37
	
38
}

There is little indication which says the above class is a Singleton class. The very first thing is a private constructor, which prevents object creation using the new keyword. Another indication is one static member variable which holds the reference to an already created object.

1
$dbDetails = array(
2
    	'db_name' => 'designpatterns',
3
		'db_host' => 'localhost',
4
		'db_user' => 'root',
5
		'db_pass' => 'mysqldba'
6
);
7
8
$db1 = database::connect($dbDetails);
9
var_dump($db1);
10
$db2 = database::connect($dbDetails);
11
var_dump($db2);
12
$db3 = database::connect($dbDetails);
13
var_dump($db3);
14
$db4 = database::connect($dbDetails);
15
var_dump($db4);
16
17
// Output

18
19
object(database)[1]
20
  private 'dbName' => string 'designpatterns' (length=14)
21
  private 'dbHost' => string 'localhost' (length=9)
22
  private 'dbPass' => string 'mysqldba' (length=8)
23
  private 'dbUser' => string 'root' (length=4)
24
  public 'dbh' => object(PDO)[2]
25
object(database)[1]
26
  private 'dbName' => string 'designpatterns' (length=14)
27
  private 'dbHost' => string 'localhost' (length=9)
28
  private 'dbPass' => string 'mysqldba' (length=8)
29
  private 'dbUser' => string 'root' (length=4)
30
  public 'dbh' => object(PDO)[2]
31
object(database)[1]
32
  private 'dbName' => string 'designpatterns' (length=14)
33
  private 'dbHost' => string 'localhost' (length=9)
34
  private 'dbPass' => string 'mysqldba' (length=8)
35
  private 'dbUser' => string 'root' (length=4)
36
  public 'dbh' => object(PDO)[2]
37
object(database)[1]
38
  private 'dbName' => string 'designpatterns' (length=14)
39
  private 'dbHost' => string 'localhost' (length=9)
40
  private 'dbPass' => string 'mysqldba' (length=8)
41
  private 'dbUser' => string 'root' (length=4)
42
  public 'dbh' => object(PDO)[2]

If you compare the output of both sections then you will see, in the output of the Singleton pattern, the resource ID for the object is the same for all different objects. But that is not the case when the design pattern is not used.

Singleton as an Anti-Pattern

This design pattern is also called an Anti-Pattern for various reasons, which I will mention below:

  1. It violates the single responsibility principle because of its quality of controlling its own creation and lifecycle.
  2. It introduces global state to your application. I would say global state is very bad because any code can change its value. So at the time of debugging it's really hard to find which portion of the code has made the current stage of global variable.
  3. Singleton is generally a bad idea if you are doing unit testing, and it's generally a bad idea not to perform unit testing.

Conclusion

I have tried my best to explain the Singleton design pattern, which is widely discussed on the internet. I hope you find this article helpful. We have covered both aspects of this pattern which are as a Design Pattern and as an Anti-Pattern.

Please post your comments, suggestions and/or questions below, and I will post my response as soon as possible. You can also reach me on Twitter @XpertDevelopers or email me straight away.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.