Day 3: Singleton Pattern

Sourabh Kumar
4 min readJul 19, 2024

--

Welcome to the third day of my 30-Day Design Pattern Challenge! Today, we’re going to explore the Singleton Pattern, a creational design pattern that ensures a class has only one instance and provides a global point of access to it.

What is it?

The Singleton Pattern is a design pattern that restricts a class from instantiating multiple objects. It creates a single instance of a class and provides a global point of access to it.

Class Diagram

Problem

The Singleton pattern solves two problems at the same time, violating the Single Responsibility Principle:

  • Ensure a single instance: Ensure that a class has just a single instance. This is useful when controlling access to shared resources, such as databases or files.
Clients may not even realize that they’re working with the same object all the time.
  • Global access: Provide a global access point to that instance, while protecting it from being overwritten by other code.

Solution

All implementations of the Singleton have these two steps in common:

  • Private constructor: Make the default constructor private, to prevent other objects from using the new operator with the Singleton class.
  • Static creation method: Create a static creation method that acts as a constructor. Under the hood, this method calls the private constructor to create an object and saves it in a static field. All following calls to this method return the cached object.

How it works

Imagine that you created an object, but after a while decided to create a new one. Instead of receiving a fresh object, you’ll get the one you already created. Clients may not even realize that they’re working with the same object all the time.

Example

Let’s consider an example of a Logger class that logs messages to a file. We want to ensure that only one instance of the Logger class exists, and we want to provide a global point of access to it.

public class Logger {
private static Logger instance;
private static Logger getInstance() {
if (instance == null) {
instance = new Logger();
}
return instance;
}
private Logger() {}
public void log(String message) {
// log the message to a file
}
}

Singleton Pattern in Multithreading Environment

The above code will work fine as long as the application is single threaded but in a multithreaded environment, the Singleton Pattern can be challenging. Multiple threads may try to access the single instance simultaneously, which can lead to issues.

There are two ways to fix this race condition.

  • One is to add synchronized to the getInstance() method.
synchronized public static Logger getInstance()
  • The other is to undertake static initialization of the instance, which is guaranteed to be thread-safe.
// The sole instance of the class
private static Logger onlyInstance = new Logger();

The problem with the above approaches is that synchronization is expensive and static initialization creates the object even if it’s not used in a particular run of the application. If the object creation is expensive then static initialization can cost us performance.

Double-Checked Locking

It is a technique that reduces the overhead of synchronization. It checks the lock twice, once before synchronizing and once after synchronizing.

public class Logger {
private static volatile Logger instance;
private static Logger getInstance() {
if (instance == null) {
synchronized (Logger.class) {
if (instance == null) {
instance = new Logger();
}
}
}
return instance;
}
private Logger() {}
public void log(String message) {
// log the message to a file
}
}

Other Examples

Other examples of Singleton Pattern include:

  • Configuration Manager
  • Cache Manager
  • Thread Pool Manager
  • Database Instance Manager
  • Some Java APIs (java.lang.Runtime, java.awt.Desktop)

Benefits

  • Ensures a class has only one instance
  • Provides a global point of access to the single instance
  • Improves performance by reducing the overhead of creating multiple instances

Disadvantages

  • Can lead to issues in multithreaded environments
  • Can make code harder to test
  • Can lead to tight coupling

When to Use

  • When a class has a significant overhead in terms of resources
  • When a class has a complex initialization process
  • When a class needs to be accessed globally

When Not to Use

  • When a class has a simple initialization process
  • When a class doesn’t require global access
  • When a class can have multiple instances

Conclusion

That’s it for today’s post on the Singleton Pattern! I hope you enjoyed learning about this creational design pattern. Stay tuned for the next post, where we’ll dive into the Prototype Pattern!

Join me on this 30-day journey, and let’s explore the world of design patterns together! If you have any questions or feedback, please leave a comment below. I’d love to hear from you!

If you liked this blog, please clap👏!

--

--

Sourabh Kumar
Sourabh Kumar

Written by Sourabh Kumar

Software Developer. Tech Enthusiast. Innovative Solver.

No responses yet