The Singleton Design Pattern

Have you ever been on a team where you need to start a project from scratch? That’s usually the case in many start-ups and other small companies.
There are a lot many different programming languages, architectures, and other concerns that it can be difficult to figure out where to start. That’s where design patterns come in.
A design pattern is like a template for your project. It uses certain conventions and you can expect a specific kind of behavior from it. These patterns were made up of many developers’ experiences so they are really like different sets of best practices.
And you and your team get to decide which set of best practices is the most useful and convenient for your project. Based on the design pattern you choose, you all will start to have expectations for what the code should be doing and what vocabulary you all will be using.
The Singleton Design Pattern
The singleton pattern only allows a class or object to have a single instance and it uses a global variable to store that instance. You can use lazy loading to make sure that there is only one instance of the class because it will only create the class when you need it.
That prevents multiple instances from being active at the same time which could cause weird bugs. Most of the time this gets implemented in the constructor. The goal of the singleton pattern is typically to regulate the global state of an application.
An example of a singleton that you probably use all the time is your logger.
If you work with some of the front-end frameworks like React or Vue, you know all about how tricky it can be to handle logs coming from multiple components. This is a great example of singletons in action because you never want more than one instance of a logger object, especially if you’re using some kind of error tracking tool.
class FoodLogger {
constructor() {
this.foodLog = []
}
log(order) {
this.foodLog.push(order.foodItem)
// do fancy code to send this log somewhere
}
}
// this is the singleton
class FoodLoggerSingleton {
constructor() {
if (!FoodLoggerSingleton.instance) {
FoodLoggerSingleton.instance = new FoodLogger()
}
}
getFoodLoggerInstance() {
return FoodLoggerSingleton.instance
}
}
module.exports = FoodLoggerSingleton
An example of the singleton class
Now you don’t have to worry about losing logs from multiple instances because you only have one in your project. So when you want to log the food that has been ordered, you can use the same FoodLogger instance across multiple files or components.
const FoodLogger = require('./FoodLogger')
const foodLogger = new FoodLogger().getFoodLoggerInstance()
class Customer {
constructor(order) {
this.price = order.price
this.food = order.foodItem
foodLogger.log(order)
}
// other cool stuff happening for the customer
}
module.exports = Customer
An example of a Customer class using the singleton
const FoodLogger = require('./FoodLogger')
const foodLogger = new FoodLogger().getFoodLoggerInstance()
class Restaurant {
constructor(inventory) {
this.quantity = inventory.count
this.food = inventory.foodItem
foodLogger.log(inventory)
}
// other cool stuff happening at the restaurant
}
module.exports = Restaurant
An example of the Restaurant class using the same singleton as the Customer class
With this singleton pattern in place, you don’t have to worry about just getting the logs from the main application file. You can get them from anywhere in your code base and they will all go to the exact same instance of the logger, which means none of your logs should get lost due to new instances.