Singleton Design Pattern

Singleton Design Pattern is a creational design pattern. The singleton design pattern ensures that the class has only one instance and available globally means global point of access to it.

How to Implement it ?

  • Private constructors to prevent instantiation with the new operator

  • A public static method preferably with the name getInstance() to return a single instance of the class

  • A private static variable to store the only instance of the class

Example in Java

package DesignPatterns.Creational.Singleton;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DatabaseConnection {
    private static DatabaseConnection instance;
    private Connection connection;
    private DatabaseConnection() {
        try {
            String url = "jdbc:mysql://localhost:3306/mydb";
            String user = "root";
            String password = "";
            connection = DriverManager.getConnection(url, user,password);
            System.out.println("Database Connection Established!");
        } catch (SQLException e) {
            throw new RuntimeException("Error connecting to the database", e);
        }
    }

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

    }
    public Connection getConnection() {
        return connection;
    }
}
package DesignPatterns.Creational.Singleton;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class Main {
    public static void main(String[] args) {
        DatabaseConnection dbInstance = DatabaseConnection.getInstance();
        Connection connection = dbInstance.getConnection();

        try {
            String sql = "SELECT * FROM employees"; // Replace with your table name
            PreparedStatement statement = connection.prepareStatement(sql);
            ResultSet resultSet = statement.executeQuery();

            while (resultSet.next()) {
                System.out.println("Employee Name: " + resultSet.getString("name"));
            }

            // Close resources
            resultSet.close();
            statement.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

How Singleton Works in My Code

DatabaseConnection class, I have implemented the Singleton pattern to ensure only one database connection instance is created and shared across the application.

The constructor is private, meaning it cannot be called from outside the class.

This prevents direct instantiation of the class, ensuring only one instance exists.

A static variable instance is used to store the single object of DatabaseConnection.

Challenge 1: Multi-Threading Issues

In a multi-threaded environment, multiple threads might try to create an instance at the same time. This can lead to multiple instances being created, breaking the Singleton principle.

  • Basic Singleton (if (instance == null)) → Not thread-safe. Multiple threads may create separate instances.

  • Synchronized Method → Fixes thread safety but reduces performance due to locking every time getInstance() is called.

  • Double-Checked Locking → Fixes thread safety and improves performance by locking only when instance is null. But it's complex and requires volatile to work correctly.

How Bill Pugh Singleton Solves It?

1. Uses an inner static helper class, which is loaded only when needed.
2.Java’s ClassLoader mechanism ensures thread safety without explicit synchronization.
3. Eliminates performance overhead since no locking is needed.


Challenge 2: Lazy Initialization

Creating a Singleton instance before it's needed can be inefficient, especially if the instance is heavy (e.g., database connection, logging system, etc.).

  • Eager Initialization (static instance = new Singleton()) → Ensures only one instance but wastes memory if not used.

  • Synchronized and Double-Checked Locking → Provides lazy initialization, but adds complexity.

How Bill Pugh Singleton Solves It?

1.The inner static class is not loaded until getInstance() is called, ensuring lazy initialization.2. No unnecessary memory usage if the instance is never needed.

package DesignPatterns.Creational.BillPughSingleton;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

class DatabaseConnection {
    private Connection connection;

    // Private constructor to prevent instantiation
    private DatabaseConnection() {
        try {
            String url = "jdbc:mysql://localhost:3306/mydb"; 
            String user = "root";
            String password = "password";

            // Load MySQL JDBC Driver
            Class.forName("com.mysql.cj.jdbc.Driver");

            // Establish the connection
            connection = DriverManager.getConnection(url, user, password);
            System.out.println("Database Connection Established!");
        } catch (ClassNotFoundException | SQLException e) {
            throw new RuntimeException("Error connecting to the database", e);
        }
    }

    // **Bill Pugh Singleton: Inner Static Helper Class**
    private static class SingletonHelper {
        private static final DatabaseConnection INSTANCE = new DatabaseConnection();
    }

    public static DatabaseConnection getInstance() {
        return SingletonHelper.INSTANCE;
    }

    public Connection getConnection() {
        return connection;
    }

    // Method to close the connection
    public void closeConnection() {
        try {
            if (connection != null && !connection.isClosed()) {
                connection.close();
                System.out.println("Database Connection Closed!");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Pros (Advantages) of Singleton Pattern

BenefitWhy?
Ensures a single instancePrevents duplicate objects and saves memory.
Global access pointCan be accessed from anywhere in the application.
Thread safety (with Bill Pugh Singleton)Avoids race conditions in multi-threaded environments.
Lazy initializationReduces memory usage by creating an instance only when needed.
Efficient resource managementEnsures shared resources (e.g., database connection) are managed properly.

Cons (Disadvantages) of Singleton Pattern

DrawbackWhy?
Breaks SOLID principlesViolates Single Responsibility Principle (SRP) because the class handles both logic and instance control.
Difficult to unit testHard to mock in unit tests because of the global state.
Hidden dependenciesMakes debugging and refactoring harder since many classes depend on the same instance.
Concurrency issues (if not implemented properly)Can cause race conditions if not thread-safe.
Increases couplingComponents depend on the Singleton, making the system less flexible and harder to modify.