The Singleton pattern is one of the oldest software design patterns and is widely used in many programming languages. It was originally described by the American software engineer GoF (Gang of Four) in the book "Design Patterns: Elements of Reusable Object-Oriented Software" published in 1994.
Singleton is one of the creational patterns that focuses on ensuring that a class only has a single instance throughout the entire application lifecycle and providing a global point of access to that instance. This pattern is often used in cases where it is necessary to ensure that there is only a single instance of a class, such as configuration managers, database connection managers, log managers, and so on.
There are a few ways to implement the Singleton pattern, but the most common is to create a private, static instance variable to hold the single instance, and provide a public static or instance method to get it. This method checks to see if the instance has already been created and, if not, creates a new instance before returning it. This ensures that there is only a single instance of the class throughout the application's lifecycle and that this instance can be easily accessed throughout the code.
The Singleton pattern has some advantages, which are:
- Single instance guarantee: The Singleton pattern guarantees that there is only a single instance of the class in the entire application lifecycle, which can be useful in cases where it is necessary to guarantee that there is only a single source of truth for a given away.
- Easy access to the single instance: The Singleton pattern provides an easy access point to the single instance, usually through a static or instance-specific method, which allows you to access the instance from anywhere in the code.
- Instantiation control: The Singleton pattern allows controlling class instantiation, which is useful in cases where it is necessary to ensure that the class is instantiated only when really necessary or in cases where it is necessary to restrict the number of instances of the class.
- Ease of implementation: The Singleton pattern is easy to implement and can be added to an existing class without much modification.
- Performance: Creating a single instance of an object can improve performance by avoiding creating multiple instances of the same object that can consume more system resources.
Here is an example of singleton applicability in nodejs below:
class Logger {
constructor() {
if (Logger.instance) {
return Logger.instance;
}
this.logs = [];
Logger.instance = this;
}
log(message) {
this.logs.push(message);
console.log(`[${new Date().toISOString()}] ${message}`);
}
}
const logger1 = new Logger();
logger1.log("Mensagem 1");
const logger2 = new Logger();
logger2.log("Mensagem 2");
console.log(logger1 === logger2); // true, é a mesma instancia
In the above example, the Logger class has a static property called instance, which is used to store the single instance of the class. In the constructor, we check if an instance has already been created, and if so, we return that instance instead of creating a new one. If there is no instance, we create a new one and store the instance in it and add a logs array where the log messages will be stored.
When creating new instances of Logger using the new operator, the constructor checks if an instance already exists and, if it does, returns that instance instead of creating a new one. In this way, all instances of that class are the same instance, ensuring that there is only a single source of truth for the log record.
Simple, right?
Imagine another scenario where each iteration made to the database creates a new instance of connections, and with that some insertions and changes made to the database return the following error Too many connections.
To reduce the error, our technical leader informs that it is necessary to create a single connection to the database in order to reduce the number of connections.
We can solve it with Singleton pattern, follow the resolution below:
const mysql = require('mysql2');
class Database {
constructor() {
if (Database.instance) {
return Database.instance;
}
this.connection = mysql.createConnection({
host: 'localhost',
user: 'username',
password: 'password',
database: 'mydb'
});
this.connection.connect();
Database.instance = this;
}
query(sql, args) {
return new Promise((resolve, reject) => {
this.connection.query(sql, args, (err, results) => {
if (err) {
return reject(err);
}
resolve(results);
});
});
}
close() {
this.connection.end();
Database.instance = null;
}
}
Here, the Database class has a static property called instance, which is used to store the single instance of the class. In the constructor, we check if an instance has already been created, and if so, we return that instance instead of creating a new one. If there isn't an instance, we create a new one and store an instance with the database connection settings, and the query and close methods in it.
query is used to make queries and close to close the connection. In this way, all instances of that class are the same instance, ensuring that there is only a single connection to the database.
The Singleton pattern is useful when we need to ensure that there is only a single instance of a given class in the entire system, and provide a global access point for that instance. This is useful in cases where we need to control access to shared resources like database connections, system settings, log files and more.
Some other situations where using Singleton is recommended include:
- When we need to ensure that an object only has one instance in the entire system and provide a global access point for it.
- When we need to control access to shared resources such as log files, database connections or system settings.
- When we need to ensure that an object is initialized only once and then reused multiple times over the lifetime of the application.
Conclusion
The Singleton pattern is a very useful design pattern for ensuring that there is only a single instance of a given class in the entire system, and providing a global access point for that instance.
Note: It is important to be careful not to overuse the Singleton. It can cause decoupling issues and make code difficult to test and maintain.
Hope this helps, until next time.
Top comments (0)