Implementing Singleton Pattern in TypeScript: A Simple Guide
Master the Singleton Pattern in TypeScript with This 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?
Restrict the creation of a class instance outside the class by making the constructor private.
Add a private static global variable in the class of the same class type.
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.