Using the Singleton pattern (part 1)
published: Mon, 8-Aug-2005 | updated: Mon, 8-Aug-2005
Whenever a couple of developers have a chat about Design Patterns next to the water cooler, the conversation is almost guaranteed to turn to the Singleton pattern. In fact, when two Delphi developers discuss Singleton, it's like they are talking about two different languages since Delphi can't do the basic trick of making a Singleton work. But no matter which language the developers use, they'll always talk about the implementation of it. Never, it seems, do they take a step back and wonder if Singleton is a "good" pattern to use.
Why is Singleton so popular? My guess is that it's so simple to understand and in most languages it's easy to implement. The average developer can get a feeling of accomplishment very easily: "Hey, I implemented X as a Singleton!" Compared with Bridge, for example, where even Pattern experts can sometimes disagree about how to implement it in a particular case, Singleton just seems so well encapsulated and innocuous. Even so, in any one language, there are often several ways to implement Singleton
In the Win32 flavor of Delphi, implementing a Singleton in a traditional fashion is difficult because of two things: the constructor of a class cannot be made private, and how do you stop the developer destroying the instance. So Delphi programmers have devised various interesting and ingenious ways to get round the problem.
However, I would venture to say that it is all done in vain.
Let's approach the issue from first principles. An application is essentially a software model of some domain and within this domain you'll often find some entity that is singular or unique. There is only one of this type of entity in the domain. An example form writing Windows applications is that there is only one mouse cursor. So there should be only one instance of some Mouse class that represents the real mouse. If there were two instances which one should you use to change the shape of the cursor?
There are other examples that don't represent something physical or something you see. An example that Jim Cooper uses in his More Design Patterns article is a list of commands that the application can execute. The commands are bound to menu options and buttons and so on. There should be only one command list in an application and so Jim makes it a Singleton. Another example is a resource manager that encapsulates a string resource. Another, a universal logging object. And so on, so forth.
So, accepting that oftentimes we need to model a Singleton-like entity from our domain, let's turn our attention to testing. For the last few years, I've come to recognize the vital importance that proper automated unit tests bring to a project. For me, there are two main questions: how do we test the code that implements the entity, and how do we test the code that uses the entity? Despite what you may think, these are not trivial questions.
Suppose that you have implemented an entity as a Singleton in the following manner:
public class SingularThing { private static SingularThing instance; private int fooCallCount; private SingularThing() { } public static SingularThing GetInstance() { if (instance == null) instance = new SingularThing(); return instance; } public void Foo() { fooCallCount++; } public int FooCallCount { get { return fooCallCount; } } }
Further suppose that you are using NUnit as your testing tool. You start writing some tests in the usual manner.
[TestFixture] public class SingularThingTester { [Test] public void FooCallCountShouldBeOneAfterFirstCall() { SingularThing st = SingularThing.GetInstance(); st.Foo(); Assert.AreEqual(1, st.FooCallCount); } [Test] public void FooCallCountShouldBeZeroWhenCreated() { SingularThing st = SingularThing.GetInstance(); Assert.AreEqual(0, st.FooCallCount); } }
When you run the tests suddenly realize that something's really amiss. The tests pass when executed in one by one but they fail when executed in the declaration order. To be blunt, that is really, really bad. It shouldn't matter in what order unit tests are run.
The problem is that the changes you are making to the Singleton's internal values in one test are propagating to the next. The reason why is simple: there is only one Singleton in a program -- including the test program. Any changes made to the Singleton object persist from one test to another. The only test that ever gets a "fresh" instance is the first test to be run in a pass. (Note that the same problem occurs in NUnit and DUnit as well.)
It could be argued that the issue is a bug in NUnit. Perhaps NUnit should create a new AppDomain for every test. That way, the Singleton would be created afresh for every test. Unfortunately though, the problem with this solution is that creating an AppDomain is a fairly lengthy process and having to recreate one for every test means that all tests everywhere slow down just because we want to make sure that the rare case of testing a Singleton works as expected.
That is not acceptable in my view. If it takes half a second to create (and destroy) an AppDomain and run a test while it's alive, and you have, say, a thousand tests in your test suite, then you will wait over 8 minutes for them all to complete. Yuk. The first thing that happens is that the tests don't get run, bugs start appearing, the quality of the code goes down.
Better would be a special test attribute in NUnit just for this kind of test case. However for now let's just note this issue with Singletons implemented in the most obvious way.
Next let's look at the opposite problem: using the implementation of a Singleton in your code. Here it's just as wacky. For a start we have exactly the same problem: since there is only one instance of the Singleton-enabled class, one test may interfere with another.
But there's another issue now. If we write a simple class and its implementation uses a Singleton instance within it, we have to bring along the entire Singleton code into our test. The Singleton may be encapsulating some heavy-duty, slow-to-initialize, expensive-to-create object that drags in a whole bunch of other DLLs and assemblies. Because Singletons are, by their very nature, global objects, we can't mock or fake them to make our tests simple and fast to write and execute. For a TDD (test-driven development) environment, this is the worse possible situation.
So, as you can see, Singletons are nasty little beasts, even though they seem to have a nice face. Next time we'll talk about how to tame them.