Tuesday, July 25, 2006

The Singleton Smell

I'm a big fan of using Object Oriented design patterns, especially the classics popularized by the GoF. Design patterns are the OO analog of algorithms. Just like a good software engineer needs to be knowledgeable about data structures and algorithms, knowledge of OO patterns is a requirement in todays OO software world (disclaimer: the previous statement is only my opinion). And just like bubblesort can be misapplied to solve a problem, OO patterns can also be used inappropriately.

One of the most (if not the most) misused patterns is the Singleton. The singleton can be very useful when applied correctly, but it can also make for some poorly designed and almost unmaintainable software. The singleton is probably the easiest pattern to understand from the GoF's Design Patterns book (above), which may be why it's so often misused by engineers who are new to patterns. In a nutshell, the singleton pattern attempts to ensure that only one instance of a class is every created. Many callers may use the class, they just end up using the same instance.

Code that uses a lot of singletons gives off a distinct smell. A small that a software engineer should recognize. It's similar to, but more potent than, the smell given off by global variables, because a singleton is effectively a global variable. Singletons are generally global in scope, thus allowing any class, at any level, access to the singleton (read: global variable). This makes for classes that are tightly coupled.

Additionally, singletons let you design classes (or more (in)appropriately, "implementations") without having to think about the class's interface, or how they interact with other classes. The singleton doesn't need to be an argument to the class's constructor, or to methods, so it's often not considered when thinking through the object model for your code. This is similar to the way that C functions don't need to declare global variables in their parameter list, because again, they're globals, so the function's interface doesn't need to consider them. And I think we'd probably all agree that global variables are generally not the best idea.

Singletons are often misused in situations where you have multiple classes that all need to access the same instance of an object. In this case, the singleton is used as a convenience to let all the classes access this one central global variable. It would likely be a better idea to think about the class' interfaces, possibly add an extra parameter here and there, and avoid the singleton altogether. Now, not only do the classes communicate interface to interface, but you may also have a new reusable class (the one that used to be a singleton)!

It's generally possible (and usually a good idea) to replace singletons with non-singletons, but it often requires a little extra though. But this is a good thing and it's one of the best reasons to get rid of singletons. Once you think through your class's interfaces, you'll likely discover that you can loosen the coupling of your classes by pushing their interaction out to their interfaces rather than leaving it buried down in their implementations. This also allows you to add documentation about this interaction in the class's *interface* rather than code comments in its implementation.

Singletons can also make unit testing difficult. If class A uses the singleton class B in A's implementation, then unit testing A requires that B be all setup and able to run correctly. However, if A's constructor were changed to take a B as an argument, then A's unit test could simply create a mock B object, and just focus on the testing of A (which is what a unit test is supposed to do).

Now, singletons aren't always evil. They can be very useful in some situations. I won't go into those examples now, but I just want to be on record as having said that they are not always bad. The main point here is, do not jump to a singleton solution just for convenience. Singletons are convenient because they can be accessed from anywhere (think, global variables), but this should be avoided in favor thinking through class interactions and making your classes communicate via their interfaces. The singleton pattern should be used when you really need to ensure that only one instance of a class is ever created. And don't use a singleton without fully understanding why you really need one.

So, please take a big whiff of your code. Do you smell a lot of singletons? If so, consider refactoring it or make sure you understand why you actually need them. The extra thought you put into your class design now will pay back double when it comes time for maintenance.

Uh, sorry this was a little out of order and jumbled... I just wanted to jot down a few thoughts that were floating around my head on the way home tonight.

No comments: