About a month ago, I made a post about what the singleton pattern costs you.
Although I stated my case in terms of trade-offs rather than prescriptive advice, I still didn’t paint the singleton pattern in a flattering light. Instead, I talked about the problems that singletons tend to create in codebases.
Whenever I’ve talked about the singleton pattern, anywhere I’m blogging, the general response follows a typical signature.
It attracts a relatively large amount of shares (usually indicating support) and traffic (often indicating support) while generating comments (these often contain objections).
Generally, the response follows this pattern from Stack Overflow. At the time of writing:
- 1,018 net upvotes for an answer explaining why they’re bad.
- 377 net upvotes for an answer explaining why they’re usually a mistake.
- 280 net upvotes for them getting a bad wrap only because people misuse them (with a disproportionate number of mitigating downvotes).
- 185 net upvotes for an answer explaining why they’re terrible.
It seems as though something like 80 percent of the active developer population has come around to thinking, “Yeah, let’s not do this anymore.”
But a vocal 20 percent minority resents that, thinking the rest are throwing the baby out with the bathwater.
Perusing some of the comments on the blog and discussion sites where it was shared, I found two themes of objection to the post.
- If singleton is so bad, why do DI frameworks provide singleton object instances?
- Your singleton examples are bad, ergo the problem is that you misuse singletons.
Today, I’d like to address those threads of criticism. But not ultimately in the way you might think.
If a Singleton is So Bad, Why Does My Framework Make One for Me?
This line of argument bemused me a bit.
I thought I went out of my way to clarify exactly what I meant and also to specify that I meant the singleton design pattern. But in retrospect, perhaps I should have called this out more explicitly.
Maybe I should have written this quick section in that post.
Since one commenter talked specifically about Java’s Spring Framework, let’s consider that. Here’s a Stack Overflow question with a lot of upvotes, suggesting that this issue confuses a fair number of people.
Simply put, my argument against implementations of the singleton pattern is not an argument against singleton beans or any other situations that call for only instantiating one copy of an object.
We’re not talking about the same things. I’ll take it one step further.
Objecting to the lowercase-s-singleton concept (only instantiating one object) would be absurd. If you write a class and only want to instantiate it once during your application’s lifetime, go nuts.
I don’t object to one, even if it is the loneliest number. I caution against the singleton design pattern because it forces its single instance via use of global state and handles both its own job and controlling its instance cardinality.
In other words, if you tell Spring you want a singleton logger and then you inject that logger in some class you’re using, that’s fine. Your logger just worries about logging, and the framework worries about how many there are.
No global state in your code, no problem.
It’s Not Singleton, It’s You, Erik
I’m going to use this second thread of objection to segue into my broader point in this post. The first point was easy enough to address, since the people raising it were talking about something different than what I was talking about.
This one is a bit more nuanced. It assumes that I either just haven’t learned the “proper” way to leverage the singleton pattern or else that I’m presumptuously impugning their code sight unseen.
My goal with this post is to make both assumptions irrelevant. But I’ll come back to that.
First, I’d like to present a thought exercise. I always hear from proponents of the singleton pattern that it’s really all about enforcing the singleton.
The global state thing is just an afterthought. If that’s the case, why doesn’t anyone ever do this?
1 2 3 4 5 6 7 8 9 10 |
public class Singleton { private static int _instanceCount = 0; public Singleton() { if (_instanceCount++ > 0) throw new InvalidOperationException("There can be only ONE!"); } } |
That achieves the precious instance cardinality enforcement but without exposing global state. As soon as someone tries to create a second new instance, an exception will greet them.
But I’ve never actually seen this or any variant in the wild. Have you?
I guess it’s just not quite the same without the globals.
It’s All Anecdotal
But I digress.
Returning to what I mentioned before, I’ll expand on one of the main problems in discussing this subject. If you like the singleton pattern, you might think that I don’t know how to use it.
Or you might think that I do know how to use it but haven’t figured out the magic for using it without the potential design issues it has left in its wake.
From my end, I’ve seen a lot of codebases. I earn a good chunk of my living assessing and remediating distressed codebases.
And it may be a (lack of) survivorship bias, but I have never once seen a singleton in the wild with significant functionality that didn’t introduce design headaches through its global state. I’ve seen plenty of abuse but never what I would consider conscientious use.
But all of that, on my part and on the part of those assuming things about me, is at best anecdotal. You and I can debate the merits of the singleton all day without making much progress since we’ll ultimately trade in opinions, past experience, and conjecture.
And that’s frustrating.
Let’s Run an Experiment
This issue isn’t unique to discussing the singleton. It applies to just about everything we as software developers do.
Should you unit test or not? Does TDD really work? Layered architecture or onion?
We have all of these sorts of debates ad nauseum, but at the end of the day, it’s hard to make them substantive. It becomes a bit like arguing about which musicians are “good” or whether an outfit “goes together” or clashes.
In our line of work, it’s hard because we work in codebases that are often proprietary. And even when they aren’t, it’s hard to isolate variables.
So let’s see if we can’t do a little better by running an experiment. We’ll have a hard time getting so far as “good” or “bad,” but we can at least hypothesize and confirm or refute.
I have a hypothesis about the singleton design pattern. I believe it has an effect on a number of other properties of codebases, based on my experience over the years. And I’m happy to put that to the test.
On the flip side, I hope those of you who think of it as a helpful tool in your toolbox would be happy to do the same. Regardless of who has the right hypothesis, wouldn’t it be for the best if we all found out together, in at least an approximation of the scientific method?
How This Works
Over the next month or so, I’m going to use NDepend to run analysis on a bunch of open source codebases.
I’ll do my best to find a decent sample size of ones that contain various numbers of singleton implementations (though I’ll necessarily be limited by what’s out there). And then I’ll record data to confirm or disprove these hypotheses.
For the purposes of the research, let’s consider what I’ll call “global stateful singleton lines of code” (GSSLOC). That will mean the number of lines of code in a codebase that are in singleton implementations containing global state.
Here are the hypotheses.
- GSSLOC varies inversely with the number of unit tests in a codebase.
- GSSLOC varies directly with average method cyclomatic complexity.
- GSSLOC varies inversely with assembly relational cohesion.
- GSSLOC varies directly with average afferent coupling per type.
- GSSLOC varies directly with average type lack of cohesion of methods (LCOM).
It’s worth noting that none of these things actually demonstrate a concept of “good” or “bad,” per se. But it would be interesting to correlate singleton usage with, say, average method complexity.
Where You Fit In
If you’re interested in participating, don’t think that you need to somehow hand over your codebase.
You can participate alongside the experiment I’m running. I encourage you to do so, especially if you have examples of “ideally used singleton” codebases that prove all of my doubts about the pattern wrong.
Let’s get those data points and see!
To participate, you’ll need a .NET codebase, and you’ll need to download NDepend and run it on your codebase to gather the metrics in question. Once you’ve gathered the data, you can post it here in the comments. (If that starts to get unruly, we’ll arrange another method for you to relay it).
Likewise, if you have questions about how to use NDepend to get that information, let me know in the comments. I can always update the post with a small addendum or subsequent post on how to do that.
I’ll still run the experiment whether or not anyone volunteers codebases or stats of their own. But the more the merrier.
And yes, I recognize that people could falsify the stats to prove a point. But if you’re doing that, it should probably tell you something about the validity of your assumptions.
So stay tuned, and let’s have some fun. I’ll follow up in the next month or so with results.