Design Pattern Singleton
Author : Hamza EL YAAQOUBI
Introduction
During this article, I will talk about a very important concept in OOP (Object-Oriented Programming). This concept is about design pattern and specifically the Singleton pattern.
So be ready to discover together all benefits of this pattern with some codes examples to explain every option.
Here we go :)
Definition
A Singleton is a class that can only have one instance in memory at any given time.
To create and guarantee the uniqueness of a Singleton, you must define on a basic way two things :
-
A private constructor to avoid any instantiation of the class
-
A static public method which return an instantiated object of the Singleton class
Also, a Singleton should be thread-safe.
Basic implementation of a Singleton
In this part, we will create a basic implementation of a Singleton.
Take a look at the following class :
public class Car {
private static Car instance = null;
private String name;
private Car() {
this.name = "DEFAULT_NAME";
}
public static Car getInstance() {
if (instance == null) {
instance = new Car();
}
return instance;
}
public String toString() {
return "name : " + this.name;
}
public void setName(String name) {
this.name = name;
}
}
The above class defines a good implementation of a Singleton. It can’t be instantiated using the Java key word new
to create many instances. It defines a private static attribute instance
which we will use it to store the only
instance of Car object that will be created by calling the static method getInstance
.
Once the attribute instance
is initialized the first time in Lazy
mode, it will be returned all the time for the next calls
to the method getInstance
.
Now, I will create a main class to test our new Singleton.
public class StartApp {
public static void main(String[] args) {
System.out.println("--- Initialization ---");
Car car1 = Car.getInstance();
Car car2 = Car.getInstance();
Car car3 = Car.getInstance();
System.out.println(car1.toString());
System.out.println(car2.toString());
System.out.println(car3.toString());
System.out.println("--- Update Name only one time ---");
car1.setName("BMW");
System.out.println(car1.toString());
System.out.println(car2.toString());
System.out.println(car3.toString());
System.out.println("--- Update Name two times ---");
car1.setName("MAZDA");
car2.setName("VOLVO");
System.out.println(car1.toString());
System.out.println(car2.toString());
System.out.println(car3.toString());
}
}
After executing this class main, here is the output :
--- Initialization ---
name : DEFAULT_NAME
name : DEFAULT_NAME
name : DEFAULT_NAME
--- Update Name only one time ---
name : BMW
name : BMW
name : BMW
--- Update Name two times ---
name : VOLVO
name : VOLVO
name : VOLVO
Finally, here is a simple test class to confirm all of that :
class CarTest {
@Test
void singleton_test() {
// Given
Car car1 = Car.getInstance();
Car car2 = Car.getInstance();
Car car3 = Car.getInstance();
// Then
Assertions.assertEquals("DEFAULT_NAME", car1.getName());
Assertions.assertEquals("DEFAULT_NAME", car2.getName());
Assertions.assertEquals("DEFAULT_NAME", car3.getName());
// When Update name for car1
car1.setName("BMW");
// Then
Assertions.assertEquals("BMW", car1.getName());
Assertions.assertEquals("BMW", car2.getName());
Assertions.assertEquals("BMW", car3.getName());
// When Update name for car1 and car2
car1.setName("MAZDA");
car2.setName("VOLVO");
// Then
Assertions.assertEquals("VOLVO", car1.getName());
Assertions.assertEquals("VOLVO", car2.getName());
Assertions.assertEquals("VOLVO", car3.getName());
}
}
By creating this Singleton, we offered a way to conserve the information that will be accessible by any code in the application.
Any modification of Singleton information must be done in thread-safe mode and therefore manage synchronization correctly via locks or synchronized. Consider using only thread-safe classes in this case for example Collections.synchronizedList() or Collections.synchronizedMap() and many others (Package java.util.concurrent).
In this first example, we used LAZY_LOADING mode, ie delaying the initialization of the instance until the getInstance
method called for the first time. In a concurrent environment (Multi-Threading) this technique is very dangerous and
can cause various side effects.
Synchronized Singleton
To correct the above problem, you will tell me that I can just make the getInstance
method synchronized,
suddenly we will solve the concurrent access problem but unfortunately it will be to the benefit of the performance
of the application.
In summary this solution works fine but not performance.
Here is the initial Car class modified to manage synchronization :
public class Car {
private static Car instance = null;
private String name;
private Car() {
this.name = "DEFAULT_NAME";
}
public static synchronized Car getInstance() {
if (instance == null) {
instance = new Car();
}
return instance;
}
public String toString() {
return "name : " + this.name;
}
public void setName(String name) {
this.name = name;
}
}
Holder Singleton
I don’t know if you have already worked with that solution but let me say that it’s an efficient solution which permit us to create and expose a Singleton class without any problem as I know (works on a multi-threading environment).
So, as you imagine it, we talk about Holder
technique. This technique consists in creating a private internal class which
will assume the uniqueness of the Singleton.
When the class Car is loaded by the JVM, the class goes through initialization.
Since the class doesn’t have any static variables to initialize, the initialization completes trivially.
The static class definition Holder within it is not initialized until the JVM determines that Holder must be executed.
The static class Holder is only executed when the static method getInstance
is invoked on the class Car,
and the first time this happens the JVM will load and initialize the Holder class.
public class Car {
private String name;
private CarHolder() {
this.name = "DEFAULT_NAME";
}
private static class Holder {
private final static Car car = new Car();
}
public static CarHolder getInstance() {
return Holder.car;
}
public String toString() {
return "name : " + this.name;
}
public void setName(String name) {
this.name = name;
}
}
Enum Singleton
Since Java 5, you can create a thread-safe Singleton by using Enumeration.
Here is an example :
public enum Car {
INSTANCE;
public static Car getInstance() {
return INSTANCE;
}
}
Deserialization of a Singleton
I finish this article by talking about deserialization of a Singleton. We have to prevent the creation of new instances which supposed to be a Singleton.
To do this, we will use the ReadValue
method which will make it possible to secure deserialization
(Official doc from Oracle).
Here is the code that illustrates the use :
public class CarReadResolve implements Serializable {
private final static CarReadResolve CAR_READ_RESOLVE = new CarReadResolve();
private String name;
private CarReadResolve() {
this.name = "DEFAULT_NAME";
}
public static CarReadResolve getInstance() {
return CAR_READ_RESOLVE;
}
public String toString() {
return "name : " + this.name;
}
public void setName(String name) {
this.name = name;
}
private Object readResolve() {
return CAR_READ_RESOLVE;
}
}
Conclusion
In this article, we saw how to create and manage Singletons through several solutions.
If you have any comments or questions don’t hesitate !
Good luck and stay tuned !