Design patterns are essential tools for building robust, maintainable, and scalable applications. In the context of JavaScript, understanding and effectively utilizing design patterns can set you apart in frontend interviews. In this interview article, we will be exploring more about the Singleton Design Pattern which promotes Consistency and Efficiency.
1. Singleton Design Pattern
The Singleton Pattern ensures that a class has only one instance and provides a global point of access to it. This pattern is useful for managing global state or single points of coordination.
class Singleton {
constructor() {
if (Singleton.instance) {
return Singleton.instance;
}
Singleton.instance = this;
this.state = "initial state";
}
getState() {
return this.state;
}
setState(newState) {
this.state = newState;
}
}
const instance1 = new Singleton();
const instance2 = new Singleton();
console.log(instance1 === instance2); // Output: true
instance1.setState("new state");
console.log(instance2.getState()); // Output: "new state"
Why is the Singleton Pattern Important?
- Global State Management: The Singleton Pattern ensures that a class has only one instance, making it ideal for managing a global state or single points of coordination. This is particularly useful when you need a centralized instance to manage resources like configuration settings, logging, caching, or database connections.
- Resource Management: By ensuring a single instance, the Singleton Pattern helps in managing resources efficiently. It avoids the overhead of creating multiple instances, thereby saving memory and improving performance.
- Consistency: Singleton ensures that all parts of your application use the same instance, leading to consistent behaviour across your application.
- Easy Access: It provides a global point of access to the instance, making it easier to manage and use in various parts of an application.
Real-Time Example: Logger
class Logger {
constructor() {
if (Logger.instance) {
return Logger.instance;
}
Logger.instance = this;
this.logs = [];
}
log(message) {
this.logs.push(message);
console.log(`LOG: ${message}`);
}
printLogCount() {
console.log(`${this.logs.length} Logs`);
}
}
const logger1 = new Logger();
const logger2 = new Logger();
logger1.log("First log");
logger2.log("Second log");
logger1.printLogCount(); // Output: 2 Logs
logger2.printLogCount(); // Output: 2 Logs
console.log(logger1 === logger2); // Output: true
Explanation of the Example
Let’s break down the provided Logger example to understand how the Singleton Pattern is implemented and why it’s beneficial.
- Constructor with Singleton Logic:
- The
Logger
class has a constructor that checks if an instance of the class already exists. if (Logger.instance) { return Logger.instance; }
– If an instance already exists, the constructor returns the existing instance instead of creating a new one.Logger.instance = this;
– If no instance exists, the constructor creates a new instance and assigns it toLogger.instance
.
- The
- Logging Functionality:
- The
log
method allows you to add a message to thelogs
array and prints it to the console. - The
printLogCount
method prints the number of logs recorded.
- The
- Using the Singleton:
- Two instances,
logger1
andlogger2
, are created using thenew Logger()
syntax. - Because of the Singleton logic, both
logger1
andlogger2
reference the same instance. logger1.log("First log");
andlogger2.log("Second log");
both add logs to the samelogs
array.- When you call
printLogCount
in either instance, it prints2 Logs
, showing that both instances share the same log data. console.log(logger1 === logger2);
confirms that bothlogger1
andlogger2
are indeed the same instance by printingtrue
.
- Two instances,
Importance in Real-World Applications
In a real-world application, the Singleton Pattern ensures that critical parts of your application, such as logging mechanisms or configuration settings, are managed through a single instance, preventing inconsistencies and reducing resource usage. This pattern is particularly useful in environments where creating multiple instances of a resource-heavy class (e.g., a database connection or a logging service) can lead to performance issues or unexpected behaviour.
By using the Singleton Pattern, you ensure that such classes are only instantiated once and are easily accessible throughout your application, promoting efficient resource usage and consistent behaviour.