One Singleton to Rule Them All
I would like to preface this post by saying that I don’t like the Singleton design pattern at all. There are some who love it, others who view it as a sometimes-useful, often-abused tool in the toolbox, and then there are people like me who believe that it’s extremely rare to see a use of it that isn’t an abuse — so rare that I consider it better to avoid the pattern altogether. I won’t go into much more detail on my thoughts on the Singleton (stay tuned for that when I get to this pattern in my design patterns series), but here is some further reading that expounds a lot on reasons not to like this pattern:
- Scott Densmore: Why Singletons are Evil
- Misko Hevery: Singletons are Pathological Liars
- Alex Miller: Patterns I Hate
- Steve Yegge: Singleton Considered Stupid
With all of those arguments against Singleton in mind, and considering the damage that abuse (and I would argue use) of the pattern causes in code bases, I found myself thinking of code bases I’ve heard of or encountered where the code was littered with Singletons. In these code bases, refactoring becomes daunting for a variety of reasons: the hidden dependencies, the complex temporal dependencies, the sheer volume of references to the Singleton instances, the almost obligatory Law of Demeter violations, etc. Singletons cause/encourage a rich variety of problems in code bases. But I think that something could be done about two such problems: the redundant Singleton implementation logic and the Single Responsibility Principle (SRP) violation of which Singleton classes are prima facie guilty.
Take a look at how the Singleton is implemented (as recommended by Microsoft, here). The basic implementation initializes the static instance in the property accessor while variants use field initializing syntax and introduce thread safety.
The basic implementation is here:
public class Singleton
{
private static Singleton instance;
private Singleton() {}
public static Singleton Instance
{
get
{
if (instance == null)
instance = new Singleton();
return instance;
}
}
}
More specific even to C# is James Michael Hare’s implementation using the Lazy<T> class to eliminate the redundant if-instance-null-instantiate logic needed in each Singleton class. But that still leaves the redundant Instance property, the redundant _instance field, and the awkwardness (SRP violation) of an object managing its own cardinality. What if we got rid of both of those issues?
public static class Singleton where T : new()
{
private static readonly Lazy _instance = new Lazy();
public static T Instance { get { return _instance.Value; } }
}
public static class SingletonClient
{
public void Demonstrate()
{
Singleton.Instance.Write("Look!! Logger isn't a Singleton -- it's a real class!!");
}
}
Using generics, this implementation allows an instance of every Singleton to be expressed in the code of a single class. In this fashion, any type can be implemented as a Singleton without the type being coupled to an internal Singleton implementation. It also standardizes the Singleton implementation by making sure it exists only once in the code base. With this pattern, there is a similar feel to IoC containers — classes can be implemented without concern for who will instantiate them and how.
Here is my take on the disadvantages and potential disadvantages of this approach:
- You still have Singletons in your code base.
- This might actually encourage more Singleton usage (though fewer actual Singleton implementations) by removing responsibility for implementation.
- Since constructor of object in question must have public default constructor, removes the Singleton gimmick of making the constructor private and thus the safeguard against additional instances being created is also removed.
- A natural extension of the previous item is that it remains a matter of documentation or convention to use Singleton<T> and not instantiate rather than it being impossible to instantiate.
But the advantages that I see:
- Only one class implements cardinality management, meaning refactoring away from Singleton is nominally easier.
- No Singleton SRP violations
- Singleton implementation (initialization, lazy load, thread-safety) is standardized to prevent inconsistent approach.
- I actually consider preventing the private constructor gimmick to be a plus — particularly where potential refactoring may be needed (and it pretty much always is when Singletons exist).
- With this pattern in your code base developers are less likely to be come comfortable/familiar with implementing the Singleton pattern.
- No need to introduce interfaces for Singletons to inject instance into classes using dependency injection — just inject a non-Singleton managed instance.
- A code base with nothing but normal instances is very easy to reason about and test, so the closer you get to that, the better.
I would like to reiterate that I’m not suggesting that you run out and start doing this everywhere. It mitigates some of the natural problems with the Singleton design pattern but “mitigates some” is a long way from “fixes all”. But, if you have a code base littered with Singletons this could potentially be a nice intermediate refactoring step to at least standardize the singleton implementation and provide slightly more friendly seams to testing legacy code. Or if you’re faced with and outvoted by a group of other developers that love their global variables (er, excuse me, Singletons), this might be a decent compromise to limit the damage they cause to the code’s maintainability. On balance, I’d say that this is an interesting tool to add to your arsenal, but with the caveat that something is probably wrong if you find that you have a use for it.
I’d be curious to hear others’ takes on this as well. I just kind of dreamed this up off the top of my head without taking a lot of time to explore all potential ramifications of it use. If you’ve implemented something like this before or if you have opinions on the advantages/disadvantages that are different than mine or if you see ways it could be improved, I’d be interested to hear about it in the comments.
Jan
25
By Erik Dietrich
Comments: Here’s my Code and I’m Sorry
Category: Language Agnostic Tags: Comments, Metaphors for Software, Software Engineering | 6 Comments
Heavily Commented Code: The Awful Empathy
Imagine a feeling–that empathy you get when you’re watching someone fail in front of an audience. Perhaps its a comedian relying on awful puns and props or a person in a public speaking course that just freezes after forgetting some lines. Ugh. It’s so awkward that it hurts. It’s almost like you’re up there with them. You start mumbling to yourself, “please, just be funny,” or, “please, just remember your lines.”
Think of how you feel when someone gets up to talk at a meeting or give a presentation, and they start off with something like, “okay, this isn’t really finished, and Jones was supposed to help, but he got assigned to the Smith contract, and I did things in here that I’m not proud of, and…” With every clause that the speaker tacks onto the mounting lists of reasons that you’re going to hate it, you feel your discomfort mounting–so much so that you may even get preemptively angry or impatient because you know he’s going to bomb and you’re about to feel that tooth-grinding failure-empathy.
Okay. Now that the stage is set and we’re imagining the same feeling, know that this is how I feel when I open code file and see the green of comments (this is the color of comments in all my IDEs) prominently in a file. It’s as though the author of the code is on a stage in front of me, and he’s saying, “okay, so this is probably not very clear, and some of this is actually completely wrong, and changing this would be a nightmare, and you might want a beer or two, heh heh, but really, this will make more sense if you’re drunk, and, you know what, I’m sorry, really, really sorry because this is just, well, it’s just… it’s complete garbage. Sorry.”
That might seem a bit harsh, but think of what you’re looking at when you see code with a comment to actual code ratio approaching 1:1. You’re looking at a situation where someone needed to spend as much space and probably as much time trying to tell you what the code says as writing the code. Why would someone do that? Why would someone write a bunch of code and then write a bunch of English explaining to someone fluent in code what the code does? This is like me sending you an email in Spanish and putting the English equivalent after every sentence. I would do it if one of the two of us didn’t speak Spanish well or at all. And that’s how I feel when I see all those comments–either you don’t speak code very well or you think that I don’t speak code very well. The former occurs a lot with people who program haphazardly by coincidence. (“I better write this down in a comment because I had no idea that’s what an array was for. Who knew?”) The latter generates mind-numbing comments that rot. (“Declares an int called x and initializes it to 6.”) If you aren’t being forced to write comments by some kind of style policy and you’re not Zorro, you’re writing things in English because you’re not bothering to write and illuminate them in Code (I’m using the uppercase to distinguish simply writing some kind of compiling code from writing Code that communicates).
Self-Fulfilling Prophecy
There’s a substantial cross section of the developer world that thinks diligently commenting code is not only a best practice, but also table stakes for basic caring and being good at your craft. As I’ve previously explained, I used to be one of those people. I viewed writing comments in the same way that I view shutting drawers when I’m done using them or making my bed–just grunt work that’s unavoidable in life if you want to be a person that’s organized. Interestingly, I never really viewed them as particularly communicative, and, since adopting TDD and writing tiny methods, I viewed them largely as superfluous except for occasionally explaining some kind of political decision that transcended code (documenting APIs that you’ll release for public consumption are also an exception as this becomes part of your deliverable product). But I started to get increasingly disillusioned with the way comments would look in group code.
I would return to a method that I’d written six months earlier and for which I’d put a very clear doc comment, only to see something like this:
Holy crap! We're clearly playing Russian Roulette here. Did the requirements change and we're no longer endangering puppies? Is this code causing terrible things to happen? Who wrote that comment about 15? Hopefully not the same person that wrote the next line! And what should this code do--what the various comments say or what it actually does? Do the people responsible even work here anymore?
I'm pretty sure that anyone reading is chuckling and nodding sympathetically right now. You can only return to a method that you've written and see this sort of thing so many times before you come to a few conclusions:
Surely this isn't really news to anyone, and it's probably answered with admonishments and mental notes to be more diligent about updating comments that everyone knows won't actually happen. So why is it then considered good form and often mandated to put lies into source control for the later 'benefit' of others? Why do we do this, discounting the bed-making/diligence/good-citizen motivation?
To answer that question, think of the kind of code where you see comments and the kind of code where you don't. If you see a four-line functional method with a single nested loop and no local variables, do you generally see comments in there? Probably not. How about a fifty line method with so many nested control structures that you need some kind of productivity add-in to know if you're scoped inside that else you saw earlier or another if, or maybe a while loop? Bingo--that's where comments go to hang out. Giant methods. Classes with lots of responsibilities and confusing internal state. Cryptically-named local variables. These things are all like cool, dank yards after a storm, sprouting explanatory comments like so many mushrooms. They sit there in mute testimony to the mildew-ridden, fungus-friendly conditions around them.
To put it another way, comments become necessary because the author isn't speaking Code well and punts, using English instead of fixing the code to be clear and expressive. Thus the comments are compensation for a lack of clarity. But they're more than that. They're an implied apology for the code as well. They're an apology for writing code and not Code. They're an apology for the fact that writing code and not Code results in the project being a legacy project before it's even done being written. They're an implied apology for big, lumbering classes, winding methods, confusing state, and other obfuscations of intent. But most of all, they're the preemptive, awkward-empathy-inducing, "hang onto your hat because what I'm doing here is actually nuts" pre-apologies/excuses to anyone with the misfortune of reading the code.
So please, I beg you--next time you find yourself thinking, "dude, nobody's ever going figure this wackiness out unless I spend a few sentences explaining myself," don't bother with the explanation. Bother instead to correct the "nobody's ever going to figure this out" part. Good Code speaks for itself so that you can focus on more important things.