This article is lecture note from "Head First Design Patterns" by Elisabeth Freeman and Kathy Sierra and CS course from Kookmin by Inkyu Kim
The Singleton Pattern ensures a class has only one instance, and provides a global point of access to it.
Singleton is a creational design pattern that lets you ensure that a class has only one instance, while providing a globals access point to this instance. This pattern also preventing any other class from creating a new instance on its own.
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// other useful methods here
public String getDescription() {
return "I'm a classic Singleton!";
}
}
This class doesn't have a public constructor, so the only way to get its object is to call the getInstance method.
This method caches the first created object and returns it in all its subsequent calls.
getInstance method have static variable which is named "uniqueInstance", and it holds our one instance.
If uniqueInstance was not null, it was previously created.
But if it isn't exist (if uniqueInstance was null), it instantiate Singleton through its private constructor and assign it to uniqueInstance variable.
Signleton is used when we need only one instance.
It saves memory because object is not created at each request. Only single obect is reused again and again.
Plus, using Singleton pattern makes you get a strict control over global variables.
The Singleton can violate the Single Responsibility Principle. The Singleton not only responsible for managing its one instance, but it is also responsible for whatever its main role is in your application.
This pattern can be bad design considering that the components of the program know too much about each class.
One of the biggest problems is the pattern is so unsafe regarding the multithreaded environment.
public class Singleton {
private static Singleton uniqueInstance;
// other useful instance variables here
private Singleton() {}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// other useful methods here
public String getDescription() {
return "I'm a thread safe Singleton!";
}
}
By adding the synchronized keyword in the getInstance method, we force every thread to wait its turn before it can enter the method. That makes no thread can enter into the method with other thread at the same time.
Or there is another way below.
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return uniqueInstance;
}
// other useful methods here
public String getDescription() {
return "I'm a statically initialized Singleton!";
}
}
Using this approach, we rely on the JVM to create the unique instance of the Singleton when the class is loaded. The JVM guarantees that the instance will be created before any thread accesses the static uniqueInstance variable.