Open-Source Internship opportunity by OpenGenus for programmers. Apply now.
Reading time: 40 minutes
Singleton pattern is a design pattern which prevents multiple objects of a class to exist. This is a very useful and widely used design pattern as object creation is a costly process (in terms of time) and there are situations that require us to have one object only such as the case of a database connection. In a database connection, we need to initiate the database object once and make all queries through it. Having multiple database objects can give rise to inconsistent database states and hence, needs to be avoided using Singleton Design Pattern.
Singleton pattern is one of the simplest design patterns.
Steps to implement
- Step 1: Create a class as Singleton is a reference type.
- Step 2: Create an instance of the class which declares as a static constant.
- Step 3: Make sure to make the initializer private to prevent creating other instances of Singleton, declare any variable you need to use.
Different approaches to make a singleton class
There are four approaches to implement singleton class:
- Method 1: classic implementation: private constructor
- Method 2: make getInstance() synchronized
- Method 3: Eager Instantiation
- Method 4 (Best): Use "Double Checked Locking"
Each approach has its shortcomings which is overcome by another approach. Read on through the approaches to get a deeper idea.
Method 1: Classic Implementation
In this method, the idea is to make the constructor private. As the constructor is needed to create an object and being private, it cannot be used by external classes and hence, is the simplest way to implement Singleton class.
See this implementation to get the idea:
// Classical implementation of singleton
// design pattern
class Singleton
{
private static Singleton obj;
// private constructor to force use of
// getInstance() to create Singleton object
private Singleton() {}
public static Singleton getInstance()
{
if (obj==null)
obj = new Singleton();
return obj;
}
}
Here we have declared getInstance() static so that we can call it without instantiating the class.
The first time getInstance() is called it creates a singleton object and after that it just returns the same object.
Singleton object is not created until it is required and call getInstance() method.
This is called lazy instantiation.
The main problem with above method is that it is not thread safe. Due to this, there can be a race condition such that multiple objects are created. As most applications like database connections are multithreaded, we need to develop a stronger solution.
Method 2: make getInstance() synchronized
The key idea of the method is to keep the contructor private and make the function getInstance() thread safe by using synchronization techniques.
// Thread Synchronizedimplementation of
// singleton design pattern
class Singleton
{
private static Singleton obj;
private Singleton() {}
// Only one thread can execute this at a time
public static synchronized Singleton getInstance()
{
if (obj==null)
obj = new Singleton();
return obj;
}
}
Here using synchronized makes sure that only one thread at a time can be executed by getInstance().
The main disadvantage is that using synchronized every time while creating the singleton object is expensive with respect to both time and space. This significantly reduces the program performance.
Method 3: Eager Instantiation
The key idea is to create the object beforehand and return it whenever it is required. We skip the object creation process completely by doing it ever before it is needed to avoid any problems during runtime.
See this implementation to get the idea:
// Static initializer based implementation of
// singleton design pattern
class Singleton
{
private static Singleton obj = new Singleton();
private Singleton() {}
public static Singleton getInstance()
{
return obj;
}
}
Here we create instance of singleton in static initializer.
The main problem is that we create the object even if it is not required which can be an overhead in several situations where no object creation is required. It puts an overhead during the startup of the program.
Method 4 (Best): Use âDouble Checked Lockingâ
If you notice carefully once an object is created, synchronization is no longer useful because now object will not be null i.e empty.
So we will only acquire lock on the getInstance() once, when the object is null. This way we only synchronize the first way through, just what we want.
// Double Checked Locking based implementation of
// singleton design pattern
class Singleton
{
private volatile static Singleton obj;
private Singleton() {}
public static Singleton getInstance()
{
if (obj == null)
{
// To make thread safe
synchronized (Singleton.class)
{
// check again as multiple threads
// can reach above step
if (obj==null)
obj = new Singleton();
}
}
return obj;
}
}
We have declared the obj volatile which ,makes sures that multiple threads/ response offer the object variable correctly when it is being initialized to Singleton instance. This reduces the overhead of calling the synchronized method every time.
Structure
Make the class of the single instance responsible for access and initialization on first use. The single instance is a private static instance. The accessor function is a public static instance.
Example
In this example, we will take a look how we can add singleton pattern to a C++ code.
Code before singleton pattern
NOTE : A global variable is default initialized - when it is declared - but it is not re-initialized in until its first occurance.
class GlobalClass
{
int m_value;
public:
GlobalClass(int v = 0)
{
m_value = v;
}
int get_value()
{
return m_value;
}
void set_value(int v)
{
m_value = v;
}
};
// Default initialization
GlobalClass *global_ptr = 0;
void foo(void)
{
// Initialization on first use
if (!global_ptr)
global_ptr = new GlobalClass;
global_ptr->set_value(1);
cout << "foo: global_ptr is " << global_ptr->get_value() << '\n';
}
void bar(void)
{
if (!global_ptr)
global_ptr = new GlobalClass;
global_ptr->set_value(2);
cout << "bar: global_ptr is " << global_ptr->get_value() << '\n';
}
int main()
{
if (!global_ptr)
global_ptr = new GlobalClass;
cout << "main: global_ptr is " << global_ptr->get_value() << '\n';
foo();
bar();
}
Output :
main: global_ptr is 0
foo: global_ptr is 1
bar: global_ptr is 2
Code after singleton pattern
Make the class responsible for its own global pointer and "initialization on first use". The client uses only the public accessor method.
Using a private static pointer and a public static accessor method.
class GlobalClass
{
int m_value;
static GlobalClass *s_instance;
GlobalClass(int v = 0)
{
m_value = v;
}
public:
int get_value()
{
return m_value;
}
void set_value(int v)
{
m_value = v;
}
static GlobalClass *instance()
{
if (!s_instance)
s_instance = new GlobalClass;
return s_instance;
}
};
// Allocating and initializing GlobalClass's
// static data member. The pointer is being
// allocated - not the object inself.
GlobalClass *GlobalClass::s_instance = 0;
void foo(void)
{
GlobalClass::instance()->set_value(1);
cout << "foo: global_ptr is " << GlobalClass::instance()->get_value() << '\n';
}
void bar(void)
{
GlobalClass::instance()->set_value(2);
cout << "bar: global_ptr is " << GlobalClass::instance()->get_value() << '\n';
}
int main()
{
cout << "main: global_ptr is " << GlobalClass::instance()->get_value() << '\n';
foo();
bar();
}
Output :
main: global_ptr is 0
foo: global_ptr is 1
bar: global_ptr is 2
Advantages and Disadvantages
Advantages
-
Instance control: Singleton prevents other objects from instantiating their own copies.
-
Flexibility: Since the class controls the instantiation process,it has the flexibility to change the instantiation process.
-
Easy to implement: Easy to create and we could use it anywhere for the lifetime of the application.
Disadvantages
-
They are a global mutable shared state : Their state is automatically shared across the entire application.
-
The relationships between Singletons and the code is usually not well defined : Since Singletons are so convenient and easy to accessâââusing them extensively usually leads to very hard to maintain âspaghetti codeâ that doesnât have clear separations between objects.