Singleton Pattern — A Quick Guide
Design Patterns: Singleton Pattern
--
“Singleton Pattern ensures a class has only one instance, and provides a global point of access to it.”
Simple Example:
Suppose you need a carpenter for a sofa, chairs and a cupboard.
It’d be the best if you call a one carpenter and do all the work with him.
It wouldn’t be wise to call a different carpenter for each object.
Real-Life Example:
It’s very similar to Simple Example. Let’s say you need a database client that you’ll handle all the CRUD operations.
You wouldn’t want to have multiple database clients since it will cause unexpected behaviors such as memory issues and data corruption.
Implementation
public class Singleton { private static Singleton instance;
private Singleton(){} // hiding the constructor
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
// other methods
}
And the client code is:
Singleton singleton = Singleton.getInstance();
In the above implementation, the logic is such as:
if object does not exist → create and return object
if object exists → return already created object
Dealing with Multithreading
The problem with above code block is that in a multithreaded application objects might get initialized multiple times if threads access the same if block concurrently when getInstance() is used for the first time.
Let’s continue with possible solutions:
1. Eager Initialization
public class Singleton { private static Singleton instance = new Singleton();
private Singleton(){} // hiding the constructor
public static Singleton getInstance() {
return instance;
}
// other methods
}
Using this approach, we pass the responsibility to JVM and it initializes our Singleton instance when the class is loaded, so multithreading is not an issue.
You can use this if you are sure you’ll use getInstance() at least one time in your application.
2. Locking the Initialization
public class Singleton { private static Singleton instance;
private Singleton(){} // hiding the constructor
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
// other methods
}
We can lock the getInstance() by using synchronized keyword and force other threads to wait current thread to exit before entering this code block.
While the above example is correct (and used in most examples), this is a bad practice. We don’t need to lock the method every time getInstance() is called, we need to lock it the first time it’s being called. Since locking is a performance-wise expensive operation, we had better not lock whole method.
public class Singleton { private static volatile Singleton instance;
private Singleton(){} // hiding the constructor
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
// other methods
}
Voila!
Things to Consider
While Singleton Pattern is great and widely being used, it’s sometimes considered as an anti-pattern since it can help us mask bad design.
1. Violating loose-coupling
When we pass Singleton classes around like Singleton.getInstance(),
we may not realize the classes of our app know too much about each other, being too tightly-coupled.
For example passing the database client all over our app is a bad practice.
2. Violating Single Responsibility Principle
Singleton Pattern both handles ensuring a class has only one instance and providing an access point to it, which makes it carry two huge responsibilities on its shoulders.
3. Singleton Pattern vs Static Classes
We can make Singleton lazy-initialized with our if check above, static methods are lazy-initialized by default.
Singleton allows method overriding, static methods don’t allow overriding but can achieve similar behavior by method hiding.
When to use static class: If you are planning to write helper methods such as math, array manipulations etc., it can be easier to just use a class full of static helper methods.
When to use Singleton: Rest of everything. Singleton Class is just a normal class following normal OOP principles.
4. Singleton Pattern vs Dependency Injection
If you are already using a framework that handles injections, it may be a better practice to just inject classes instead of applying Singleton Pattern.
I prefer to first check if it’s a helper method, if yes, create a static class.
If not, check our DI availabilities. If yes, use DI. If not, use Singleton.
Thank you for reading and being a part of my journey!
Reference(s)
- Head First Design Patterns. by Eric Freeman, Elisabeth Robson, Bert Bates, Kathy Sierra. Released October 2004. Publisher(s): O’Reilly Media, Inc.