Low-level design2 min
Singleton Pattern
Type: Creational Pattern
Relevance: Extremely high (very common in interviews), but often considered an anti-pattern in modern development if overused, because it introduces global state.
The Singleton pattern ensures that a class has only one instance and provides a global point of access to that instance.
Real-Life Analogy
Think of the President of a Country. A country can only have one active President at a time. Regardless of who asks to speak to the President, or from where they ask, they are always directed to the exact same person.
The Problem
- Global Access: You need access to some shared resource (like a database connection pool, a logger, or a configuration manager) from anywhere in your application.
- Instance Control: If you create two database connection managers, they might step on each other's toes or waste memory. You strictly need exactly one.
How to Implement Singleton
To create a Singleton:
- Make the default constructor
privateso other objects cannot use thenewoperator. - Create a
staticmethod (often calledgetInstance()) that acts as a constructor. This method checks if an instance already exists. If not, it creates it. If it does, it returns the existing one.
Example in Code
class DatabaseConnection {
// 1. Static variable that holds the single instance
private static instance: DatabaseConnection;
// 2. Private constructor prevents instantiation from outside
private constructor() {
console.log("Initializing database connection...");
}
// 3. Public static method to get the instance
public static getInstance(): DatabaseConnection {
if (!DatabaseConnection.instance) {
DatabaseConnection.instance = new DatabaseConnection();
}
return DatabaseConnection.instance;
}
public query(sql: string): void {
console.log(`Executing: ${sql}`);
}
}
// Usage
const db1 = DatabaseConnection.getInstance(); // Prints: "Initializing..."
const db2 = DatabaseConnection.getInstance(); // Does not print anything
console.log(db1 === db2); // true! They are the exact same instance in memory.
Class Diagram
Thread-Safety (Important for Java/C++ etc.)
In multi-threaded languages, two threads might call getInstance() at the exact same millisecond when the instance is null. Both might bypass the if (!instance) check and create two instances! To fix this, you must use locking (e.g., synchronized blocks in Java or Double-Checked Locking).
Why is it an Anti-Pattern?
- Hidden Dependencies: It hides dependencies across your application. Instead of injecting a database into a class, the class just magically summons it out of thin air via
Database.getInstance(). - Testing Nightmare: Because it's global state, tests can pollute each other. If Test A changes a setting in the Singleton, Test B might fail because it reads that modified setting.