Singleton Pattern
Ensure a class has only one instance, and provide a global point of access to it.
Intent & Objective
Restrict the instantiation of a class to one single object throughout the application lifecycle, while providing a thread-safe, coordinated global access point.
"Just like you do not want multiple conflicting doctors giving you contradictory prescriptions, a Singleton ensures there is only one authoritative instance managing your medical records."
"Only one official government authority is legally empowered to issue recognized national passports, preventing duplicate and conflicting identification."
"A school has only one head principal overseeing operations, curriculum, and school-wide discipline."
"Exactly one alarm clock instance wakes you up in the morning—no duplicates allowed to cause conflicting chaos."
The Problem
Shared hardware access (e.g., printer spooler, serial communication port), global configuration management, or connection pooling require exactly one coordinator. Creating multiple instances causes race conditions, cache desynchronization, and resource exhaustion.
The Solution
Make the constructor private, store a static reference to the unique instance, and provide a static method (e.g. `getInstance()`) that returns this instance, lazily creating it on first request with thread safety.
Key Participants & Architectural Roles
Code Samples (Traditional OOP vs Functional Programming)
Available in C++, Python, and JavaParadigm Contrast: Traditional OOP vs Functional / Data-Oriented
In OOP, Singleton is implemented with private constructors and static accessors. In Functional Programming, mutable global state is discouraged. Instead, "Singleton" behavior is achieved through module-level closures, Reader Monads, or passing immutable configuration records through pure function pipelines.
Advantages
- ✓ Guarantees strict single-instance constraint
- ✓ Controlled access to sole resource with lazy initialization
- ✓ Avoids polluting global namespace with unencapsulated global variables
Trade-offs
- ✗ Can mask tight coupling and introduce hidden global state
- ✗ Difficult to unit test and mock without Dependency Injection
- ✗ Violates Single Responsibility Principle if it handles both its business role and its lifecycle
When to Apply
- → Managing access to shared external resources (hardware drivers, thread pools, audio engines)
- → Global application settings or configuration registry
- → Logging services and distributed tracer coordinators
When to Avoid
- ! Just to pass state across functions (use parameter passing or context instead)
- ! In multi-threaded environments where distributed clustering is used (a singleton per JVM/process is not unique across clusters)