Singleton is More Than Just Design Pattern

Singleton is one of the most recognizable terms in JVM world, but it’s also a common source of confusion and misunderstanding. How can this be possible, you ask? Well, the reason is that Singleton is an overloaded term which refers to multiple concepts. However, most developers either don’t know about different “singletons”, or see these concepts as equivalent. They are not.

In this article, I’ll go through the meanings of the term “singleton” and explain how they are different from one another. I’ll also describe the context where each “singleton” is used and discuss their respective benefits and drawbacks.

The Good Singleton

The first meaning of the term “singleton” is a specific class that will be instantiated only once during the lifetime of the application. Does it mean that every class which is instantiated just once becomes singleton then? Well, formally, yes. However, that’s not how we use this term in practice.

See, to become “true singleton”, that single instance of a class should be used by multiple clients inside the application. For example, the most popular use case for singletons is to hold application-wide state. Imagine something like SessionManager object which holds information about the current session. If you’ll use one instance of SessionManager across your entire app, it’ll become a singleton.

To differentiate this “singleton” from others which we’ll discuss below, I’ll refer to it as singleton (note the lower case) in the remaining of this article.

The Bad Singleton

The second “singleton” is one of the classical design patterns described in Gang of Four’s (GoF) book. It prescribes a specific way to ensure that only one instance of a class will ever be created. In other words, Singleton design pattern is a specific technique to create singletons.

That’s how it looks:

public class Singleton {
    
    private static Singleton singleton = null;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

The fact that Singleton design pattern turns a class into singleton makes it tricky to understand the difference between the two. Let’s put it this way: all classes which implement Singleton design pattern are singletons, but not all singletons necessarily implement Singleton design pattern. In other words, Singleton design pattern is not the only way to make a class singleton.

To make my life a bit easier, in the remaining of this post I’ll refer to Singleton design pattern as just Singleton (note the capitalization).

The difference between Singleton and singleton is very important. The reasons is that while your application might need some singletons, implementing them as Singletons can cause major issues down the road. Therefore, let’s discuss Singleton in more details.

The first benefit of Singleton is that it’s simple to implement. The second benefit is that it’s very easy to grab that single instance of a class from anywhere inside the application. Just write Singleton.getInstance() and you’re good to go. It works like magic. However, this last benefit is also the biggest Singleton‘s problem.

See, since you can retrieve Singleton from anywhere, it allows you to easily pierce through all architectural boundaries. Accessing database from UI layer? No problem! Setting domain state from infrastructure logic? Be my guest! Issuing network requests from everywhere? Hell yeah! You might think that you’re disciplined enough to withstand this temptation, but less experienced developers would do that and wouldn’t even understand the implications.

In addition, since Singleton.getInstance() is a static call, you can’t “see” this dependency through client’s public API. Therefore, once you start using Singletons, these “hidden” dependencies quickly turn your codebase into sneakily inter-coupled mess.

The last big problem with Singletons that I want to mention here is circular coupling. It’s trivially simple to have a flow which invokes Singleton2.getInstance() from Singleton1 class and Singleton1.getInstance() from Singleton2. That’s your circular coupling. In fact, it’s the simplest case. It’s much more problematic when a flow goes through one or more Singletons repeatedly after passing through several other objects. In some sense, this kind of circular coupling aggregates the involved objects into a single “distributed God Object” (like a chain tying multiple objects together).

If you aren’t convinced yet, then read about additional problems with Singletons in this article.

Now, many developers would argue that it doesn’t have to be like that and you can use Singletons without creating this mess. They’ll argue that’s just developers’ mistake, not design pattern’s limitation. This might be true, but all codebases that used Singletons which I saw had all of these problems. In fact, I would argue that if you use Singletons, then emergence of circular coupling is just a matter of time.

Therefore, I consider Singleton design pattern to be anti-pattern and recommend avoiding it as much as possible.

The Ugly Singleton

The last type of “singleton” that you can observe in the wild is @Singleton annotation. It’s part of JSR-330 standard (Dependency Injection for Java) and resides inside javax.inject package.

Javadoc for @Singleton says:

Identifies a type that the injector only instantiates once. Not inherited.

“Instantiates once” sounds very much like singleton, but what’s the story with “the injector”? To understand this nuance, you’ll need a bit of knowledge about Dependency Injection, so I’ll just assume that you know what I’m talking about.

If you use just one instance of a specific injector in your project (the correct term is “composition root” actually, but I’ll stick to javadoc’s language for now), then @Singleton annotation will indeed turn an object into singleton. However, nothing stops you from defining multiple injectors, in which case the role of @Singleton will change. In this case, @Singleton will only guarantee that one instance of an object will be reused by each specific injector. However, since different injectors will provide different instances, those objects won’t be singletons anymore.

In essence, @Singleton is just scoping annotation. It says that one instance of an object should be scoped to its injector (i.e. live for as long as the injector lives). The following code demonstrates the general idea behind scoping:

class Injector {

    private Singleton singleton = null;
    
    public Singleton getSingleton() {
        // this code is equivalent to @Singleton-scoped objects (thread-safety constructs omitted)
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
    
    public NonSingleton getNonSingleton() {
        // this code is equivalent to non-scoped objects
        return new NonSingleton();
    }
}

If one instance of the above Injector will be reused across the entire app, Singleton class will be singleton. Otherwise, it won’t.

I don’t like @Singleton scope. First, it doesn’t, on its own, guarantee that the annotated object will be singleton. For example, nothing stops you from using this annotation to represent Activity scoped objects if you use Dagger 2. However, my biggest problem with this annotation is the fact that the term “singleton” is overloaded. If this scope is used, then, when other developers say “this object is singleton”, a clarification is required: “do you mean @Singleton scoped, or you’ve got Singletons in your app?”. I’ve had such conversations, they are confusing and are complete waste of time.

Therefore, I prefer to define alternative annotation, like @AppScope, to replace @Singleton. It doesn’t address the first problem described above, but, at least, it removes the terminology confusion. In my opinion, hardcoding the word “singleton” into JSR-330 was a mistake that led to much unneeded confusion and misunderstandings.

Conclusion

As I said at the beginning, “singleton” is a widely known term, but it’s also very confusing. In this article I explained three different “singletons”, which are related, but still represent different concepts. Therefore, when you discuss “singletons” with your colleagues, it’s worth clarifying the terms to avoid confusion.

I, personally, use singletons (though I prefer to call them “global objects” to avoid confusion) and @Singleton annotation (though I prefer to replace it with e.g. @AppScope, if possible). However, I do my best to stay away of Singletons because I often see the consequences of using this pattern in codebases that I work with.

So, what’s your take on “singletons”?

Leave a Comment

Subscribe for new posts