The Singleton Pattern is one of the most foundational concepts to grasp. It is classified as a creational design pattern and it is used to control the creation of objects, ensuring that only one instance of a particular class exists throughout the lifetime of an application.
In simpler terms, it ensures that a class is responsible for managing only one version of itself, and it gives global access to that single instance.
So when/why to use Singleton Pattern, this is the one of the most common question?
Before diving into the when/why using Singleton patterns, it’s important to understand the problem statements. Each design pattern is created in response to specific challenges faced by developers.
Problem Statement: Database Connection
We want to manage a single connection to a database throughout our application. Creating multiple connections can lead to conflicts and resource exhaustion.
// first instance
DataConnection dataConnection1 = new DataConnection();
//Second instance
DataConnection dataConnection2 = new DataConnection();
In this scenario, creating two instances of DataConnection
can cause resource issues. Each instance may perform operations independently, leading to conflicts and unpredictable behavior if they attempt to access the same database resource simultaneously.
To solve this problem, we need the Singleton pattern.
When Should Do We Use Singleton Pattern?
The Singleton Pattern is indeed a popular, especially in software design, as it's a powerful tool but can be easily misused. Let have understanding of when it’s appropriate to use the Singleton Pattern:
When to Use the Singleton Pattern
When Only One Instance of a Class is Needed Globally
If we need a class that should have only one instance across the entire application, the Singleton Pattern is a best choice 👍 . This ensures that all parts of the application are working with the same instance, maintaining consistency and preventing duplicate instances that might waste memory.When We Need Centralized Management of Shared Resources
Singletons are beneficial when there are shared resources, like configuration settings, caches, or logging tools, which are needed by multiple parts of the application. Centralized access and management simplify resource handling, especially for classes that need to be globally accessible and modifiable.When We Want to Control Access to a Resource
Singletons allow controlled access to a single resource by encapsulating it within a class. This makes them helpful when only one connection or resource should be used at a time, like managing a file writer to ensure synchronized access or a shared network connection.In Multithreaded or Parallel Applications
In applications with multiple threads, a Singleton can ensure that critical sections or shared resources aren’t accessed by more than one thread at a time. A properly implemented Singleton can provide thread-safe access, reducing the likelihood of race conditions and ensuring that only one instance of a shared resource is accessed concurrently.
Now, we understand about problem and when to use, let see how to address this problem.
UML Diagram of Singleton Pattern
The UML diagram typically includes the following components:
- Singleton Class: This is the class that restricts instantiation. It usually has a private static instance variable to hold the single instance.
- Constructor: The constructor is private to prevent direct instantiation from outside the class.
- Public Static Method: This method provides access to the instance. If the instance does not exist, it creates one; otherwise, it returns the existing instance.
Here’s a simple representation of the UML diagram for the Singleton pattern:
Note:
-
-
: Private access modifier (indicating that the instance variable and constructor are not accessible outside the class). -
+
: Public access modifier (indicating that the getConnection() method can be accessed publicly).
Java Implementation
Here’s an example of Singleton pattern implemented in Java:
public class DataConnection {
private static DataConnection instance = null;
/*
* This private keyword in constructor
* is to prevent direct instantiation from
* outside the class.
*/
private DataConnetion() { }
/*
* static method so that we can call
* this method outside the class without
* instantiation object
*/
public static DataConnection getConnection() {
if (instance == null) {
instance = new DataConnection();
}
return instance;
}
public void showMessage() {
System.out.println("Database is connected");
}
}
Now, if we want to get the instance of Database Connection, we call its static methods like this: DataConnection dataConnection = DataConnection.getConnection();
.
public class Main {
public static void main(String[] args) {
/*
* We can't directly initialize DataConnection
* due to its private constructor.
*
* DataConnection dataConnection = new DataConnection();
*
* This would cause an error.
*
*/
DataConnection dataConnection = DataConnection.getConnection();
System.out.println(dataConnection.hashCode()); // Example output: 112810359
DataConnection dataConnection1 = DataConnection.getConnection();
System.out.println(dataConnection1.hashCode()); // Example output: 112810359
// Both variables, dataConnection and dataConnection1, refer to the same instance in memory.
}
}
Conclusion
The Singleton Pattern is a foundational design pattern that ensures only one instance of a particular class exists across an application, By restricting the instantiation process and providing global access to a single instance, the Singleton pattern helps manage shared resources efficiently, such as database connections, configuration settings, and loggers. This pattern is especially useful in scenarios where consistency and controlled access to resources are crucial.
For more information and best practices on implementing the Singleton Pattern effectively, refer to this guide on DigitalOcean.
Top comments (2)
Very detailed! Nice 🙂
Very well explained!