Back to Overview
Singleton Pattern
The Singleton Pattern is a creational design pattern that ensures a class has only one instance while providing a global point of access to that instance. It's useful when exactly one object is needed to coordinate actions across the system.
Problem
There are cases when we need to ensure that a class has just a single instance:
- To control access to a shared resource, such as a database or a file
- When a single point of coordination is needed (like a registry or a manager)
- To reduce resource usage when one object is sufficient
The regular constructor call always creates a new object, so we need a mechanism to ensure only one instance exists and provide global access to it.
Components
- Singleton Class: A class responsible for creating and maintaining its sole instance.
- Private Constructor: Prevents external instantiation of the class.
- Static Instance: A static field that holds the singleton instance.
- Static Access Method: Provides global access to the singleton instance (usually named getInstance()).
Implementation Example
Here's a basic implementation of the Singleton pattern in JavaScript:
class Singleton {
// Private static instance - holds the single instance
static #instance = null;
// Some state to demonstrate the singleton maintains state
#counter = 0;
// Private constructor - prevents direct instantiation
constructor() {
// Check if an instance already exists
if (Singleton.#instance) {
throw new Error("Cannot create multiple instances of Singleton. Use getInstance() instead.");
}
}
// Public static method to get the singleton instance
static getInstance() {
// Create the instance if it doesn't exist yet
if (!Singleton.#instance) {
Singleton.#instance = new Singleton();
console.log("Singleton instance created");
}
return Singleton.#instance;
}
// Example of instance methods that maintain state
incrementCounter() {
return ++this.#counter;
}
getCounter() {
return this.#counter;
}
}
Usage Example
// This is how clients use the singleton
function clientCode() {
// Get the singleton instance
const singleton1 = Singleton.getInstance();
console.log(`Counter value: ${singleton1.getCounter()}`);
// Increment the counter
singleton1.incrementCounter();
console.log(`After increment: ${singleton1.getCounter()}`);
// Get the singleton instance again - it's the same instance
const singleton2 = Singleton.getInstance();
console.log(`Second reference counter: ${singleton2.getCounter()}`);
// They are the same object
console.log(`Are they the same instance? ${singleton1 === singleton2}`);
}
// Run the client code
clientCode();
Interactive Demo: Logger Singleton
This demo demonstrates a Logger implemented as a Singleton. All clients get the same instance, allowing for centralized logging.
Instance 1: Not created yet
Instance 2: Not created yet
Log Count: 0
Instance 2: Not created yet
Log Count: 0
When to Use
- When you need exactly one instance of a class accessible to all clients
- When you need stricter control over global variables
- For shared resources like a configuration manager, connection pool, or cache
- For coordinating actions across the system
Benefits
- Ensures a class has only one instance
- Provides a global access point to that instance
- Controls when and how the instance is initialized
- Permits a variable number of instances (variants can support a controlled number of instances)
- More flexible than static methods (instance can be subclassed, etc.)
Considerations
- Thread Safety: In multi-threaded environments, care must be taken to ensure thread-safe instantiation.
- Global State: Singletons introduce global state which can make testing and debugging more difficult.
- Tight Coupling: Classes that use a singleton are tightly coupled to it, which can hinder extensibility.
- Dependency Hiding: Singletons can hide dependencies, making it harder to understand and maintain code.
Real-World Uses
- Database connection pools
- Logger implementations
- Configuration managers
- Caching mechanisms
- Thread pools
- Device drivers for hardware like printers
- Dialog managers in GUI applications