Skip to content

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:

  1. Logging: Having one central logger instance to manage log files and settings.
  2. Configuration Management: One object to load and hold application configuration settings.
  3. 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'.

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_a and config_b are identical.
  • The comparison config_a is config_b is True, confirming they are the same object.
  • The change made by config_a is immediately visible through config_b.

The Singleton pattern successfully enforced uniqueness and guaranteed global consistency for the configuration object.