1. Home
  2. /
  3. Docs
  4. /
  5. Design Pattern
  6. /
  7. Creational
  8. /
  9. Singleton

Singleton

What is the Singleton Pattern?

The Singleton pattern is a creational design pattern that ensures a class has only one instance throughout the application’s lifetime while providing a global point of access to that instance. Think of it like having only one president of a country at any given time – there can be elections and changes, but at any moment, there’s exactly one person in that role.

This pattern is particularly useful when you need to coordinate actions across your system from a single point, manage shared resources, or maintain a consistent state throughout your application.

Key Characteristics

  • Single Instance: Only one object of the class exists in the entire application
  • Global Access Point: The instance is accessible from anywhere in the code
  • Lazy or Eager Initialization: Can be created when first needed or at startup
  • Self-instantiation: The class itself controls its instantiation
  • Thread-Safe: Should handle concurrent access properly in multi-threaded environments

Structure and Flow

classDiagram
    class Singleton {
        -static instance: Singleton
        -Singleton()
        +static getInstance(): Singleton
        +businessLogic()
    }
    
    class Client1 {
        +usesSingleton()
    }
    
    class Client2 {
        +usesSingleton()
    }
    
    Client1 ..> Singleton: uses
    Client2 ..> Singleton: uses
    
    note for Singleton "Private constructor prevents\ndirect instantiation"

How It Works

The Singleton pattern works through a few key mechanisms:

  1. Private Constructor: The class constructor is made private, preventing external code from creating new instances using the new operator
  2. Static Instance Variable: A private static variable holds the single instance of the class
  3. Public Static Method: A public static method provides global access to the instance, creating it if it doesn’t exist

Pseudo Code

class Singleton:
    private static instance = null
    
    // Private constructor
    private Singleton():
        // Initialization code
    
    // Public method to get instance
    public static getInstance():
        if instance == null:
            instance = new Singleton()
        return instance
    
    public businessMethod():
        // Actual business logic

// Usage
singleton1 = Singleton.getInstance()
singleton2 = Singleton.getInstance()
// singleton1 and singleton2 refer to the same object

Access Flow Diagram

sequenceDiagram
    participant Client1
    participant Client2
    participant Singleton
    
    Client1->>Singleton: getInstance()
    alt Instance doesn't exist
        Singleton->>Singleton: Create new instance
    end
    Singleton-->>Client1: Return instance
    
    Client2->>Singleton: getInstance()
    Singleton-->>Client2: Return same instance
    
    Note over Client1,Client2: Both clients share the same instance

Real-World Use Cases

1. Database Connection Pool

Managing database connections is expensive. A Singleton pattern ensures that your application maintains a single connection pool manager that coordinates all database connections efficiently, preventing connection leaks and managing resources optimally.

Why Singleton? Creating multiple connection pool managers would waste resources and make it difficult to track and manage active connections across the application.

2. Logging Service

Application logging requires coordination to avoid conflicts when writing to log files. A Singleton logger ensures all parts of your application write to the same log file in a controlled, thread-safe manner, maintaining consistent formatting and preventing file access conflicts.

Why Singleton? Multiple logger instances could cause file locks, inconsistent log formats, and race conditions when writing simultaneously.

3. Configuration Manager

Applications often need to access configuration settings from various components. A Singleton configuration manager loads settings once and provides consistent access throughout the application, avoiding redundant file reads and ensuring all components use the same configuration values.

Why Singleton? Reading configuration files multiple times wastes I/O operations, and different instances might have inconsistent states if settings change during runtime.

4. Cache Manager

Caching systems store frequently accessed data in memory to improve performance. A Singleton cache manager ensures all application components share the same cache, maximizing hit rates and preventing memory waste from duplicate cached data.

Why Singleton? Multiple cache instances would duplicate data in memory and defeat the purpose of caching, as different parts of the app wouldn’t benefit from each other’s cached data.

5. Thread Pool Manager

Managing worker threads for concurrent operations requires centralized control. A Singleton thread pool manager allocates and reuses threads efficiently across the entire application, preventing thread explosion and resource exhaustion.

Why Singleton? Each thread pool manager would create its own set of threads, leading to uncontrolled thread proliferation and difficulty in managing system resources.

6. Device Driver Access

Hardware devices like printers or scanners typically allow only one active connection. A Singleton provides controlled access to the device driver, managing the queue of requests and ensuring operations don’t conflict.

Why Singleton? Multiple objects trying to control the same hardware simultaneously would cause conflicts, errors, or hardware malfunctions.

When to Use Singleton

flowchart TD
    A[Need for a class?] --> B{Multiple instances cause problems?}
    B -->|No| C[Don't use Singleton]
    B -->|Yes| D{Need global access?}
    D -->|No| E[Consider other patterns]
    D -->|Yes| F{Managing shared resource?}
    F -->|Yes| G[Use Singleton]
    F -->|No| H{Coordinating actions?}
    H -->|Yes| G
    H -->|No| C
    
    style G fill:#c8e6c9
    style C fill:#ffcdd2
    style E fill:#fff9c4

Consider using the Singleton pattern when:

  • You need exactly one instance of a class available to all clients
  • The single instance needs to be accessible from a well-known access point
  • The sole instance should be extensible through subclassing
  • You’re managing a shared resource like a file, database, or hardware device
  • You need to coordinate actions across the system

Benefits

  • Controlled Access: Strict control over how and when clients access the instance
  • Reduced Memory Footprint: Only one instance exists, saving memory
  • Global Access Point: Easy access from anywhere in the codebase
  • Lazy Initialization: Can defer creation until actually needed
  • Refinement of Operations: Can be subclassed to refine operations

Potential Drawbacks

  • Global State: Can make code harder to test and reason about
  • Hidden Dependencies: Classes using Singletons have hidden dependencies that aren’t obvious from their interfaces
  • Threading Issues: Requires careful implementation in multi-threaded environments
  • Violates Single Responsibility: The class handles both its business logic and instance management
  • Difficult to Unit Test: Hard to mock or replace in tests
  • Tight Coupling: Can create tight coupling between classes

Best Practices

  1. Thread Safety: Ensure your implementation is thread-safe in multi-threaded environments
  2. Lazy vs Eager: Choose initialization strategy based on resource cost and usage patterns
  3. Consider Alternatives: Sometimes dependency injection or static classes might be better
  4. Keep It Simple: Don’t overload the Singleton with too many responsibilities
  5. Document Clearly: Make it obvious why the Singleton pattern is used
  6. Avoid Overuse: Don’t make everything a Singleton just for convenience

Common Variations

Eager Initialization

The instance is created at class loading time, regardless of whether it will be used. This is thread-safe by default but may waste resources if the instance is never used.

Lazy Initialization

The instance is created only when first requested. This saves resources but requires thread-safety mechanisms in concurrent environments.

Double-Checked Locking

A performance optimization for lazy initialization that reduces locking overhead by checking if the instance exists before acquiring a lock.

Alternatives to Consider

Before implementing a Singleton, consider these alternatives:

  • Dependency Injection: Pass instances through constructors, making dependencies explicit
  • Static Classes: For stateless utility functions
  • Monostate Pattern: Multiple instances that share state
  • Factory Pattern: Control instance creation without enforcing a single instance

Key Takeaways

  • Singleton ensures only one instance of a class exists in your application
  • Ideal for managing shared resources like database connections, logs, or configuration
  • Provides global access but be mindful of the drawbacks like tight coupling and testing difficulties
  • Implementation must handle thread safety in concurrent environments
  • Use judiciously and consider alternatives like dependency injection when appropriate

The Singleton pattern is a powerful tool when used correctly, but it’s important to understand both its benefits and potential pitfalls. In the next post, we’ll explore the Factory Method pattern and see how it complements the Singleton in creating flexible, maintainable code.

Articles

Still stuck? Contact

How can we help?

Leave a Reply

Your email address will not be published. Required fields are marked *

All Rights Reserved 2025.