The Singleton Design Pattern ensures that only a single instance of a class can be created and provides a global point of access to that instance. This pattern is useful in situations where you want to restrict the instantiation of a class to a single object, such as when you want to ensure there is only one instance of a resource or when you need a centralized manager or configuration object.
To implement the Singleton Design Pattern in Java, you can follow these steps:
- Create a private constructor for the class to prevent other classes from directly instantiating it.
- Declare a private static variable of the class type to hold the single instance of the class.
- Provide a public static method that returns the single instance of the class. If the instance doesn't exist, create it; otherwise, return the existing instance.
// Singleton
class CarOrderManager {
private static CarOrderManager instance;
private CarOrderManager() {
// Private constructor to prevent direct instantiation
}
public static CarOrderManager getInstance() {
if (instance == null) {
synchronized (CarOrderManager.class) {
if (instance == null) {
instance = new CarOrderManager();
}
}
}
return instance;
}
public void placeOrder(String carModel, String customerName) {
System.out.println("Order placed: " + carModel + " for " + customerName);
// Additional order processing logic
}
}
public class Main {
public static void main(String[] args) {
CarOrderManager orderManager1 = CarOrderManager.getInstance();
CarOrderManager orderManager2 = CarOrderManager.getInstance();
orderManager1.placeOrder("SUV", "John Doe");
orderManager2.placeOrder("Sedan", "Jane Smith");
System.out.println(orderManager1); // CarOrderManager@355da254
System.out.println(orderManager2); // CarOrderManager@355da254
}
}
In this example, we have a CarOrderManager
class that represents a singleton for managing car orders. It has a private static variable instance to hold the single instance of the class. The constructor is made private to prevent direct instantiation.
The getInstance()
method is declared as public and static. It checks if the instance is null and creates a new CarOrderManager
object if it doesn't exist. Subsequent calls to getInstance()
will return the existing instance.
In the Main
class, we obtain two instances of the CarOrderManager
class using the getInstance()
method. Then, we use these instances to place orders by invoking the placeOrder()
method.
Since the CarOrderManager
class is a singleton, both instances refer to the same underlying object. Thus, when we place orders using either instance, the output will demonstrate that the same instance is being used.
Use the Singleton pattern when a class in your program should have just a single instance available to all clients; for example, a single database object shared by different parts of the program.
- The Singleton pattern disables all other means of creating objects of a class except for the special creation method. This method either creates a new object or returns an existing one if it has already been created. Use the Singleton pattern when you need stricter control over global variables.
- Unlike global variables, the Singleton pattern guarantees that there’s just one instance of a class. Nothing, except for the Singleton class itself, can replace the cached instance.
- Note that you can always adjust this limitation and allow creating any number of Singleton instances. The only piece of code that needs changing is the body of the
getInstance()
method.
Inside the getInstance()
method, we use double-checked locking to perform the instance creation in a synchronized block. The initial check outside the synchronized block helps to improve performance by avoiding unnecessary synchronization once the instance has been created. Then, within the synchronized block, we check again to ensure that only one thread creates the instance when it's still null. This modification ensures that the instantiation of the CarOrderManager
class is thread-safe. Multiple threads can call the getInstance()
method simultaneously, but only one thread will create the instance while others wait. By using synchronized double-checked locking, we achieve both lazy initialization and thread-safety for the singleton instance of CarOrderManager
.