DEV Community

Heli Shah
Heli Shah

Posted on • Edited on

Understanding Singleton Design Pattern: A Simple Guide

What is Singleton Pattern?

Imagine you have a TV remote at home. You don't need 10 remotes for the same TV, right? One remote is enough for everyone in the family to use.

The Singleton pattern works the same way - it ensures that a class has only one instance and everyone uses that same instance.

Why Do We Need Singleton?

Think about these real-world examples:

  • Database connection - You don't want 100 connections to the same database
  • Printer - Multiple print jobs should go to the same printer
  • Settings/Configuration - Your app should have one set of settings, not multiple copies

Simple Example

Let's say we're building a Logger class to write logs to a file. We want only one logger in our entire application.

public class Logger {
    // Step 1: Create a private static instance
    private static Logger instance = null;

    // Step 2: Make constructor private (no one can create new instances)
    private Logger() {
        System.out.println("Logger created!");
    }

    // Step 3: Provide a public method to get the instance
    public static Logger getInstance() {
        if (instance == null) {
            instance = new Logger();
        }
        return instance;
    }

    // Your actual methods
    public void log(String message) {
        System.out.println("LOG: " + message);
    }
}
Enter fullscreen mode Exit fullscreen mode

How to Use It

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

        // Both are the same instance!
        System.out.println(logger1 == logger2); // prints: true

        logger1.log("Hello World!");
        logger2.log("This is the same logger!");
    }
}
Enter fullscreen mode Exit fullscreen mode

The Problem with Above Code

The above code has a problem - it's not thread-safe. If two threads call getInstance() at the same time, we might create two instances.

Better Solutions

1. Thread-Safe Version

public class ThreadSafeLogger {
    private static ThreadSafeLogger instance = null;

    private ThreadSafeLogger() {}

    // Add 'synchronized' keyword
    public static synchronized ThreadSafeLogger getInstance() {
        if (instance == null) {
            instance = new ThreadSafeLogger();
        }
        return instance;
    }
}
Enter fullscreen mode Exit fullscreen mode

2. Eager Initialization (Recommended for Beginners)

public class EagerLogger {
    // Create instance immediately when class loads
    private static final EagerLogger instance = new EagerLogger();

    private EagerLogger() {}

    public static EagerLogger getInstance() {
        return instance; // Just return the already created instance
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Enum Singleton (Best Approach)

public enum LoggerEnum {
    INSTANCE;

    public void log(String message) {
        System.out.println("LOG: " + message);
    }
}

// Usage
LoggerEnum.INSTANCE.log("Hello from enum singleton!");
Enter fullscreen mode Exit fullscreen mode

Advantages

Memory Efficient - Only one instance exists

Global Access - Can be accessed from anywhere

Lazy Loading - Created only when needed (in some implementations)

Disadvantages

Hard to Test - Difficult to mock in unit tests

Global State - Can make code harder to understand

Threading Issues - Need to handle multiple threads carefully

When to Use Singleton

Good for:

  • Database connections
  • File/Network managers
  • Logging systems
  • Configuration settings
  • Cache managers

Avoid for:

  • Regular business objects
  • When you need multiple instances later
  • When testing is important

Key Takeaways

  1. Singleton ensures only one instance of a class
  2. Make the constructor private
  3. Provide a public static method to get the instance
  4. For beginners, use Eager Initialization
  5. For advanced users, Enum Singleton is the best

Final Tip

Don't overuse Singleton! It's often called an "anti-pattern" because it can make your code harder to test and maintain. Use it only when you truly need exactly one instance of something.

Remember: One remote, one TV. One singleton, one purpose!


Happy coding! 🚀

Top comments (0)