Singleton Design Pattern
Well, it's been a while since my last post! I have been exploring software design principles to improve my development skills.
These patterns help you create clean, extensible, and robust code, reducing the number of breaking changes in your releases and reducing the effort required for code maintainability.
Design patterns are broadly divided into below categories
- Creational Patterns - They are concerned with the way in which objects are created.
- Structural Patterns - They explain how to assemble objects and classes into larger structures while keeping these structures flexible and efficient.
- Behavioural Patterns - They are concerned with algorithms and the assignment of responsibilities between objects.
This is the first post in the Design pattern series and I'll keep posting new ones in the coming weeks.
For understanding purposes, many things are shared in the context of Java-Springboot.
Problem Statement
The Singleton design pattern is to -
- Have a single instance of a class in a JVM.
- Global Point of Access for the object.
- Create Heavy Weight/Super Classes.
In Java Springboot,
- To mark a class as Singleton, annotate the class with @Singleton.
- Singleton instance is initialised lazily (created only when required). To force the creation of a singleton, annotate the class with @startup.
- The instance thus created is mostly used in @PostConstruct and @PreDestroy.
When using Singleton, the following things should be taken in mind -
Multithreading
Special care should be taken when a singleton has to be used in a multithreading application.
Serialization
When Singletons are implementing a Serializable interface they have to implement the readResolve method in order to avoid having 2 different objects.
Class Loaders
If 2 different class loaders load the Singleton class we'll have 2 different classes, one for each class loader.
Global Access Point
Represented by the class name - The singleton instance is obtained using the class name. The first view this is an easy way to access it, but it is not very flexible. If we need to replace the Singleton class, all the references in the code should be changed accordingly.
How to create Singleton Class?
The following conditions need to be met -
- All the attributes of the class need to be private and static.
- The constructor needs to be private
- A static synchronized method for retrieving the instance
Example Code -
class Singleton
{
private static Singleton instance;
private Singleton()
{
// Instantiating code
}
public static Singleton getInstance()
{
if (instance == null)
{
synchronized(Singleton.class)
{
if (instance == null) instance = new Singleton();
}
}
return instance;
}
public void doSomething()
{
System.out.println("doSomething(): Singleton does something!");
}
}
Uses of Singleton Classes
- Logger Class
- Configuration Class
- Accessing resources in a shared mode like Databases, Message Queue Factories
- Factories implemented as Singleton.
Conclusion
The Singleton pattern is a useful design paradigm for centralising access points to certain types of resources providing you global access to the instance.
- This pattern however violates the Single Responsibility Principle since it solves two problems at the same time.
- Overuse of Singletons can lead to "God" classes with a vast amount of code.
- This pattern also needs special treatment in the case of a multi-threaded environment.
- Also since the pattern uses a private Constructor, it is not easily mocked by testing frameworks that rely on inheritance to create mock objects.
Still, Singleton is one of the most widely used and understood Design patterns.
This is it for this post. Until next time...