You don't need a singleton
In my previous article (from more than two years ago), I wrote a section with a similarly named “You don’t need a singleton”. The section was very brief. I tried to explain why, but the rule of thumb was right there in the title, You don’t need a singleton. This was intended to convey the message not that “maybe you should use singletons slightly less” but in fact, quite simply: don’t use singletons.
Singletons are global variables
While writing this article, I wanted to talk about how “global variables are bad”. But you probably already know that. I thought I could just skip the section on global variables and go to explaining why you should avoid singletons. Then I realized that would be a very short article. That’s when I realized something about my experiences with other programmers; they don’t equate singletons with global variables. I never hear about someone proudly checking their code in for review and proclaiming in the commit description “created a global variable to …”. Well, maybe they do, but it’s often associated with an apology in the comments. Conversely, finding a similar comment “created a singleton to …” It seems that the word “singleton” feels like design and “global variable” feels like technical debt.
Briefly: singletons are just as bad as global variables, and you should feel equally bad about using either.
“One object in the program”
Programmers think that if an object can only exist once in a program, then a singleton does no harm because making it not a singleton would make a runtime error of constructing a second instance.
I think this a poor excuse simply because your program may only need one instance today, but eventually that may not be true anymore. I have seen this kind of assumption made and then broken so many times that it seems shocking that an experienced programmer would be willing to make it again.
Here’s some examples, some less extreme than others:
- There can only be one database connection in the program, so it can be a singleton. Until you need two connections because you want to do an operation like “copy the data” to a new instance. Or the database becomes unmaintainably large and you need to move some tables to another.
- The program wants to make queries against the database from two threads.
- A program only has one logger because all the logs need to go to one place. Then your server gets busy, or some sessions are long-lived, and you need to have each connection do its own logging.
- A program has a configuration file, and the program “obviously” is only run with one configuration file specified. Except for the reusable component which needs its own unique configuration file.
Yes, I’ve experienced all of the above issues before in the real world.
Singletons document poorly
Programmers consider replacing a singleton with an object onerous because it has to be passed around everywhere, and stored in temporary objects, and so forth. Some say that it makes the code messy because you have so many functions that have a parameter
DatabaseConnection *db, or whatever.
Maybe it’s more typing, but it’s superb documentation and a dramatic benefit for code readability. If a function has such a parameter, then the reader of code knows that it uses that object. The immediate presence of such a variable indicates the side effects (reading or writing to a database), it even may indicate a performance property (for example, reading from a database is a lot slower than having data in memory).
Singletons make testing harder
Many times, a program will have a singleton object representing the application itself. This singleton tends to store the instance of the application’s main database connection and configuration. So, now you can’t run your tests in parallel.
By wrapping up all the state into one object, it is hard to know what needs to be initialized for your tests (see above: they document poorly) and you’ll have to just reset your singleton completely before each test. And know which singletons need to be reset can be quite difficult, unless you wrote all of the code yourself and remember every singleton you added.
In the real world, you might have a database server running somewhere with a copy of your production data, and this server might not be available to your development machines, nevertheless, you try to make a connection to it and it fails, so your tests don’t even run. Now you need a bunch of code in your production code that that only serves to escape from running setup code in a test.
Singletons make multithreading much harder
When you write a program, and you don’t spawn any threads, you don’t need to worry about concurrency, obviously. When you start a thread, you need to worry about concurrency when your two threads have some sort of shared state. If you use some high level threadsafe API does the message passing, then the problem goes away because it’s easy to reason about where locks might be necessary. As soon as your program has a singleton, this guarantee goes away. Any function can need some sort of locking because it’s impossible to know if a function accesses the singleton.
Consequently, you have to be certain that every single function a function calls doesn’t use that singleton. Or you might end up putting a mutex right in the singleton itself. Which might cause performance problems in what is probably code that is intended to perform well.
When you do need a singleton
Sometimes you do need a singleton. I assert that the only time a singleton is warranted is when a resource out of your control has global state, for example, signal handlers, windowing system (on Windows), the heap, and so forth. This really is just another way of saying you only need a singleton when there’s already a singleton that you can’t remove.
The fact that you need a singleton to manage other singletons suggests that singletons are “infections”, and it can easily have negative implications on your design.
I don’t think singletons are code smells (in that they are symptoms), I think they are often the root causes of performance, behavioral and maintainability problems.
So: don’t use a singleton, unless you really have to. If you really have to, you probably still don’t.