The Singleton Design Pattern


The Singleton Design Pattern as we know it today was made popular by the book Design Patterns, written by the famous Gang of Four and published in 1994.

This pattern shows us how to create a Singleton class. A Singleton ensures there is exactly one object of that class. We cannot directly create new instances, and every time we retrieve the instance through its accessor, we get the same object. This allows sharing the state of that instance across our application.

For example, the code below shows a Singleton class implemented in Java. The constructor is made private, to prevent anyone from invoking it, except the class itself. And the getInstance method returns the only valid instance of this class.

public final class Counter {
    private static final Counter instance = new Counter();
    private int n;
    private Counter() {}
    public static Counter getInstance() {
        return instance;
    }
    public int next() {
        return ++n;
    }
}

To use this singleton, we just need to invoke the getInstance() method to retrieve the instance, and then we can call any methods on it as we’d usually do:

instance = Counter.getInstance()

// Get the next counter value
instance.next()

Singletons Are Spicy

Although very simple to implement, it is also one of the most controversial, with many people strongly objecting to it being used at all. The main objections are about the risk of exposing a global concern to every part of the code. It is generally accepted that global variables are a bad idea. A singleton can be close to that because it gives global access to shared state, and that often causes the same problems, mainly because they couple our code.

However, I believe that singletons are tools that can bring benefits if used at the right time and in the right way. Just like a knife can be dangerous if not handled with care, we can avoid the dangers of using singletons.

A common example of when to use a singleton is application configuration, to ensure that everything is read consistently and from one place, for instance. Creating a new instance every time we want to read settings is sometimes impractical or inconvenient. As in the app config example, in some cases, we really just need a single instance, and that coupling is expected.

Alternatives

Instead of writing singletons, we could create a single instance of our class and pass it as an argument into every class or function that needs it. I once worked in a team that was strict about not using singletons, and some parts of the code got really messy because we needed to inject this and other dependencies like config, logger, analytics, feature flag client, and others to basically every class in our codebase. It is possible, and we got used to it, but we were coupled to the interfaces anyway and it added a lot of verbosity to our code.

I can’t back this up, but in my personal experience it seems that people tend to be more comfortable with static classes than with singletons. I find this interesting, as static classes also add coupling to our code. I have worked with teams that have tens of static classes, but would despair at the idea of adding just one singleton. Instead, I believe that both static classes and singletons have a role to play in a healthy codebase, when used appropriately.

For example, static classes are great for pure helpers with no state. Singletons, on the other hand, are useful when we need one shared state, like an app configuration. Sometimes we can use either, but other times a singleton is the reasonable option, like when creating a feature flag client.

Another reason to prefer singletons over static classes is that it is a lot easier to test classes that use a singleton. In these cases, we can use our singleton by default, and define a double for our tests, so we can isolate the logic being tested.

Pain Points

I do find it challenging to test singleton classes though. Because constructors are private, we are restricted to always getting the same instance. Any side effects of using the singleton will be persisted while the application (or the test suite) runs, so tests won’t be independent unless we put extra effort into making them safe.

It’s also worth noting that singletons are not always thread safe by default, although we can make them thread safe with a few extra changes. The exact mechanism usually depends on each programming language, so I won’t be covering that in this article.

Takeaways

I believe that we need to be pragmatic rather than dogmatic. There are times in which the best solution to a problem is to add a singleton, and in those cases I am happy to add them to our codebase. I do, however, like when someone challenges this decision, as this gives us the opportunity to explore alternative solutions. After all, I also agree that we should not have more singletons than we really need.

Cheers!
José Miguel

Share if you find this content useful, and Follow me on LinkedIn to be notified of new articles.


Leave a Reply

Your email address will not be published. Required fields are marked *