Singleton Pattern in TypeScript — ✨ Creational Design Pattern #5
🙋🏻♀️ What is the Singleton Pattern?
The Singleton Pattern is a design approach that limits the creation of a class to only one instance which is accessible from all parts of the code globally.
The term singleton
comes from the mathematical concept of a singleton set
which is a set that contains exactly one element. In programming, the Singleton Pattern is a way of implementing this concept for classes.
How is it different from using Global Variables?
Using Singleton Pattern might sound like using global variables, but they are different! The Singleton Pattern can provide a more encapsulated way to manage shared data, compared to using global variables.
Global variables are typically defined and initialized outside of a class, while the Singleton Pattern is implemented as a class (with a private constructor and a static method) to retrieve the single instance of the class.
Moreover, in the case of a global variable, any part of the program can access and modify its value, which can lead to data inconsistency and make it difficult to debug. On the other hand, the Singleton Pattern ensures that there is only one instance of a class, and provides a controlled way to access and modify the state of that instance, ensuring that the state of the program is consistent across all parts of the code.
✨ Simple TypeScript Example
In TypeScript, how can you ensure that only a single instance is created within a class? This can be achieved by excluding a public constructor. and including a private constructor and a static method called getInstance()
.
class Store {
private static instance: Store
private count: number
private constructor() {
this.count = 0
}
static getInstance(): Store {
if (!Store.instance) {
Store.instance = new Store()
}
return Store.instance
}
readStore(): void {
console.log(`Count: ${this.count}`)
}
getCount(): number {
return this.count
}
setCount(value: number): void {
this.count = value
}
}
The class Store
has a private static variable instance
which is an instance of the Store
class. The constructor
method of the Store
class is marked private so that it cannot be called from outside the class. This ensures that no other instance of the Store
class can be created from outside the class.
The getInstance()
method is a static method that is responsible for creating and returning the instance of the Store
class. It first checks whether an instance of the class already exists. If an instance does not exist, it creates a new instance of the class and assigns it to the instance
variable. If an instance already exists, it simply returns that instance.
Let’s say you call the static method getInstance()
in A.ts, the instance of Store
class will be created for the first time. (This is called lazy initialization.)
// src/A.ts
const store = Store.getInstance()
store.readStore() // "Count: 0"
store.setCount(365)
store.readStore() // "Count: 365"
Then, if you call getInstance()
in B.ts, you will retrieve the only instance you created before, and get the consistent result.
// src/B.ts
const store2 = Store.getInstance()
store.readStore() // "Count: 365"
store2.setCount(0)
store.readStore() // "Count: 0"
You can also try eager initialization if you’re concerned about multithreading issues. Here, the instance
variable is initialized with the new
keyword when the class is loaded into memory.
class Store {
private static instance = new Store()
...
static getInstance(): Store {
return Store.instance
}
🧑🏻💻 Use it or Avoid it
When to use it
The Singleton Pattern is useful when you want to ensure that there is only one☝️️ instance of a particular class in your program. Examples are when you want to save resources, make consistent results, or avoid unexpected behavior in your program due to reading and writing on different instances.
When to avoid it
However, it’s important to be cautious when using the Singleton Pattern, because a singleton
class may tightly couple different parts of your code, violating SRP(Single Responsibility Principle). The singleton
class can manage its instance, provide global access to that instance, and perform other operations related to its purpose. These multiple responsibilities can make the class more complex, harder to write test codes, and eventually harder to maintain. (So, you may want to separate other operations into different classes.)
In short, Singleton Pattern is a design pattern that ensures that a class has only one instance and provides a global point of access to it.