Performant Singletons

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.

In this article I will be exploring some of the commonly presented justifications for the use of the singleton pattern and providing rebuttals to those points. I’m going to be focusing on the conceptual flaws of the pattern. I believe these design-level faults of the singleton are vastly more problematic than any of the implementation-level faults that may exist (although these should not be discounted, and I will briefly touch on them later).

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. As we will find, this is a case that is actually excruciatingly 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. Furthermore, said reasoning usually ignores the problems and “design rot” that singletons bring to any code they inhabit.

Hopefully, after reading this article, you will have a better appreciation of just how unnecessary and dangerous everybody’s favorite design pattern really is.

“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, you’re abandoning clean programming principles and 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 N