Singleton Design Pattern
Deep dive into the Singleton Design Pattern with real-world examples
This pattern ensures that a class has only one instance and provides a global point of access to that instance.
Real World Analogy
Think about the role of a President (or Prime Minister) in a country. A nation can have many citizens, many government officials, and many distinct government departments. However, at any given moment, there is only one President.
This role is a Singleton.

Left: Without Singleton: Different clients directly instantiate the object they need. This results in multiple instances existing at once, each possibly having different internal data. Right: With Singleton: All clients must go through a static gatekeeper method. This checks if the single instance already exists. If it does, it returns it; if not, it creates it once and then returns it.

-
PresidentSingletonClass: This central box defines the structure. -
-instance: PresidentSingleton(Static): A private, static variable that holds the reference to the single instance. The minus sign (-) means private. Static means it belongs to the class itself, not to objects of the class. It initializes tonull. -
-PresidentSingleton()(Private Constructor): This is critical. The constructor is private. This means external code cannot callnew PresidentSingleton(). The pattern works by blocking the standardnewkeyword. -
+getInstance(): PresidentSingleton(Public Static): This is the global access point. It is public, static. The plus sign (+) means public. The entire system uses this method to interact with the singleton. It manages the creation logic (if null, create; otherwise, return existing).
Working:
It involves the following steps:
- Private Constructor: Prevents instantiation from outside the class
- Static Variables: Holds the single instance of the class.
- Public Static Method: Provides a global access point to get the instance. There are 2 ways of implementing it:
- Eager Loading
- Lazy Loading
Eager Loading (Early Initialization)
The resource is instantiated upfront as soon as the module/class is executed:
class DatabaseConnection{ constructor() { console.log("DB Connection Initialized"); } query(sql){ return `Executing query: ${sql}`; }}class EagerDatabaseService { //Static Property initialized immediately static instance = new DatabaseConnection(); static getInstance() { return this.instance; }}//Even before calling getInstance(), connection is already createdconsole.log("App Started");const db = EagerDatabaseService.getInstance();console.log(db.query("SELECT * FROM users"));Lazy Loading
The resource is held as null until someone calls the accessor method, instantiating it only on demand:
class DatabaseConnection { constructor() { console.log("Database connection initialized!"); } query(sql) { return `Executing query: ${sql}`; }}class LazyDatabaseService { static #instance = null; // Starts empty static getInstance() { if (!this.#instance) { console.log("Instance not found, creating now..."); this.#instance = new DatabaseConnection(); } return this.#instance; }}console.log("App started");// Nothing is initialized yet!// Instantiation happens here on the first requestconst db1 = LazyDatabaseService.getInstance();// Reuses the existing instance on subsequent requestsconst db2 = LazyDatabaseService.getInstance();console.log(db1 === db2); // true