November 03, 2008

Singletons Deemed Dangerous

If you ask a software developer to name a few common design patterns, nine times out of ten, you'll hear about the venerable Singleton pattern. After all, it's one of the simplest design patterns, and one of the half-dozen or so creator patterns in the Gang of Four book. Sadly, this is an incredibly over-used pattern: to the point of qualifying as an anti-pattern.

I say anti-pattern because it is most commonly used as a way of creating an object-oriented global variable. This commonly occurs when the author of the code imagines that only one of a specific resource should ever be available within the system, and he wishes to make sure there is only one instance to match. Other times, the object is needed in several places which don't have a convenient way to pass an instance around, and a singleton is used to make sure the object is always available. In any case, the net result is the introduction of what is effectively a global variable into the code.

Once in place, a Singleton causes a number of insidious problems. The most difficult is that it is impossible to change how many objects of that class there are. It is frequently the case that it seems like there should only be one instance of an object at first, but that requirements change down the road, and multiple objects are needed later on. One example of this is a database connection. Since many different parts of a program may rely on a database, and most programs only address a single database, it is tempting to make the database connection pool a Singleton. Unfortunately, this doesn't allow for a situation where multiple databases must be addressed at once, or if some of the data moves behind a remote service call instead of the database.

A second major dilemma produced by Singletons is that it is impossible to substitute an alternate implementation. Either the behavior of the Singleton is changed for everyone, or not at all. This can lead to an explosion of complexity both within the Singleton (as it is adapted and configured to suit more and more separate and conflicting needs), and around it (as other classes need to provide more and more context). In ordinary circumstances, it would be easy enough to provide a group of subclasses with a shared interface to manage this complexity. Since the nature of Singletons makes this impossible, one is stuck with more complex means of altering the class's behavior.

A third problem is that Singletons make code which uses them very hard to unit test. Since every class which uses a Singleton fetches it at will from a global, static location, it is nearly impossible to test that class without also stubbing out everything which the Singleton interacts with (in addition to whatever stubs were necessary for the class itself). In effect, one must consider the functionality of the Singleton as being part of every class which uses it, since the two are so tightly Coupled as to be inseparable.

Finally, Singletons are not at all friendly to a programmer's Unit Economy. In order to successfully alter or maintain any class, one must bear in mind how it is used by every known class. By using layers and avoiding tight Coupling, one can make this a fairly manageable task. However, a Singleton destroys this effort completely. It is inherently available to any object of any layer, and it is inherently tightly coupled to any class which uses it. Both of these things mean that modifying a Singleton is a much larger undertaking than any other sort of class because of the potentially enormous mental context required to understand the ramifications of doing so.

So, what's the solution? There really are cases where everything in the system should refer to the same instance of an object, and there really are situations where an object is needed in a lot of places that aren't really connected to one another. Fair enough... I'll discuss one excellent alternative next time.

No comments:

Post a Comment