Singleton design pattern
We use design patterns to solve problems that occur often during software design. One of the basic and widely used design patterns is the singleton design pattern.
In this article, we will understand what is a singleton design pattern and what purpose it serves in software design using two examples in Python.
Why should we use design patterns at all?
Think of design patterns as proven solutions to the set of commonly occurring problems during programming. For example, if you and your friend run into the same problem while programming, you both sit down and develop a solution for it. Next time, if that same problem occurs while programming, both of you can re-use that solution to solve the problem at hand.
Similarly, design patterns are the proven solutions for commonly occurring problems in software engineering.
Singleton
Imagine you have a special class in your software that’s like the gatekeeper for something super important. Let’s say it’s like the guardian of a treasure chest. Now, you really need to make sure there’s only one guardian for that chest, or chaos could happen. That’s where the Singleton design pattern comes into play.
In simple terms, the Singleton pattern is like having a rule that says, “There can only be one guardian for this treasure chest at any given time.” So, no matter how many times you ask for a new guardian, you always end up with just that one, reliable guardian.
This is really handy because it gives you a single point of contact to manage access to that critical resource. It keeps everything tidy and prevents any confusion or conflicts that might arise if there were multiple guardians running around.
Example 1
Let us construct a Python class to exemplify the Singleton pattern. In this instance, we’ll investigate a scenario involving a Configuration Manager.
class ConfigurationManager:
_instance = None # A repository for the singular instance
def __new__(cls):
if not cls._instance:
cls._instance = super(ConfigurationManager, cls).__new__(cls)
# Initialization logic goes here
return cls._instance
This code snippet implements the Singleton design pattern in Python using a class called ConfigurationManager
. Let’s break it down:
class ConfigurationManager:
: This line defines a class namedConfigurationManager
._instance = None
: This line creates a class variable_instance
and initializes it toNone
. This variable will be used to hold the singular instance of the class.def __new__(cls):
: This is a special method in Python classes that is responsible for creating and returning a new instance of the class. It’s called before the__init__
method.if not cls._instance:
: This line checks if the_instance
variable isNone
, indicating that no instance of the class has been created yet.cls._instance = super(ConfigurationManager, cls).__new__(cls)
: If_instance
isNone
, this line creates a new instance of theConfigurationManager
class using the__new__
method inherited from the superclass (super(ConfigurationManager, cls).__new__(cls)
).return cls._instance
: Finally, this line returns the instance of the class. If an instance already exists, it returns the existing one; otherwise, it returns the newly created instance.
Overall, this code ensures that only one instance of the ConfigurationManager
class is created throughout the lifetime of the program, fulfilling the Singleton pattern’s requirement.
Recommendation: SOLID Principles in Python
Example 2
To underscore the practicality of Singleton, let’s examine a hypothetical scenario involving a Database Connector in Python. The Singleton pattern ensures that throughout the application lifecycle, only one database connection instance is instantiated and accessed.
class DatabaseConnector:
_instance = None
def __new__(cls):
if not cls._instance:
cls._instance = super(DatabaseConnector, cls).__new__(cls)
# Initialization logic for database connection
print("Creating a new database connection...")
return cls._instance
# Example 1: Creating two instances of DatabaseConnector
connector1 = DatabaseConnector()
connector2 = DatabaseConnector()
# Example 2: Checking if both instances refer to the same object
print(connector1 is connector2) # Output: True
Explanation:
- The
DatabaseConnector
class has a class variable_instance
to hold the single instance of the class. - The
__new__
method is overridden to control the instantiation process. It ensures that only one instance of the class is created. If_instance
isNone
, it creates a new instance using thesuper().__new__()
method, otherwise, it returns the existing instance. - In Example 1, we create two instances of the
DatabaseConnector
class. However, because of the Singleton pattern, only one actual instance is created, and bothconnector1
andconnector2
refer to the same object. - In Example 2, we confirm that both
connector1
andconnector2
refer to the same object by checking if they are identical using theis
operator. The output isTrue
, indicating that they indeed refer to the same instance. Additionally, the print statement inside the__new__
method is only executed once during the creation of the first instance, demonstrating that the initialization logic is only executed once.
In software development, the Singleton design pattern proves instrumental when managing resources demanding singular control. Whether it be a Configuration Manager or a Database Connector, Singleton ensures consistency and prevents redundancy, contributing to a harmonious software architecture.
In scenarios demanding centralized control and singular instances, Singleton stands as an invaluable tool in the developer’s toolkit.
Talha is a seasoned Software Engineer with a passion for exploring the ever-evolving world of technology. With a strong foundation in Python and expertise in web development, web scraping, and machine learning, he loves to unravel the intricacies of the digital landscape. Talha loves to write content on this platform for sharing insights, tutorials, and updates on coding, development, and the latest tech trends