Singleton Design Pattern: Ensuring a Single Instance of a Class

The Singleton Design Pattern is a creational design pattern that ensures a class has only one instance and provides a global point of access to that instance. This is useful when you need to control access to shared resources, such as a database connection, configuration manager, or logging service.

Why Use the Singleton Pattern?

  • Single Instance: Ensures only one instance of a class exists in the application.

  • Global Access: Provides a global access point to that instance.

  • Resource Management: Helps manage shared resources efficiently.

Example: Logger Class

Let’s say you’re building a logging system for your application. You want to ensure that only one instance of the Logger class exists, so all parts of the application log to the same file or console.

Step-by-Step Implementation

Step 1: Create the Logger Class

The Logger class will have a private constructor and a static method to get the single instance.

public class Logger {
    // Step 2: Create a static instance of the class
    private static Logger instance;

    // Step 3: Make the constructor private to prevent instantiation
    private Logger() {
        System.out.println("Logger instance created.");
    }

    // Step 4: Provide a global access point to the instance
    public static Logger getInstance() {
        if (instance == null) {
            instance = new Logger();
        }
        return instance;
    }

    // Step 5: Add methods to the class
    public void log(String message) {
        System.out.println("Log: " + message);
    }
}

Step 2: Use the Singleton Logger

Now, you can use the Logger class in your application. No matter how many times you call getInstance(), it will always return the same instance.

public class Main {
    public static void main(String[] args) {
        // Get the singleton instance of Logger
        Logger logger = Logger.getInstance();

        // Log some messages
        logger.log("Application started.");
        logger.log("User logged in.");

        // Check if it's the same instance
        Logger anotherLogger = Logger.getInstance();
        System.out.println("Are both instances the same? " + (logger == anotherLogger));
    }
}

Output

Logger instance created.
Log: Application started.
Log: User logged in.
Are both instances the same? true

Key Points About the Singleton Pattern

  1. Private Constructor: Prevents external instantiation of the class.

  2. Static Instance: A static variable holds the single instance of the class.

  3. Lazy Initialization: The instance is created only when needed (on the first call to getInstance()).

  4. Thread Safety: The above implementation is not thread-safe. For thread safety, you can use synchronized or other techniques.

Thread-Safe Singleton (Optional)

If your application is multi-threaded, you need to ensure that the Singleton instance is created in a thread-safe manner.

public static synchronized Logger getInstance() {
        if (instance == null) {
            instance = new Logger();
        }
        return instance;
    }

Real-Life Analogy

Think of the Singleton Pattern like a CEO of a company. There’s only one CEO, and everyone in the company interacts with the same person for major decisions. Similarly, the Singleton Pattern ensures that only one instance of a class exists, and all parts of the application interact with that instance.

When to Use the Singleton Pattern?

  • When you need to control access to shared resources (e.g., database connections, configuration files).

  • When you want to ensure only one instance of a class exists.

  • When you need a global point of access to an object.