CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Jeremy D. Miller -- The Shade Tree Developer

Under the hood and working with .Net, TDD, Software Design, and Agile Stuff

Chill out on the Singleton Fetish

My little team is taking over development for one of our other products.  Today is the first day of the typical "I'm getting the app up on my box and the NAnt build doesn't work on my box" hell.  One of the things I'm seeing in trying to debug the broken unit tests is a bag of Singleton's that perform data access.  Just to make it more fun, one singleton uses another singleton to get its configuration.  My real problem is that the database connection string isn't correct yet, but the test failures exposed some larger structural issues to address later. 

Look at the code below for a second.  It's nothing special really, just a generic Singleton that serves as a gateway to some kind of resource, but this little bit of innocuous code can thoroughly hose the testability of any application.

 

	public class Singleton
	{
		private static Singleton _instance;
// Lazy initialization because that's more *efficient* public static Singleton GetInstance() { if (_instance == null) { lock (typeof(Singleton)) { if (_instance == null) { _instance = new Singleton(); } } } return _instance; } private object _something; private Singleton() { _something = getSomethingFromDatabase(); } private object getSomethingFromDatabase() { // go fetch some kind of configuration information from the database return null; } }

So what's so wrong with this?  Plenty.  The instance of Singleton touches the database in its constructor function.  Unit testing any class that depends on the Singleton class automatically includes some sort of live data access -- or does it?  One of the problems with singleton's in unit testing is the lack of control over when the singleton was created.  To the best of my knowledge, NUnit does not guarantee the order of unit test execution, so the singleton instance will be instantiated by whichever unit test happens to run first.  Why is this such a bad thing?  Because your tests aren't starting from a known state and therefore the results of the test are not reliable.

For another thing your unit test isn't a unit test, it's an integration test. Integration tests are cool too, but unit tests might be more important from the standpoint of creating working code.  One of the qualifications and benefits of a unit test is that it pinpoints the exact trouble spot in the code when it fails.  If a coarse-grained test fails, you've got to look in a lot more code to find the exact problem.  If there's a database involved, then you're troubleshooting search widens outside of your code.  It's possible to test against a live database if you can control the database state, but why do all that extra work if you don't need to?  Solve one problem at a time and data access is often a separate issue.  One obvious way to tell whether code is adequately unit tested at a fine-grained level is the amount of time spent with the debugger to fix problems.  If you're using the debugger a lot, you really need to reconsider your unit testing approach. 

Forget TDD for a second.  Using a stateful singleton opens yourself up to all kinds of threading issues.  Not many developers that write business software are going to be threading experts.  When you screw up threading safety you can create really wacky bugs that are devilishly hard to reproduce.  You do love bugs that can't be reproduced don't you?  The code I'm looking at today seems to handle the thread safety issues quite well, but all the "lock(this, that, or the other)" code blows up the lines of code count.  It adds complexity to the code and makes the code harder to understand and read.  Add in a bunch of tracing code and you end up with a whole lot of noise code surrounding a little piece of code that actually does something useful.  My biggest issue with the code is whether or not it was really necessary in the first place.  One of the truisms in software development you bump into occasionally is that "Premature Optimization is the root of all evil" in software development.  I'm guessing that the singletons were put in place due to performance concerns.  Do it a simple way first and forget caching out of the gate.  The performance bottlenecks are almost never where you think they'll be anyway.  By doing things the simple way upfront you can leave yourself with more time later when the real performance problems become evident.  Don't do anything that makes the code harder to understand unless you absolutely have to.

My best advice is to not use caching via static members until you absolutely have to for performance or resource limitation reasons.  If you do need singleton-like functionality, you might try some of the techniques from this article.  You can also get around the singleton issues by just being able to replace the single instance with a mock or stub from a static member.  It works, but it's not my preference.

 



Comments

Scott Allen said:

Actually - that kind of double check locking in GetInstance can have threading issues :) The first check of _instance == null is looking at a "work in progress" in shared memory without taking a lock. It might see _instance become non-null before all the ctor writes are finished. One recommended solution is to use the volatile keyword with the _instance field declaration ....


... but I agree, this doesn't sound like the best place for a singleton.
# August 4, 2005 5:11 PM

Jeremy D. Miller said:

Thanks Scott.

If I do a singleton these days I just go for the "private static instance = new Singleton();" approach and do it upfront.
# August 4, 2005 6:34 PM

Gary Williams said:

Actually, the singleton in question was used to wrap the static M/S SQL*Helper class. It was a very early part of the implementation on a team that was just beginning to learn TDD.

Was it a mistake? Possibly. The architecture was completely refactored part way through the project and the original rationale for the singleton was largely lost. It's an item that is a candidate for a refactor, certainly...but there are always lots of those.
# August 4, 2005 8:53 PM

Christopher Steen - Learning .NET said:

101 Samples for Visual Studio 2005 [Via: Paul Ballard@nospam.com ]
A DataSet is not a Database - Don't...
# August 4, 2005 10:58 PM

Christopher Steen said:

101 Samples for Visual Studio 2005
[Via: Paul
Ballard@nospam.com ]
A DataSet is not a Database...
# August 4, 2005 10:59 PM

Rob said:

Especially on classes that work in a service environment, locking is a necessary evil, but one that shouldn't be done carelessly. Locking isn't just a problem for singleton classes, as even instance classes can be accessed by multiple threads. Having said that, not locking in the *right* places is bad, and locking too much can be equally as bad. However, one problem that should be addressed here is try *not* to lock on types. That's generally a Bad Thing (tm), even though a lot of older MSDN samples showed it being done. It's best to use a specfic object instance as a lock, not a type.
# August 5, 2005 11:32 AM

Jeremy D. Miller said:

Thanks Rob, the original code did lock on an object instance in their singleton, I introduced the lock on the type in my sample. I get your point though.

# August 5, 2005 12:03 PM

Jeremy D. Miller -- The Shade Tree Developer said:

Inversion of Control (IoC) is an essential tool for any software designer’s design toolbox because it’s...
# September 20, 2005 3:52 PM

Jeremy D. Miller -- The Shade Tree Developer said:

Mock objects are like any other tool.  Used one way they’re a huge time saver.  In other cases...
# January 10, 2006 6:01 PM

Jeremy D. Miller -- The Shade Tree Developer said:

Inversion of Control (IoC) is an essential tool for any software designer’s design toolbox because it’s...
# January 18, 2006 4:12 PM

Jeremy D. Miller -- The Shade Tree Developer said:

One of my colleagues asked me to demonstrate and explain six patterns for folks with little or no exposure...
# April 11, 2006 4:11 PM

Jeremy D. Miller -- The Shade Tree Developer said:

This post isn't really a rant, more of a warning. I really, really think that testability should

# December 18, 2006 5:09 PM

Jeremy D. Miller -- The Shade Tree Developer said:

I will finish "Build your own CAB" at least before Acropolis hits and makes it all obsolete

# June 29, 2007 11:12 AM

Digital Blasphemy said:

The Monostate Pattern

# April 21, 2008 12:49 PM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add

About Jeremy D. Miller

Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy previously worked as a systems architect building mission critical supply chain software for a Fortune 100 company and learned agile development practices as a .Net consultant at ThoughtWorks, one of the pioneers of agile development. Jeremy is the author of the open source StructureMap (http://structuremap.sourceforge.net) tool for Dependency Injection with .Net and the forthcoming StoryTeller (http://storyteller.tigris.org) tool for supercharged FIT testing in .Net. Jeremy's thoughts on just about everything software related can be found on his weblog "The Shade Tree Developer" at http://codebetter.com/blogs/jeremy.miller, part of the popular CodeBetter site. Jeremy is a Microsoft MVP for C#. Check out Devlicio.us!

This Blog

Syndication

News

All opinions expressed here constitute my (Jeremy D. Miller's) personal opinion, and do not necessarily represent the opinion of any other organization or person, including (but not limited to) my fellow employees, my employer, its clients or their agents.

About Me

"Best Of" Compendium

StructureMap (Dependency Injection for .Net)

StoryTeller (Supercharged Fit)

Build your own Cab

TestDriven

MVP