I was recently asked in an interview, "What do you like about the Singleton pattern?"
My initial response was, with a smile, "Seriously? Removing it from codebases."
It elicited a small chuckle, but also a rephrasing. "Yes, but, when would you use one?" my interviewer replied.
The singleton pattern, on the surface, is a simple interface for creating and accessing a single instance of an object. It can seem like a good idea when that object is expensive to create and initialize on-demand. And the interface doesn't add much complexity. I just get my instance - easy, right? That's the promise of the singleton.
"Now, can I tell you what I dislike about the Singleton pattern?"
First off let me say, that interface is a fraud; it's lying to you! If only it were that easy! I'll talk more about this in a minute. But second, rarely does it even make sense to control only a single instance of something globally.
- It violates the Single Responsibility Principle: it's initializing a global value, and then providing access to it.
- It feigns the appearance of thread safety and concurrency when it is often the biggest offender in both of these categories. You still have to make sure that the bloody thing you are Singleton-ing (apologies for verbing a noun) is itself thread safe in order to use it concurrently. Are the users of your singleton just reading values or are they making updates to it as well? Are their changes going to collide with each other?
- It perpetuates the use of statics and globals further. Introducing one tends to cause a ripple effect that constrains others to utilizing a static or global instance to use your static or global instance. For that reason, in general is a design smell in production code.
- They make things hard to test.
And that leads me to what I hate about the singleton pattern: no one seems to know how to implement it correctly. There are multiple ways to screw up double-check locking, and the whole approach itself is fundamentally flawed. See also the Initialization-on-demand holder idiom, derived from Effective Java. In a clustered environment, the abstraction beings to leak: depending on what it is you're controlling access to, there could be many sources of truth, one on each node. Have you ever seen a Singleton implementation that replicates its state across a cluster? Me either. But sometimes, you need to do this, or at least make sure updates are synchronized.
So I'd avoid Singletons if possible. This is one time I'm definitely throwing the baby out with the bathwater. It doesn't deliver on its promises, and you're better off thinking of the world without this pattern. Singletons are essentially premature optimization: they gloss over important questions of resource utilization when you're developing and not testing anything under load or concurrently. Instead, prefer immutability and statelessness, and get rid of those globals! Create your heavyweight objects every time they're needed first, and then when performance problems become obvious (and they will) investigate other options. There are two things that you can typically replace most singletons with, which guide you toward the right questions to ask. Both are around lifecycle of the instances in question:
- Binding instances to a predefined container's context using whatever mechanism they expose.
- Pooling your instances close to where they are being used.
A capable Inversion of Control container can provide a sane place to manage instances for me if my use case fits in nicely with a prescribed scope/context that it's defined. For example, a user instance bound to Session scope. Producer methods in CDI are a good replacement if this is a fit, in combination with binding the instance to an appropriate context; same goes for prototype scope (and other scopes) in Spring (again, if you've figured out Thread safety). Or, the @Singleton scope and @Lock modes within EJB3.1. You could also investigate creating your own context.
If an instance is a real bear to create and you need it to perform better, and it doesn't fit nicely with a known context, you might want to investigate the nuts and bolts of a capable instance pooling mechanism, and figure out what the usage pattern for your instances should be. Where are they being accessed from, who borrows and returns instances, and do we need to tune the pooling behavior in production? Sometimes it may just mean it's time to go back to the drawing board.
Now at some level, if you have a completely thread safe object that you're controlling access to, and you're sure you understand how to use the Init-on-demand holder idiom above, and it makes sense to initialize exactly once per VM, and you don't have a container you can use, and pooling is not necessary, use the Singleton. And definitely leave a comment explaining why you did.