Skip to content
Low Level Design Mastery Logo
LowLevelDesign Mastery

Singleton Pattern

Ensure only one instance exists - control access to shared resources!

Singleton Pattern: One Instance to Rule Them All

Section titled “Singleton Pattern: One Instance to Rule Them All”

Now let’s dive into the Singleton Pattern - a creational design pattern that ensures a class has only one instance and provides a global point of access to it.

Imagine a pizza shop. You only need one manager to coordinate everything - you don’t want multiple managers giving conflicting orders! The Singleton Pattern works the same way!

The Singleton Pattern ensures that a class has only one instance and provides a global point of access to that instance. Instead of creating multiple instances, everyone uses the same one.

The Singleton Pattern is useful when:

  1. You need exactly one instance - Multiple instances would cause problems
  2. Shared resource management - Database connections, file systems, caches
  3. Global configuration - Application settings that should be consistent
  4. Logging systems - One logger instance for the entire application
  5. Resource-intensive objects - Objects that are expensive to create (only create once)
  6. State management - When you need a single source of truth

What Happens If We Don’t Use Singleton Pattern?

Section titled “What Happens If We Don’t Use Singleton Pattern?”

Without the Singleton Pattern, you might:

  • Create multiple instances - Wasting memory and resources
  • Inconsistent state - Different parts of code using different instances
  • Resource conflicts - Multiple connections to the same resource
  • Configuration chaos - Different parts reading different configurations
  • Performance issues - Creating expensive objects multiple times
  • Race conditions - Multiple instances competing for the same resource

Let’s start with a super simple example that anyone can understand!

Diagram

Here’s how the Singleton Pattern works in practice - showing how multiple clients get the same instance:

sequenceDiagram
    participant Client1
    participant Client2
    participant Client3
    participant Singleton as PizzaShopManager
    
    Client1->>Singleton: get_instance()
    activate Singleton
    Singleton->>Singleton: Check if instance exists
    Singleton->>Singleton: Create instance (first time)
    Singleton-->>Client1: Returns instance #1
    deactivate Singleton
    
    Client2->>Singleton: get_instance()
    activate Singleton
    Singleton->>Singleton: Check if instance exists
    Singleton-->>Client2: Returns same instance #1
    deactivate Singleton
    
    Client3->>Singleton: get_instance()
    activate Singleton
    Singleton->>Singleton: Check if instance exists
    Singleton-->>Client3: Returns same instance #1
    deactivate Singleton
    
    Note over Client1,Singleton: All clients get<br/>the SAME instance!

You’re building a pizza shop system. You need a manager to coordinate orders, but you only want one manager - having multiple managers would cause chaos! Without Singleton Pattern:

Problems:

  • Multiple instances with different states
  • No single source of truth
  • Memory waste
  • Inconsistent data across the application
classDiagram
    class PizzaShopManager {
        -_instance: PizzaShopManager
        -orders: List
        +__new__() PizzaShopManager
        +add_order(order) void
        +get_orders() List
    }
    class Client1 {
        +get_manager() PizzaShopManager
    }
    class Client2 {
        +get_manager() PizzaShopManager
    }
    class Client3 {
        +get_manager() PizzaShopManager
    }
    
    Client1 --> PizzaShopManager : gets instance
    Client2 --> PizzaShopManager : gets instance
    Client3 --> PizzaShopManager : gets instance
    
    note for PizzaShopManager "Only ONE instance<br/>exists globally"
    note for PizzaShopManager "All clients get<br/>the same instance"

Real-World Software Example: Database Connection Manager

Section titled “Real-World Software Example: Database Connection Manager”

Now let’s see a realistic software example - a database connection manager that should only have one instance to manage connection pooling efficiently.

You’re building an application that needs database access. Creating multiple connection managers would waste resources and cause connection pool conflicts. Without Singleton Pattern:

Problems:

  • Multiple connection pools wasting resources
  • Expensive initialization happens multiple times
  • No shared connection management
  • Potential connection pool exhaustion

There are several ways to implement the Singleton Pattern:

1. Eager Initialization (Simple Singleton)

Section titled “1. Eager Initialization (Simple Singleton)”

The instance is created when the class is loaded:

Pros: Simple, thread-safe
Cons: Instance created even if never used

The instance is created only when first requested:

Pros: Instance created only when needed
Cons: Not thread-safe (can create multiple instances in multi-threaded environment)

3. Thread-Safe Singleton (Double-Check Locking)

Section titled “3. Thread-Safe Singleton (Double-Check Locking)”

Thread-safe version using double-check locking:

Pros: Thread-safe, lazy initialization
Cons: Slightly more complex

4. Singleton with Module Pattern (Python-specific)

Section titled “4. Singleton with Module Pattern (Python-specific)”

In Python, modules are naturally singletons:

module_singleton.py
# singleton_module.py
class _Singleton:
def __init__(self):
self.value = None
# Module-level instance
_instance = _Singleton()
def get_instance():
return _instance
# Usage: from singleton_module import get_instance
# This is the most Pythonic way!

Pros: Most Pythonic, simple
Cons: Python-specific


Use Singleton Pattern when:

Exactly one instance needed - Multiple instances would cause problems
Global access required - Need to access from anywhere in the application
Expensive resource - Object is expensive to create (database connections, file handles)
Shared state - Need a single source of truth
Configuration management - Application-wide settings
Logging systems - One logger for the entire application
Caching - Single cache instance
Thread pools - Single thread pool manager

Don’t use Singleton Pattern when:

Multiple instances needed - If you might need multiple instances, don’t use Singleton
Testing difficulties - Singletons can make unit testing harder
Hidden dependencies - Global state can hide dependencies
Concurrency issues - Can cause problems in distributed systems
Violates Single Responsibility - Can become a “god object”
Tight coupling - Creates global coupling


Mistake 2: Not Thread-Safe in Multi-Threaded Environment

Section titled “Mistake 2: Not Thread-Safe in Multi-Threaded Environment”

Mistake 3: Serializable Singleton Without readResolve

Section titled “Mistake 3: Serializable Singleton Without readResolve”
SerializableSingleton.java
import java.io.Serializable;
// ❌ Bad: Serializable singleton without readResolve
public class BadSerializableSingleton implements Serializable {
private static final long serialVersionUID = 1L;
private static BadSerializableSingleton instance = new BadSerializableSingleton();
private BadSerializableSingleton() {}
public static BadSerializableSingleton getInstance() {
return instance;
}
// Problem: Deserialization creates a new instance!
// Need readResolve() method to return the singleton instance
}
// ✅ Good: With readResolve
public class GoodSerializableSingleton implements Serializable {
private static final long serialVersionUID = 1L;
private static GoodSerializableSingleton instance = new GoodSerializableSingleton();
private GoodSerializableSingleton() {}
public static GoodSerializableSingleton getInstance() {
return instance;
}
// Prevents deserialization from creating a new instance
protected Object readResolve() {
return getInstance();
}
}

  1. Controlled Access - Only one instance exists, controlled access point
  2. Resource Efficiency - Expensive objects created only once
  3. Global State - Single source of truth for shared state
  4. Memory Efficient - No memory waste from multiple instances
  5. Lazy Initialization - Can defer expensive initialization until needed
  6. Namespace - Provides a namespace for global variables

Singleton Pattern is a creational design pattern that ensures a class has only one instance and provides a global point of access to that instance.

  • Exactly one instance - Multiple instances would cause problems
  • Resource efficiency - Expensive objects created only once
  • Global access - Easy to access from anywhere
  • Shared state - Single source of truth
  • Memory efficient - No waste from multiple instances
  1. Private constructor - Prevents external instantiation
  2. Static instance variable - Stores the single instance
  3. Static getter method - Returns the instance (creates if needed)
  4. Controlled creation - Only one instance can exist
Client → getInstance() → Singleton Instance
  • Singleton Class - Class with private constructor
  • Static Instance - The single instance variable
  • Getter Method - Method to get the instance
  • Client - Code that uses the singleton
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super(Singleton, cls).__new__(cls)
return cls._instance
# Usage
obj1 = Singleton()
obj2 = Singleton()
print(obj1 is obj2) # True - same instance!

✅ Exactly one instance needed
✅ Expensive resource creation
✅ Global access required
✅ Shared state management
✅ Configuration management
✅ Logging systems

❌ Multiple instances might be needed
❌ Testing difficulties are a concern
❌ Hidden dependencies are problematic
❌ Distributed systems
❌ Violates Single Responsibility

  • Singleton Pattern = Only one instance exists
  • Private Constructor = Prevents external creation
  • Static Getter = Controlled access point
  • Benefit = Resource efficiency and global access
  • Caution = Use judiciously, can make testing harder
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super(Singleton, cls).__new__(cls)
return cls._instance
@classmethod
def get_instance(cls):
return cls()
# Usage
instance = Singleton.get_instance()
  • Singleton Pattern ensures only one instance exists
  • It provides controlled access to that instance
  • Use it for expensive resources or shared state
  • Don’t overuse it - can make code harder to test
  • Consider alternatives like dependency injection

What to say:

“Singleton Pattern is a creational design pattern that ensures a class has only one instance and provides a global point of access to that instance. It’s useful for managing shared resources like database connections, loggers, or configuration managers.”

Why it matters:

  • Shows you understand the fundamental purpose
  • Demonstrates knowledge of when to use it
  • Indicates you can explain concepts clearly

Must mention:

  • Exactly one instance needed - Multiple instances would cause problems
  • Expensive resource - Database connections, file handles
  • Global access - Need to access from anywhere
  • Shared state - Single source of truth
  • Configuration - Application-wide settings

Example scenario to give:

“I’d use Singleton Pattern for a database connection manager. Creating multiple connection managers would waste resources and cause connection pool conflicts. With Singleton, all parts of the application share the same connection pool.”

Must explain:

  1. Private Constructor - Prevents external instantiation
  2. Static Instance Variable - Stores the single instance
  3. Static Getter Method - Returns the instance (creates if needed)
  4. Controlled Creation - Ensures only one instance exists

Visual explanation:

Client → getInstance() → Singleton Instance (created once, reused always)

Must discuss:

  • Problem: Multiple threads can create multiple instances
  • Solution: Use synchronization (locks, synchronized blocks)
  • Double-Check Locking: Check twice, lock once

Example to give:

“In a multi-threaded environment, multiple threads might check if the instance is null at the same time and both create instances. To prevent this, I use double-check locking - check if instance is null, acquire lock, check again, then create.”

Benefits to mention:

  • Resource Efficiency - Expensive objects created only once
  • Global Access - Easy to access from anywhere
  • Controlled Access - Single point of control
  • Memory Efficient - No waste from multiple instances

Trade-offs to acknowledge:

  • Testing Difficulties - Hard to mock and test
  • Hidden Dependencies - Global state hides dependencies
  • Tight Coupling - Creates global coupling
  • Concurrency Issues - Can cause problems in distributed systems

Q: “What’s the difference between Singleton and Static Class?”

A:

“A Singleton is an instance of a class that can be created, while a static class cannot be instantiated. Singleton can implement interfaces and be passed as parameters, while static classes cannot. Singleton allows lazy initialization, while static classes are initialized when the class is loaded.”

Q: “How do you make Singleton thread-safe?”

A:

“I use double-check locking - first check if instance is null without locking (for performance), then acquire a lock, check again (to prevent race conditions), and create the instance if still null. Alternatively, I can use eager initialization or synchronized methods, but double-check locking provides the best balance of thread safety and performance.”

Q: “What are the problems with Singleton Pattern?”

A:

“Singleton Pattern can make unit testing difficult because you can’t easily mock the singleton. It also creates hidden dependencies and tight coupling. In distributed systems, each process has its own singleton instance, which can cause inconsistencies. Additionally, it violates the Single Responsibility Principle if the singleton does too much.”

Key implementation points:

  1. Private Constructor - Prevents external instantiation

    def __new__(cls):
    if cls._instance is None:
    cls._instance = super(Singleton, cls).__new__(cls)
    return cls._instance
  2. Thread-Safe Implementation - Use locks for multi-threaded environments

    _lock = threading.Lock()
    with cls._lock:
    if cls._instance is None:
    cls._instance = super(Singleton, cls).__new__(cls)
  3. Lazy vs Eager Initialization - Choose based on needs

    • Lazy: Create when first accessed
    • Eager: Create when class is loaded
  4. Serialization (Java) - Implement readResolve() to prevent new instances

Good examples to mention:

  • Database Connection Manager - Single connection pool
  • Logger - One logger instance for the application
  • Configuration Manager - Application-wide settings
  • Cache Manager - Single cache instance
  • Thread Pool Manager - Single thread pool

Mistakes interviewers watch for:

  1. Not Thread-Safe - Can create multiple instances

    • ❌ Bad: No synchronization in multi-threaded environment
    • ✅ Good: Use double-check locking or synchronized methods
  2. Public Constructor - Allows external instantiation

    • ❌ Bad: Public constructor
    • ✅ Good: Private constructor
  3. Serialization Issues (Java) - Deserialization creates new instance

    • ❌ Bad: Serializable without readResolve()
    • ✅ Good: Implement readResolve() method
  4. Overuse - Using Singleton for everything

    • ❌ Bad: Singleton for classes that need multiple instances
    • ✅ Good: Use Singleton only when exactly one instance is needed

Be ready to discuss:

  • Dependency Injection - Pass instance as parameter
  • Service Locator - Central registry for services
  • Monostate Pattern - All instances share the same state
  • Factory Pattern - Can return the same instance

When to use alternatives:

“If testing is a priority, I might use dependency injection instead of Singleton. This makes it easier to mock dependencies and test components in isolation.”

Before your interview, make sure you can:

  • Define Singleton Pattern clearly in one sentence
  • Explain when to use it (with examples)
  • Describe the structure and components
  • Implement thread-safe Singleton
  • Discuss thread safety and synchronization
  • List benefits and trade-offs
  • Identify common mistakes
  • Compare with alternatives (static class, dependency injection)
  • Give 2-3 real-world examples
  • Explain problems with Singleton Pattern

Remember: Singleton Pattern ensures only one instance exists, providing controlled access to shared resources. Use it judiciously - it’s powerful but can make code harder to test! 🎯