A guide to software design patterns
Creational Design Pattern: Singleton
The Singleton design pattern is one of the simplest and easiest patterns to implement. It is a pattern that restricts the instantiation of a class to one single instance.
That simply means a class is responsible for creating a single instance of itself and no matter how many times you get an instance of that class, you will always get the only and same created instance. It is one of the five creational patterns, and the creational pattern is one of the three design pattern categories.
There are many times where using this pattern is beneficial. Consider this scenario. You have been tasked to write a class to access a database. Your class will be used by a module to perform basic CRUD (create, read, update and delete) operations. These operations will be performed by a single user per session.
You could implement your class to create a new connection every time a CRUD action is performed. Even though you’re still using the same session, you would end up opening and closing many connections. A better approach would be to implement the singleton pattern to prevent your application from opening multiple connections instead of just once per session. Another good scenario would be a logging class. You don’t need to create a new instance every time you need to log something in your application, creating one instance would be ideal and improve performance. I’m sure different engineers might have a different use case for when the pattern should be used and that’s perfectly fine. There’s more than one way to skin a cat. However, we’re interested in understanding the pattern benefits and how it’s implemented.
We’re going to implement this pattern using both eager and lazy initialization starting with eager first. Using eager initialization simply means we’ll create our first and only instance of our class, initialize it and return it when an instance is requested. I’m going to be using java, but implement this in other languages should be similar.
Eager initialization
public class DBConnection { //create the only private instance of the class
private static DBConnection instance = new DBConnection(); //prevents the class from getting instantiated
private DBConnection(){}
public static DBConnection getInstance(){
return instance;
}
public void executeAction(){
System.out.println("CRUD action execute successfully");
}
}Lazy initialization
Lazy initialization is the opposite of eager initialization. It means we’ll create our instance and initialize it the first time an instance is requested if one hasn’t been created, otherwise we’ll just return the existing one instead of the eager approach.
public class LazyDBConnection {
private static volatile LazyDBConnection instance; //prevents the class from getting instantiated
private LazyDBConnection(){}
public static LazyDBConnection getInstance(){ if (instance == null) {
instance = new LazyDBConnection();
}
return instance;
}
public void executeAction(){
System.out.println("CRUD action execute successfully");
}
}When developing, it’s good to keep in mind if your application needs to be single or multi-threaded. The code above would work fine for a single-threaded application, but would not work for a multi-threaded application. The reason is that if two threads enter the if code block at the same time and the instance is null, two instances will get created. That is a problem and we need to fix that. We can accomplish that by using synchronizing the code in the if block. Our code would now look the same with one extra check to prevent threads from creating multiple instances.
public class LazyDBConnection {
private static volatile LazyDBConnection instance;
//prevents the class from getting instantiated
private LazyDBConnection(){}
public static LazyDBConnection getInstance(){
if (instance == null) {
synchronized (LazyDBConnection.class) {
instance = new LazyDBConnection();
}
}
return instance;
}
public void executeAction(){
System.out.println("CRUD action execute successfully");
}
}Both can be used the same way. To get an instance, you just call the static getInstance method in the class. Then you can use that instance to do whatever the class allows. That would then make our code thread-safe. The code would now look like this.
public static void main(String[] args) {
DBConnection eagerInstance = DBConnection.getInstance();
eagerInstance.executeAction();
LazyDBConnection lazyInstance = LazyDBConnection.getInstance();
lazyInstance.executeAction();
}There are many other scenarios where the Singleton pattern won’t be the ideal choice, but there are times when it’s beneficial to use it. It is up to you as an engineer to use your best judgment and use it when it makes sense. Thank you for making it to the end. Happy coding.
Previous: Design Pattern Explained In Five Minutes
