Singleton is a creational design pattern. This pattern involves a single class which is responsible to create an object while making sure that only single object gets created. It is used for created an object in memory only once in an application and have it shared by multiple classes.
We sometimes need to an object’s only one instance. For example, we want to create modelling solar system. We create some classes that naming Sun, Mars, Earth, Mercury, Venus, Jupiter, Saturn, Uranus, Neptune, Pluto. So in this case we have to create one instance for each planet. If we create multiple instance, It will be absurd because there will be multiple planet as same naming in this situation for example two earth, three mars, four sun etc. So we have to use singleton pattern for create planets.
Additionally, all of the constructors are declared private in the singleton design pattern, then it is impossible to create a subclass with a valid constructor therefore the singleton class is effectively final.
Implementations
I will explain four forms. Classic Implementation, Implementation with Static Initialization Block and Lazy Instantiation with Non Thread Safe and Thread Safe.
Classic Implementation
We can do classic implementation with following steps…
- All Constructors’ Access Modifier of Class should be setting private.
- Create a private static final variable of class and initialize it.
- Requires a public static method to retrieve the instance of the singleton
I shared a instance for classic implementation.
/**
* @author Hikmet
* @since 20-05-2022+03:00
*/
public class Earth {
// It's used for indicate to Earth's living population
private int population = 0;
// It indicate Earth our used
private static final Earth instance = new Earth();
// We don't want to multiple instance so we have to set access modifier to private
private Earth() {
}
// This method is used for get to Earth
public static Earth getInstance() {
return instance;
}
// If you want to increase population, you have to use this method
public void addToPopulation(int addAmount) {
population += addAmount;
}
// If you want to decrease population, you have to use this method
public void subtractFromPopulation(int subtractAmount) {
population -= subtractAmount;
}
// If you want to get to population, you have to use this method
public int getPopulation() {
return population;
}
}
Implementation with Static Initialization Block
Creating singleton’s instance is done inside the static initialization block. This implementation is equivalent with Classic Implementation because these two implementations create the singleton when class is loaded.
Static initialization block usage gives some capabilities to us. We can add additional steps for singleton’s setting after singleton has been created. Let’s check out following example…
/**
* @author Hikmet
* @since 20-05-2022+03:00
*/
public class Earth {
// It's used for indicate to Earth's living population
private int population = 0;
// It indicate Earth our used
private static Earth instance;
// It's used for initialize to instance and more...
static {
instance = new Earth();
// You can add whatever you want.
}
// We don't want to multiple instance so we have to set access modifier to private
private Earth() {
}
// This method is used for get to Earth
public static Earth getInstance() {
return instance;
}
// If you want to increase population, you have to use this method
public void addToPopulation(int addAmount) {
population += addAmount;
}
// If you want to decrease population, you have to use this method
public void subtractFromPopulation(int subtractAmount) {
population -= subtractAmount;
}
// If you want to get to population, you have to use this method
public int getPopulation() {
return population;
}
}
Lazy Instantiation
This technique provide delay creation of the singleton. Unless there is any call for create class instance, Singleton’s instance isn’t created.
/**
* @author Hikmet
* @since 20-05-2022+03:00
*/
public class Earth {
// It's used for indicate to Earth's living population
private int population = 0;
// It indicate Earth our used
private static Earth instance;
// We don't want to multiple instance so we have to set access modifier to private
private Earth() {
}
// This method is used for get to Earth
public static Earth getInstance() {
if(instance == null) {
instance = new Earth(); // Don't forget this situation is not THREAD SAFE!
}
return instance;
}
// If you want to increase population, you have to use this method
public void addToPopulation(int addAmount) {
population += addAmount;
}
// If you want to decrease population, you have to use this method
public void subtractFromPopulation(int subtractAmount) {
population -= subtractAmount;
}
// If you want to get to population, you have to use this method
public int getPopulation() {
return population;
}
}
Do you realize that I added to if block inside to getInstance(). We don’t create instance until getInstance() method is called.
Lazy Instantiation with Thread Safe.
If we want to thread safety for singleton. We can use following code pieces. We add synchronized keyword to methods and inside “getInstance()” method because synchronized keyword prevents concurrent access to a block of code or object by multiple threads.
synchronized keyword should add inside “getInstance()”. If we add to method, this will be costly because “getInstance()” method’s every call will require synchronization. So we use double checked locking.
Additionally, volatile keyword was added to instance variable. Volatile tells the compiler that the value of a variable must never be cached as its value may change outside of the scope of the program itself.
/**
* @author Hikmet
* @since 20-05-2022+03:00
*/
public class Earth {
// It's used for indicate to Earth's living population
private int population = 0;
// It indicate Earth our used
private static volatile Earth instance;
// We don't want to multiple instance so we have to set access modifier to private
private Earth() {
}
// This method is used for get to Earth
public static Earth getInstance() {
if(instance == null) {
synchronized (Earth.class) {
if(instance == null) {
instance = new Earth();
}
}
}
return instance;
}
// If you want to increase population, you have to use this method
public synchronized void addToPopulation(int addAmount) {
population += addAmount;
}
// If you want to decrease population, you have to use this method
public synchronized void subtractFromPopulation(int subtractAmount) {
population -= subtractAmount;
}
// If you want to get to population, you have to use this method
public synchronized int getPopulation() {
return population;
}
}
I shared the codes I explained and more in GitHub. Additionally, certainly I suggest that you should practice with this structures. I coded these structures and these structures’ some test scenarios. You can check out to repository.
I used various resource for prepare this essay. I indicated in following. You can check out.
- Oracle Certified Professional Java SE 8 Programmer II— Jeanne Boyarsky and Scott Selikoff
- https://refactoring.guru/design-patterns/singleton
- https://www.tutorialspoint.com/design_pattern/singleton_pattern.htm