Design Pattern : Singleton Live Project Usecase

Design Pattern : Singleton Live Project Usecase

Motivation

  • when I was working on a project, where we wanted our system to scale to 3 million users we were facing issues with the scalibility at that point of time I was learning how I can optimise the performance of my company's website , I can see some of the objects are being repeately created, so I was looking for a mechanism how I can create it once and use it everywhere since it really help me to save some time , This design pattern comes under Creation Design Pattern Category

Real life usecase

  1. Lets say in your application you are making a query to the database, and lets imagine you are making 50 queries in total and eveytime you wanted to make a query, you have to make a database connection and then execute the query, but there is some cost associated with this since database connections are costly.

Solution

  1. Create one class which will give me the object and create the connection for me so I do not have to take create the connection again and again.

Examples

public class DatabaseManager {

// Creating an object of the DatabaseManager 
    private static final DatabaseManager INSTANCE = new DatabaseManager();

  // constructor is private so that object can be created within the class only.
    private DatabaseManager() {
        // Complex code to connect and initialize the database.
    }

// public so that we can call this method from where we need the instance 
    public static DatabaseManager getInstance() {
        return INSTANCE;
    }

    public void queryDb() {

    }
}
  • How to use the above class
DatabaseManager.getInstance().queryDb();

Problem or Improvements for above class

  1. eager loaded Instance
  • here we can see even if we do not require the instance we are creating the instance of the but that should be created when getInstance is called right.

  • below mentioned example will use lazy loading for the instance + it shows how same object is retreived.

/* Online Java Compiler and Editor */
public class HelloWorld{

     public static void main(String []args){
        System.out.println("Hello, World!");

        DatabaseManager db1=DatabaseManager.getInstance();

        DatabaseManager db2 = DatabaseManager.getInstance();

        if(db1==db2) {
            System.out.println("same object");
        }


        Employee emp1=new Employee();
        Employee emp2=new Employee();

        if(emp1==emp2) {
            System.out.println("Employee object is same");
        }
        else {
            System.out.println("Different Object");
        }

     }
}

class DatabaseManager {
    private static  DatabaseManager INSTANCE = null;
    private DatabaseManager() {
        //instance the database manager and connect to the database
    }

    public static DatabaseManager getInstance() {
        if(INSTANCE==null) {
            INSTANCE= new DatabaseManager();
        }
        return INSTANCE;
    }
}

class Employee {

}
  1. Thread Safe Singleton Pattern
  • above mentioned singleton pattern is not thread safe two threads can come at the same time and execute the geInstance Method and thus breaking the singleton pattern so for that we have to make it thread safe.
        if (INSTANCE == null) {
            INSTANCE = new DatabaseManager();
        }
        return INSTANCE;
    }
  • Problem with synchronized block is that lets say thread 1 is executing all the others threads lets say thread2 and thread3 have to wait now a days we have multicore cpu so we can use multiple threads to speed up the process but here we are stuck with this solution.
  1. Thread Safe using Double check locking mechanism : with this use it in multithreaded environment
  • make the instance volatile
private volatile static DatabaseManager INSTANCE = null;
  • double check inside the getInstance method , so instead of using synchronised onthe method we are using it insde the getInstnace method
public static DatabaseManager getInstance() {
    if (INSTANCE == null) {
        synchronized (DatabaseManager.class) {
            if (INSTANCE == null) {
                INSTANCE = new DatabaseManager();
            }
        }
    }
    return INSTANCE;
}
  1. Protection against reflection
  • accessibility of the private constructor can be changed and can be used to protect for this
private DatabaseManager() {
    if (INSTANCE != null) {
        throw new RuntimeException("Use getInstance() to get the Instance.");
    }

    // Complex code to connect and initialize the database.
}

Complete Singleton Example with thread safety and Reflection safety

public class DatabaseManager {

    // Not loaded eagerly, but initialed on demand.
    private volatile static DatabaseManager INSTANCE = null;

    private DatabaseManager() {
        if (INSTANCE != null) {
            throw new RuntimeException("Use getInstance() to get the Instance.");
        }

        // Complex code to connect and initialize the database.
    }

    public static DatabaseManager getInstance() {
        if (INSTANCE == null) {
            synchronized (DatabaseManager.class) {
                    INSTANCE = new DatabaseManager();
            }
        }
        return INSTANCE;
    }

    public void queryDb() {

    }
}