TypeScript Singleton Design Pattern
What is singleton pattern?
Singleton is a creational design pattern in software engineering. It is a pattern that revolves around restricting the number of class instances to one, a single instance, throughout the application. Then it provides a global access point to that instance.
When should the singleton pattern be used?
The Singleton design pattern is mostly used in scenarios where there is no need for multiple instances of a class. It is applicable when an object needs to be shared across the entire project without alteration, such as with database connections or logger objects. The Singleton pattern is employed to reduce memory usage and avoid namespace pollution in the project.
To illustrate a clear image of the Singleton pattern, let’s use an analogy: lets say we are documenting every event that occurs in a neighborhood in a notebook with 100 pages, referred to as ‘log.’ Each time an event happens, we use one page of the notebook to log it. After recording the event, we close the notebook and place it on a shelf called the logger, disregarding the fact that there are still 99 pages left in the notebook. We repeat this process for each event, using a new, fresh notebook each time. What a wasteful usage!
If we were to apply the Singleton technique effectively, instead of squandering numerous notebooks, we could simply use one notebook for the entire project. This approach would allow us to conserve resources spent on buying notebooks and the space needed to store them.
Singleton design pattern UML diagram
How to implement singleton pattern in TypeScript
When implementing the Singleton pattern properly, we need to consider a few things.
First, we need to change the class constructor’s access modifier to private to prevent infinite instantiation.
Second, we need to utilize lazy initialization and thread safety techniques, as shown below in the code
class Singleton {
//An 'instance' class variable is created with the aim of providing the class instance to the outside world.
//To restrict direct access to the field,we set it with a private access modifier.
private static instance: Singleton;
//To prevent infinite instantiation of a class, the class constructor was made private.
private constructor() { }
//Within the 'getInstance' method, we utilized lazy initialization and thread safety techniques.
public static getInstance() : Singleton {
//Using lazy initialization, we ensured that if there is no instance of the class, a new one is created.
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
//By employing a thread safety method, we verified whether a class instance was locked and then utilized it.
return Singleton.instance;
}
}
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
//Output: True
console.log(instance1 === instance2);
Disadvantages of singleton
- Inheritance: Classes that are marked and used as singletons cannot be inherited due to the private constructor nature of singletons. This disadvantage of singletons prevents us from adding new properties and functions to the singleton class.
class user extends Singleton{
//Error: Cannot extend a class 'Singleton'. Class constructor is marked as private
}
- Control: When using the Singleton pattern, we do not have control over the singleton classes. We cannot determine whether the instance we are using is a newly created instance or a reserved instance of the singleton.
- Dependency Injection: Singletons conceal their dependencies due to the private constructor. This can make it challenging to modify the singleton class or employ dependency injection.
- Global state: The singleton instance, by its nature, is used throughout the entire application. That’s why if we manually make a change in the singleton class, the entire application will also change. This can potentially cause bugs and unpredictable errors within the application.
- Lifetime Management: A singleton typically exists throughout the entire lifecycle of an application. However, this feature may cause memory management issues if the data held by the singleton is no longer needed for the application.
- Testing and Mocking Difficulties: Singleton classes are often challenging to mock during testing. Since they typically have a private constructor, it isn’t easy to replace them with mock implementations, which makes unit testing more challenging.