Singleton Design Pattern

Emre DALCI
4 min readNov 4, 2022

In this article, we will talk about what is Singleton Design Pattern and when we need it. Further, we will implement different approaches and learn design concerns with implementations.

What is Singleton Design Pattern?

Singleton Design Pattern makes sure that a class has only one instance and is globally accessible by all other classes. The instance of the singleton class is reused by multiple threads and this reduces memory usage.

When do we need Singleton Pattern?

We use logging, caching, and connections of a database in our projects constantly. For instance, we have a LoggerService class that we can use across our project to log. If every class creates a new LoggerService instance, it will cause an unnecessary load on the system that occupies more memory and can lead to an out-of-memory error. So, making singleton LoggerService and restricting other classes to instantiate LoggerService prevents performance issues.

Implementations

We will take a look at the LoggerService as an example to clarify different singleton pattern approaches.

— Eager Initialization

In eager initialization, in order to create a singleton instance, LoggerService should have a private constructor so that no instance of it can be created by other classes. It also should have a public static method so that it can be called to get its singleton instance.

The instance of singleton LoggerService is created at the time of class loading. This situation causes a drawback in that the instance is created even though the client application might not be using it.

— Lazy Initialization

Lazy initialization implementation is similar to eager initialization except that the instance of the class is created when we call the getInstance() method. Thus, if the client application does not use the LoggerService, it is not created.

This implementation can cause issues if multiple threads are inside the if condition at the same time. It destroys the singleton pattern and both threads get different instances of the singleton class.

— Thread Safe Singleton

With a thread-safe singleton, we can eliminate the drawback of lazy initialization.

We do not make the getInstance() method synchronized in this implementation. If we do, it reduces the performance because of the cost associated with the synchronized method. We need a synchronized block only for the first few threads that might create separate instances. To avoid this extra overhead every time, double checked locking principle is used. In this approach, the synchronized block is used inside the if condition with an additional check to ensure that only one instance of a singleton class is created.

Problem With Serialization and Deserialization

All of the above implementations work fine until you are not doing serialization and deserialization with a singleton class.

We achieve the singleton behavior by making the constructor private and making the constructor inaccessible for creating a new instance of the class. But if we make serialize the above singleton classes, when deserializing a class, new instances are created. It does not matter if the constructor is private or not. This violates singleton property.

The output for the above code is:

Two objects are not the same

In order to solve this problem, we have to implement the readResolve() method which is called when preparing the deserialized object before returning it to the caller.

When we run SerializeDemo class again, the output will be:

Two objects are the same

Problem With Reflection

Anyone can change the private constructor to the public constructor using reflection. And this also violates singleton property.

Output :

Two objects are not the same

In this way, the non-accessible private constructor becomes accessible and the whole idea of making the class singleton breaks.

But all of the above problems can be solved very easily by using the enum type to make singletons.

— Enum Singleton

Since enums are inherently serializable, we don’t need to implement them with a serializable interface. Also, Java ensures that any enum is instantiated only once in a Java program. So, the reflection is not a problem there. Thus, this method is recommended as the best method of making singletons in Java.

One thing to remember here is, when serializing an enum, field variables are not getting serialized. Refer to the Oracle docs for more details about enum serialization.

Pros and Cons of Singleton Design Pattern

Pros:

  • Singleton design pattern guarantees that only one instance of the class will be available throughout the application context. This ensures that you do not waste memory for a new object instance when you don’t need one.
  • Singleton design pattern might be handy when dealing with concurrent access to resources. Singletons can also provide thread safety.

Cons:

  • Singleton design pattern introduces a global state in the application. This makes unit testing difficult. The global state does not align well with unit testing because it increases coupling, and control over more than one unit is needed while writing the unit tests.
  • Due to the resource being locked in a parallel processing environment, multi-threading in some cases can not be used to its full potential with singleton design patterns.
  • Singleton design pattern solves less than it causes. This means this pattern has minimal practical use cases and, if used otherwise, can generate more problems. Some of these are also direct violations of other principles. (e.g., Single Responsibility: by virtue of the fact that they control their own creation and lifecycle.)

References

--

--