The singleton is probably one of the most controversial design patterns, sparking perennial debate on forums and discussion boards, and prompting analysis and dissection in many articles and papers. It is a bastardized design pattern, almost always misused, that causes a number of problems without really providing any benefits. Singletons ruin code.
The singleton gained much of its popularity after the publication of Gamma et al’s now-classic work “Design Patterns: Elements of Reusable Object-Oriented Software.” Of all the design patterns presented in that book, the singleton is one that is most-often remembered by readers, most likely due to its simplicity. What many adopters of the singleton pattern fail to recognize is the distinction between what the singleton is and when the singleton should be used. “Design Patterns” aimed to simply present the definitions of commonly occurring solutions to problems in software development. It did not presume to definitively illustrate exactly when and where those solutions should be applied, although many readers mistook the books examples as guidelines for determining applicability. As ground-breaking as Gamma et al’s book was for the time, they — like all of us — are human, and make mistakes. Their chapter on the singleton is perhaps the worst, particularly because of its very poorly chosen examples.
The singleton pattern should be applied only when both of its aspects – singularity and global accessibility — must exist. In other words, there must be no other sane way to implement the object in question without both of those properties. This is a case that is actually quite rare. The rationales that are provided for making objects singletons are usually born not out of necessity, but out of laziness or a pathological design fault. Said reasoning usually ignores the problems and “design rot” that singletons bring to any code they inhabit.
“Everything needs to access my FoobazManager!”
Also presented as “but if FoobazManager is not a singleton I will have to pass it to every function,” this is one of the most common attempts at justifying singletons. It’s also the most endemic of serious flaws in your design.
There is nothing wrong with passing objects to functions, if those functions need those objects to perform their jobs. Similarly, there is nothing wrong with a class holding a reference to an external object, if that external object is required for the class to perform its duties.
The trick, of course, is to only do this as much as is actually necessary. If you really require access to your front-end renderer object from every function in the entire graphics system, the solution is not to make the front-end renderer a singleton (or a global); that would just be a band-aid over the real problem, which is that your graphics subsystem components are far too tightly coupled with each other. Instead, the solution is to refactor those components to remove unnecessary dependencies on other objects.
Well-designed systems isolate responsibility and try to avoid assumptions. This modularizes the system’s components, making them easier to test, maintain and extend. The result is more robust code. Singletons actively work against this separation by introducing implicit, hidden dependencies on external systems and allowing instant access from any location, even if that location is illogical. They allow you to avoid thinking about inter-system and inter-object interfaces, and when you abandon those issues, you’re abandoning clean programming principals are entering the world of messy, rotten spaghetti code.
“It doesn’t make sense to have more than one FoobazManager!”
This only holds true in limited contexts, not in general. That is to say, whether or not there needs to be a single instance of an object depends upon the context in which that object is being used, and it is that context that should provide the singularity guarantee by simply creating a single instance. The object itself should not be responsible for maintaining such a guarantee, as that couples the object very tightly to the context and consequently reduces modularity.
If you’re building an object model for a cell, for example, it makes sense for there to exist only a single Nucleus object within the context of the Cell object. If your game only supports input from a single source, your game knows to create only a single instance of the input dispatcher. The input dispatcher itself, if it is to be written well, should not know or care how many instances of itself are allowed.
You might argue that YAGNI stipulates that because you don’t actually need multiple instances, you should not cater to them. This argument is flawed because it presupposes that singularity is a default property of objects. It is not: remember we’re talking about object-oriented programming, here, which seeks to model problems with code objects that are analogies to real-world objects. There are precious little, if any, real-world objects that have only a single instance and it is thus rather foolish to incorporate such a property into any analogy we develop.
Furthermore, almost no language has native support for the singleton pattern, which means that building a singleton requires extra (though typically modest) effort, which actually goes against the YAGNI principal. YAGNI is about avoiding the creation of unnecessary functionality, not arbitrarily restricting functionality.
“I want to make sure I don’t accidentally create another FoobazManager!”
This is an especially flaccid justification. After all, singletons enforce the presence of a single instance globally, across the entire code base. That does not allow you to prevent accidental construction of multiple instances at the level of a specific context, which — as discussed above — is the proper place to make the determination about the number of instances that are appropriate.
Besides, not creating an instance takes a minimal amount of self-control and awareness — when was the last time you “accidentally” created two loop iteration variables when you only wanted one? If you have enough self-control not to intentionally dereference null pointers, you have enough self-control not to create random instances of FoobazManager all over the place.
This issue boils down to simply creating as many instances as you need — one, if that’s all it takes. If instance count restrictions must be applied in a more complex fashion, some sort of factory object should be employed to manage the construction of instances up to whatever the appropriate limit is.
“I don’t want to become obsessed with finding the perfect design and never write any code!”
Excellent! Too much focus on the “getting it right the first time” is a problem all programmers can suffer from. Even after fifteen years of writing code, I still occasionally catch myself spending far too much time thinking about the hypothetical problems with my current design rather than simply going ahead and building an implementation.
Stressing the avoidance of “singletonitis” in your designs does not mean you have to spend days or weeks doing up-front design of your system. But it does mean you need to think about the design. Singletons tend to allow you to avoid thinking altogether, which avoids a design altogether, which usually means you’re just coding blindly in the hope you eventually end up with something vaguely useful.
Your initial singleton-less designs might involve a lot of parameter-passing. So what? You can always refactor your code later, and in fact you probably will have to refactor your code a number of times before you ship. Change, and iteration, is part of software development.
When you encounter a scenario where you need to depend on another object or subsystem, stop and ask yourself why you need that dependency, and if there is a way you can refactor one or both classes so that the dependency is not required. If there is, perform the refactoring, and if not, leave the dependency. Either the dependency is something that is required, or you will have a realization later on about how you can remove it to streamline the system.
“You have to do what you have to do to ship the product!”
This is one of the weakest arguments in favor of singletons. The thrust of the argument is that, towards the end of the project when deadlines are looming and crunch time is ramping up, you just really need to finish the damn thing and you can’t be bothered “wasting time” on the “ideologically correct” solutions. You just have to hack it until it’s shippable and boot it out the door.
Whether or not we, as developers, should accept that as a reality of modern software development is a subject deserving of an entire separate article. However, the argument does not hold water as a defense of singletons.
Singletons tend to be used for “management” classes, objects that have some kind of supervisory role over large subsystems. As a result, they tend to come into play very early on –- in the design and initial development stages -– because it is highly unlikely that entirely new large subsystems will be added to the project once crunch time hits. During the project’s birth pangs, you should have ample time to consider appropriate designs and appropriate implementations. You have ample time to write robust and maintainable code – in fact, it should be a priority, because an hour spent writing it correctly early on could save you ten hours of debugging and hacking during the crunch.
Singletons are neither robust nor maintainable. If you let a singleton creep in, that singleton will propagate its design rot into the code for the lifetime of the project. Once that happens, the refactoring required to get rid of them can be daunting — in many cases so daunting in terms of man-hours that there is simply no budget for it. In this case, living with the singleton must be a fact of life, at least until the project ships and you have the time and resources for the appropriate major revisions. That does not mean it was okay to use a singleton in the first place, only that it is okay to accept the reality of the situation until such time as you have the appropriate resources to properly fix the problem.
So what does all this mean? I’ve never encountered a use for a singleton that turned out to be a good, justified application of the pattern, but I have reservations about making complete unilateral statements such as “they have no use in software development.” However, I have no problem asserting that they are overused as crutches and band-aids by many programmers, from the novice to the experienced. Think long and hard before employing the singleton pattern, because chances are that you do not really need it and electing to give in to its call will damn your code to depravity.