SINGLETON PATTERN
The Singleton Pattern
The Singleton pattern ensures that a class has only one instance and provides a global point of access to that instance.
Why Do We Need the Singleton Pattern?
You need the Singleton pattern when you have a resource or component that should be unique throughout the application and accessible from anywhere.
Common Use Cases:
- Logging: Having one central logger instance to manage log files and settings.
- Configuration Management: One object to load and hold application configuration settings.
- Database Connection Pool: A single instance to manage the connections to a database efficiently.
The Problem Without a Pattern
Without enforcing a Singleton, every time you try to create an object of a class that should be unique, you might inadvertently create a new, separate instance.
Example of the Problem (Non-Singleton Class):
Imagine a simple configuration manager.
class ConfigurationManager:
"""A standard class for application configuration."""
def __init__(self):
# Pretend this loads a config file, which is an expensive operation
self.settings = {"timeout": 30, "log_level": "INFO"}
print("Configuration Manager instance created.")
def get_setting(self, key):
return self.settings.get(key)
# Scenario: Two different parts of the application create a config manager
config_a = ConfigurationManager()
config_b = ConfigurationManager()
print(f"Config A ID: {id(config_a)}")
print(f"Config B ID: {id(config_b)}")
# Output will show 'Configuration Manager instance created.' twice and different IDs:
# Configuration Manager instance created.
# Configuration Manager instance created.
# Config A ID: 4464547440
# Config B ID: 4464547496 (IDs will vary)
The Problem:
- Wasteful Resource Use: The expensive configuration loading process is executed twice.
- Inconsistency Risk: If one instance (e.g.,
config_a) modifies a setting, the other instance (config_b) does not see the change, leading to unpredictable behavior and bugs.- If
config_a.settings['log_level'] = 'DEBUG',config_b.settings['log_level']is still'INFO'.
- If
How the Singleton Pattern Helps
The Singleton pattern modifies the class structure to ensure that the constructor ($\text{\_\_init\_\_}$ or equivalent) is only run once, and subsequent calls return the very first object created.
Python Implementation using $\text{\_\_new\_\_}$:
In Python, the most robust way to create a Singleton is by overriding the special $\text{\_\_new\_\_}$ method, which is called before $\text{\_\_init\_\_}$ to create the instance.
class SingletonConfigurationManager:
"""The Singleton implementation."""
# 1. Class-level variable to hold the single instance
_instance = None
def __new__(cls):
# 2. Check if an instance already exists
if cls._instance is None:
# 3. If not, create a new instance using the parent's __new__ (object)
print("Configuration Manager: Creating the first and only instance.")
cls._instance = super(SingletonConfigurationManager, cls).__new__(cls)
# Initialization logic goes here (like loading the config)
cls._instance.settings = {"timeout": 30, "log_level": "INFO"}
# 4. Always return the stored single instance
return cls._instance
def get_setting(self, key):
return self._instance.settings.get(key)
def set_setting(self, key, value):
self._instance.settings[key] = value
# Scenario: Multiple parts of the application try to get the config manager
config_a = SingletonConfigurationManager()
config_b = SingletonConfigurationManager()
print("\n--- After Creation ---")
print(f"Config A ID: {id(config_a)}")
print(f"Config B ID: {id(config_b)}")
print(f"Are they the same object? {config_a is config_b}")
# Test for consistency: Change a setting via one instance
config_a.set_setting("log_level", "DEBUG")
# Check the setting via the other instance
print(f"Config B's log level: {config_b.get_setting('log_level')}")
Output Analysis:
- The message
Configuration Manager: Creating the first and only instance.appears only once. - The IDs for
config_aandconfig_bare identical. - The comparison
config_a is config_bis True, confirming they are the same object. - The change made by
config_ais immediately visible throughconfig_b.
The Singleton pattern successfully enforced uniqueness and guaranteed global consistency for the configuration object.