Singleton Pattern
The Singleton Pattern is something many people have used without even realising it. It’s used in software engineering to restrict the instantiation of a class to one object. In other words, it ensures that a class has only one instance and provides a global point of access to that instance.
In other…other words it’s useful in situations where you need to ensure that only one instance of a class exists and that it is easily accessible to other parts of your code.
A good example would be a logging system that needs to be accessed by multiple components. You use a singleton to sure that there is only one instance of the logging system, and that all components access the same instance.
Coding Examples
C# Example
Here’s a simple example of how to implement the Singleton Pattern in C#:
public class Singleton
{
private static Singleton instance;
private Singleton()
{
// Private constructor to prevent object instantiation from outside the class
}
public static Singleton Instance
{
get
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
Here we see Singleton
has a private constructor to prevent object instantiation from outside the class. Instead, we use the Instance
property to access the class:
Singleton instance = Singleton.Instance;
That’s as simple as it gets!
C++ Example
In C++ things become a bit more intricate:
#include <iostream>
using namespace std;
// Singleton class
class Singleton {
public:
// Delete the copy constructor and assignment operator
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
// Static method to get the instance of the singleton
static Singleton& getInstance() {
static Singleton instance;
return instance;
}
// Example method
void showMessage() {
cout << "Hello, this is a singleton instance!" << endl;
}
private:
// Private constructor to prevent instantiation
Singleton() {
cout << "Singleton instance created." << endl;
}
// Destructor
~Singleton() {
cout << "Singleton instance destroyed." << endl;
}
};
int main() {
// Get the single instance of the singleton class
Singleton& singleton = Singleton::getInstance();
// Call a method on the singleton instance
singleton.showMessage();
return 0;
}
Let’s explain things a bit:
- Private Constructor and Destructor: The constructor and destructor of the
Singleton
class are private to prevent direct instantiation and destruction. - Deleted Copy Constructor and Assignment Operator: The copy constructor and assignment operator are deleted to prevent copying of the singleton instance.
- Static Method for Access: The
getInstance
static method provides a global point of access to the singleton instance. It uses a local static variable to ensure that only one instance is created and it is destroyed when the program terminates. - Example Method: The
showMessage
method is an example method that can be called on the singleton instance.
Python Example
In Python we have more than one ways of achieving this, let’s look at a more complex example first:
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
return cls._instance
def __init__(self):
self.value = None
def show_message(self):
print("Hello, this is a singleton instance!")
# Usage
singleton1 = Singleton()
singleton2 = Singleton()
print(singleton1 is singleton2) # True
singleton1.show_message() # Hello, this is a singleton instance!
This method gives you a lot of control over how the singleton is instantiated, though it is complex to understand at first and I find myself referencing it time and again.
A different method would be using a decorator:
def singleton(cls):
instances = {}
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class Singleton:
def __init__(self):
self.value = None
def show_message(self):
print("Hello, this is a singleton instance!")
# Usage
singleton1 = Singleton()
singleton2 = Singleton()
print(singleton1 is singleton2) # True
singleton1.show_message() # Hello, this is a singleton instance!
This is more concise and easy to understand.
My favourite method though, is using a module:
# singleton_module.py
class Singleton:
def __init__(self):
self.value = None
def show_message(self):
print("Hello, this is a singleton instance!")
singleton = Singleton()
# Usage in another file
# import singleton_module
# singleton1 = singleton_module.singleton
# singleton2 = singleton_module.singleton
# print(singleton1 is singleton2) # True
# singleton1.show_message() # Hello, this is a singleton instance!
In Python, modules are singletons by design, meaning that they are only loaded once and the same instance is used throughout the application.
Thread-safety
Something I neglected to touch on in the C# example was that it was fine and well, in a single-threaded environment. If we move to multiple threads this class might not be very safe to use. I’m not going to address thread safety, as a concept, in this article but a simple way to make this singleton thread safe would be as follows:
public sealed class Singleton
{
private static volatile Singleton instance;
private static object syncRoot = new object();
private Singleton() { }
public static Singleton Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
}
Here we used the double-check locking technique and the Singleton
classic is made thread-safe by using the lock
statement to ensure that only one threat at a time can create the instance of the class.
The volatile
keyword tells the compiler to exclude the property from various optimisations and ultimately ensures the value of instance
is always up-to-date across different threads.
The double-check locking technique is used to minimise the performance impact of the lock
statement. We can implement the class similarly using something like semaphores.
A final note is that this implementation is lazy-loaded. In other words, the Singleton is not instantiated until it is requested. You could easily create it eagerly by initialising the instance
field when it is declared.