Design patterns: Singleton pattern

D

There can be requirements when we need variables to maintain data for the system (like some global parameters) or need to call methods from the class without creating a new instance of the class every time.

One solution to these situations will be to use static fields and methods. But with static, you can not inherit the methods or variables from another class or interface. Because of this, you will always have to hardcode the class and can not work with interfaces. 

Let’s see this with an example. The requirement is to create a logger class LogOnCommonServer like-

class LogOnCommonServer {
    fun log(severity: Severity, message: String) {
        //implementation to log the message to common server
    }
}

Now, some customers don’t want the logs to go out of their network. So, you decide to create another class LogLocal-

class LogLocal {
    fun log(severity: Severity, message: String) {
        //implementation to log the message to local database
    }
}

Now, you have to replace every instance of LogOnCommonServer with LogLocal! We can avoid this by using an interface and using what we will see later as the Factory Pattern.

So, our final requirement is-
There should only be one instance of a class available at any time. If there is already an instance created, we should get the same instance every time.

Such classes are known as singletons.

Still not convinced?

Another advantage of using singleton is lazy initialization. The singletons are initialized only when they are needed for the first time. This helps in saving resources when some classes are not required in the code path.

prerequisites

If you don’t know what are design patterns, please go through the first post of the series.
Software design patterns

Okay. Fine. How to do it?

We should be able to create only one instance of the class. So, we need to stop the client from creating objects of this class. We can do so by making the constructor private.

class CommonLogger {
    private constructor() {
        //with private constructor, no one can create instance of the class
    }
}

But we need to create one instance of the class. With private constructor, we can create the instance only inside the class itself. Let’s make a static variable singletonInstance (for holding the instance) and a static method getInstance(to initiate and return the singletonInstance).

companion object {
    private var singletonInstance: CommonLogger? = null

    fun getInstance(): CommonLogger {
        if (singletonInstance == null) {
            singletonInstance = CommonLogger()
        }
        return singletonInstance!!
    }
}

This is simple. Now we can just call

CommonLogger.getInstance()

and it will return the same instance of the class every time initializing the class first time it is called.

Synchronization

As our static function is going to be accessed from the whole application, we need to look into the concurrency issues. Do you think something like this is possible if two threads are trying to call the function almost at the same time-

Thread 1Thread 2
CommonLogger.getInstance()
CommonLogger.getInstance()
if (singletonInstance == null)
if (singletonInstance == null)
singletonInstance = CommonLogger()
return singletonInstance!!
singletonInstance = CommonLogger()
return singletonInstance!!

So, we created two instances of a singleton 🙂

Whenever there is concurrency, we have synchronization issues and the solution is to use locks. So, the new code looks like this-

private var singletonInstance: CommonLogger? = null

fun getInstance(): CommonLogger {
    synchronized(CommonLogger::class.java) {
        if (singletonInstance == null) {
            singletonInstance = CommonLogger()
        }
    }
    return singletonInstance!!
}

synchronized block makes sure that only one thread can enter the block at a time. This code will have some performance penalty in the case when the singleton is already initialized. When multiple threads will try to access the method, it will slow down because the block is synchronized. To improve this, we will do a null check before the synchronized block as well. The final code looks like this-

fun getInstance(): CommonLogger {
        if (singletonInstance == null) {
            synchronized(CommonLogger::class.java) {
                if(singletonInstance == null) {
                    singletonInstance = CommonLogger()
                }
            }
        }
        return singletonInstance!!
    }
}

This will ensure that multiple threads are not waiting for the lock in case the instance is already initialized.

Boilerplate?

In general, almost all the newer languages support an easy way to create singletons without the developer writing all this boilerplate code. In Kotlin, the way is to use the object keyword instead of class.

//CommonLogger is a singleton
object CommonLogger {
    fun log(severity: Severity, message: String) {
        //implementation to log the message to common server
    }
}

The object keyword ensures that there is only one instance of the class ever created (lazy initialization) and you can directly access that instance using the class name.

CommonLogger.log()

When this is called for the first time. The object will be initialized and the function will be called on the initialized object.

All good. What’s bad?

Single responsibility principle

The singleton pattern (SRP) is primarily criticized because it doesn’t follow the single responsibility principle. It has two responsibilities-

  1. The main responsibility of the class
  2. Responsibility to create the new instance of the class

SRP is important because it ensure that there is only one reason to change this class which is the change in that one responsibility. In this case, it has two responsibilities but in my opinion it is very unlikely that the class will be changed because of the new instance responsibility.

Concurrency

Yes, we saw this earlier but this time the same concurrency issues apply to other class fields. If the singleton object is being updated from multiple places, there can be race conditions between the threads that can lead to unpredictable results. We need to take care that we appropriately synchronize all our fields and methods wherever there is a possibility of race conditions. In Kotlin, we can use thread-safe primitive types like AtomicInt, AtomicLong, etc. if appropriate.

Reflection

We prevented multiple instances of the class by making the constructor private, but that can be bypassed by using reflection operations in some languages. So, you can create multiple instances of a singleton class. This isn’t really a drawback, it is the way most languages are designed to be able to access the private members using reflection but not directly. In the end, we are doing it because it is our requirement to have a single instance only.

Conclusion

Many people oppose the single pattern completely and I believe it always depends on your requirements. If singleton pattern helps to solve your problems, feel free to use them in your projects.

Thank you for reading the article. Any feedbacks, comments, questions are welcome 🙂

About the author

Akshay Jain

I am passionate about programming and feel amazing when I see people using the software I have contributed in. I believe it is essential to write high quality code, which is easy to understand and test. I am still a work in progress and decided to document and share some of my learnings with everyone. Apart from that, I like to read books, and so you might find a lot of book reviews on my blog. I am working as a Software Engineer at Oracle since post-graduation from IISc Bangalore, and happily married :)

Add Comment

Akshay Jain

I am passionate about programming and feel amazing when I see people using the software I have contributed in. I believe it is essential to write high quality code, which is easy to understand and test. I am still a work in progress and decided to document and share some of my learnings with everyone. Apart from that, I like to read books, and so you might find a lot of book reviews on my blog. I am working as a Software Engineer at Oracle since post-graduation from IISc Bangalore, and happily married :)

Get in touch