A Guide to the Singleton Design Pattern

Mastering the Singleton Design Pattern: Step-by-Step Instructions

A Guide to the Singleton Design Pattern

The Singleton Design Pattern is one of the simplest in the category of creational design patterns. It ensures that a class has only one instance and provides a way to access that instance.

There are numerous scenarios where it's necessary to limit clients from creating multiple instances of a class. This design pattern is applicable when,

  • Creating an object for each request is expensive.

    A class has a shared state through the global variables used throughout the application.

    It's undesirable to have code that addresses a single problem dispersed across multiple classes.

Few common scenarios where we can see this design pattern are followed:

  • Create single connection to the database across application.

  • Maintain single truth of source for user state.

  • Global configuration of the application

How to implement Singleton Desing Pattern?

  1. Restrict the creation of a class instance outside the class by making the constructor private.

  2. Add a private static global variable in the class of the same class type.

  3. Create a public static method that checks the state of the class instance variable and initializes it if necessary, then returns it.

In this blog, let's take the example of a Database Connection class and implement it using the Singleton Design Pattern with various approaches. We will understand the pros and cons of each approach.

Simple Approach

In this approach we follow above mentioned simple steps to implement singleton design pattern using lazy initialization of the class instance.

package com.techtalesonline.designpattern.singletone;
public class DBConnection {
// Create static class variable of the same class
private static DBConnection dbCon;
/**
* Private constructor restrict creation of the object outside
* of the class.
*/
private DBConnection(){
}
/**
* This static method creates instance of the class if it null and return.
*/
public static DBConnection getInstance(){
if(dbCon == null){
dbCon = new DBConnection();
}
return dbCon;
}
}

Prons:

  • Easy implementation

  • Lazy object creation

Cons:

  • It is not thread safe in multi-threaded environment.

Eager initialization Approach

In this approach, we create an instance of the class using a static class variable. The JVM ensures that a single instance of the class is created when the class is loaded into memory. This approach solves the problem of thread safety and ensures there is always a single instance of the class.

package com.techtalesonline.designpattern.singletone;
public class DBConnection {
// Create private static class variable of the same class
private static DBConnection dbCon = new DBConnection();
/**
* Private constructor restrict creation of the object outside
* of the class.
*/
private DBConnection(){
}
/**
* This static method creates instance of the class if it null and return.
*/
public static DBConnection getInstance(){
return dbCon;
}
}

Prons:

  • Easy implementation

  • Eager initialization of the class instance

  • Thread-safe as the instance of the class is created during class loading.

Cons:

  • Waste of resources if the instance is not used in the application.

  • Slow application startup if there are many classes with singleton design pattern implementation.

  • Unable to pass parameters during the initialization of the instance.

  • Exception handling is not possible.

Lazy Initialization with Thread safe Approach

In this approach, we use lazy initialization of the class instance through a class method. This method is marked as synchronized to resolve thread safety issues. This ensures that only one thread gets the lock for the class and executes the method.

package com.techtalesonline.designpattern.singletone;
public class DBConnection {
// Create private static class variable of the same class
private static DBConnection dbCon;
/**
* Private constructor restrict creation of the object outside
* of the class.
*/
private DBConnection(){
}
/**
* This static method creates instance of the class if it null and return.
*/
synchronized public static DBConnection getInstance(){
if(dbCon == null){
dbCon = new DBConnection();
}
return dbCon;
}
}

Prons:

  • Resolves the thread safety issue.

  • Lazy initialization of the instance reduces memory waste.

Cons:

  • Adds the overhead of wait time when multiple threads try to access the method.

Lazy initialization with Double check locking

In this approach, we make only the critical section of the code synchronized and add a double-check before creating a new object. This reduces wait time when multiple threads try to create an instance. This is achieved through a synchronized code block.

package com.techtalesonline.designpattern.singletone;
public class DBConnection {
// Create private static class variable of the same class
private static DBConnection dbCon;
/**
* Private constructor restrict creation of the object outside
* of the class.
*/
private DBConnection(){
}
/**
* This static method creates instance of the class if it null and return.
*/
public static DBConnection getInstance(){
if(dbCon == null){
synchronized(DBConnection.class){
if(dbCon == null){
dbCon = new DBConnection();
}
}
}
return dbCon;
}
}

Drawback of the Singleton Pattern

  • This design pattern violates the Single Responsibility Principle as it tries to solve two problems.

  • It is difficult to perform unit testing.

  • This design pattern requires special attention in a multi-threaded environment.

I hope you enjoyed the blog and understood the concepts of the Singleton Design Pattern.

Thank you. I appreciate your feedback and comments.

Did you find this article valuable?

Support Vishad Patel by becoming a sponsor. Any amount is appreciated!