Implementing Singleton Pattern in TypeScript: A Simple Guide

Master the Singleton Pattern in TypeScript with This Guide

Implementing Singleton Pattern in TypeScript: A Simple Guide

The Singleton Design Pattern is one of the simplest creational design patterns. It ensures that a class has only one instance and provides a way to access that instance.

There are many scenarios where it's necessary to limit clients from creating multiple instances of a class. This design pattern is applicable when:

  • Creating an object for each request is expensive.

  • A class has a shared state through the global variables used throughout the application.

  • It's undesirable to have code addressing a single problem dispersed across multiple classes.

Common scenarios for this design pattern include:

  • Creating a single database connection across an application.

  • Maintaining a single source of truth for user state.

  • Managing global configuration of the application.

How to Implement the Singleton Design Pattern?

  1. Restrict the creation of a class instance outside the class by making the constructor private.

  2. Add a private static global variable in the class of the same class type.

  3. Create a public static method that checks the state of the class instance variable, initializes it if necessary, and returns it.

In this blog, let's take the example of a Database Connection class and implement it using the Singleton Design Pattern with various approaches. We will understand the pros and cons of each approach.

Simple Approach

In this approach, we follow the above-mentioned steps to implement the Singleton Design Pattern using lazy initialization of the class instance.

class DBConnection {
  // Create a static class variable of the same class
  private static dbCon: DBConnection | null = null;

  // Private constructor restricts creation of the object outside the class
  private constructor() {}

  // This static method creates an instance of the class if it is null and returns it
  public static getInstance(): DBConnection {
    if (this.dbCon === null) {
      this.dbCon = new DBConnection();
    }
    return this.dbCon;
  }
}

Pros:

  • Easy implementation

  • Lazy object creation

Cons:

  • It is not thread-safe in a multi

-threaded environment.

Eager Initialization Approach

In this approach, we create an instance of the class using a static class variable. The TypeScript runtime ensures that a single instance of the class is created when the class is loaded into memory. This approach solves the problem of thread safety and ensures there is always a single instance of the class.

class DBConnection {
  // Create a private static class variable of the same class
  private static dbCon: DBConnection = new DBConnection();

  // Private constructor restricts creation of the object outside the class
  private constructor() {}

  // This static method returns the instance of the class
  public static getInstance(): DBConnection {
    return this.dbCon;
  }
}

Pros:

  • Easy implementation

  • Eager initialization of the class instance

  • Thread-safe as the instance of the class is created during class loading

Cons:

  • Waste of resources if the instance is not used in the application

  • Slow application startup if there are many classes with singleton design pattern implementation

  • Unable to pass parameters during the initialization of the instance

  • Exception handling is not possible

Lazy Initialization with Thread-safe Approach

In this approach, we use lazy initialization of the class instance through a class method. This method is marked as synchronized to resolve thread safety issues. This ensures that only one thread gets the lock for the class and executes the method.

class DBConnection {
  // Create a private static class variable of the same class
  private static dbCon: DBConnection | null = null;

  // Private constructor restricts creation of the object outside the class
  private constructor() {}

  // This static method creates an instance of the class if it is null and returns it
  public static getInstance(): DBConnection {
    if (this.dbCon === null) {
      this.dbCon = new DBConnection();
    }
    return this.dbCon;
  }
}

Pros:

  • Resolves the thread safety issue

  • Lazy initialization of the instance reduces memory waste

Cons:

  • Adds the overhead of wait time when multiple threads try to access the method

Drawbacks of the Singleton Pattern

  • This design pattern violates the Single Responsibility Principle as it tries to solve two problems.

  • It is difficult to perform unit testing.

  • This design pattern requires special attention in a multi-threaded environment.

I hope you enjoyed the blog and understood the concepts of the Singleton Design Pattern. Thank you. I appreciate your feedback and comments.

Did you find this article valuable?

Support Vishad Patel by becoming a sponsor. Any amount is appreciated!