Skip to content
Creational

Singleton

Ensure a class has only one instance and provide a global point of access to it.

Intent

Restrict the instantiation of a class to a single object and provide a well-known access point for that instance, ensuring coordinated access to a shared resource.

Problem

Multiple parts of your application need access to the same shared resource — such as a configuration store, connection pool, or logging service — but creating multiple instances would cause conflicts, duplicated state, or wasted resources. There is no built-in language mechanism to guarantee a single instance while still allowing lazy initialization.

Solution

Make the default constructor private and provide a static creation method that acts as the constructor. On the first call it creates the instance and caches it; on subsequent calls it returns the cached instance. Thread safety must be considered in concurrent environments.

Participants

  • Singleton — the class that manages its own unique instance and provides a static accessor
  • Client — any code that obtains the instance through the static accessor rather than direct construction

Advantages

  • Guarantees that only one instance of the class exists throughout the application lifetime
  • Provides a global access point to that instance without relying on global variables
  • The instance is created only when first requested, enabling lazy initialization

Disadvantages

  • Violates the Single Responsibility Principle by coupling instance management with business logic
  • Can mask bad design by hiding dependencies instead of making them explicit via constructor injection
  • Makes unit testing difficult because the global state persists between tests
  • Requires special handling in multi-threaded environments to avoid race conditions during initialization

Real-World Analogy

A country has exactly one government. Regardless of the personal identities of the individuals who form governments, the title 'The Government of X' is a global point of access that identifies the group in charge. You don't create a new government each time someone needs to interact with it — you access the existing one.

Use Cases

  • Database connection pool shared across an application
  • Application-wide configuration or settings registry
  • Centralized logging service
  • Hardware interface access such as a printer spooler
  • Caching layer that must be consistent across modules

Code Examples

singleton.ts
class DatabaseConnection {
  private static instance: DatabaseConnection | null = null;

  private readonly connectionId: string;
  private readonly connectedAt: Date;

  // Private constructor prevents direct instantiation
  private constructor(private readonly host: string, private readonly port: number) {
    this.connectionId = crypto.randomUUID();
    this.connectedAt = new Date();
  }

  static getInstance(host = "localhost", port = 5432): DatabaseConnection {
    if (!DatabaseConnection.instance) {
      DatabaseConnection.instance = new DatabaseConnection(host, port);
    }
    return DatabaseConnection.instance;
  }

  query(sql: string): string {
    return `[${this.connectionId}] Executing on ${this.host}:${this.port}: ${sql}`;
  }

  getInfo(): { id: string; host: string; connectedAt: Date } {
    return { id: this.connectionId, host: this.host, connectedAt: this.connectedAt };
  }
}

// Usage
const db1 = DatabaseConnection.getInstance("db.example.com", 5432);
const db2 = DatabaseConnection.getInstance(); // returns the same instance

console.log(db1 === db2);          // true
console.log(db1.query("SELECT 1")); // [same-uuid] Executing on db.example.com:5432: SELECT 1

Thread-safe singleton using a private constructor and a static getInstance method with lazy initialization. TypeScript's module system naturally runs once, so the class-based approach is mainly useful when you need controlled, deferred creation.