My last "versus" post seemed to elicit a good number of interesting responses. Today, I'm musing about Singletons vs. Static Classes -- two constructs which appear to accomplish the same task. Or do they?
The singleton pattern, as applied to a class, is a design pattern that is used to restrict instantiation of that class to a single instance. A singleton is responsible for creating and maintaining its unique instance, and must define a public member that grants clients a global point of access to the unique instance.
A quick Google search will fetch you a number of canonical examples of singletons. Listing 1 more or less follows the implementation presented by the GoF, modified for C#.
In Listing 1, the Singleton.Globals class contains logic to ensure that one and only one instance of the class can be returned via the Instance property. By making the constructor protected, we insure that the class cannot be instantiated in any other way except by accessing the Instance property.
(Now I understand that the above example is not thread safe, blahblahblah. Deal with it. It isn't really important to the discussion that follows.)
Listing 2 verifies that multiple references to the Instance property refer to the same instance in memory.
I can certainly see where the Singleton pattern might be useful in modeling global resources like application logs, configuration settings, database connections, and so on. That being said, I have to admit that I haven’t made much use of the Singleton pattern in my projects. In the past, whenever I’ve thought “global object”, my next thought has pretty much always been “static class”, like in Listing 3.
Consuming the DoSomething method is pretty much the same either way, as shown in Listing 4.
I don’t know about you, but to me, there doesn’t appear to be a whole lot of practical difference here, at least on the surface. The other day I started thinking about this a little more. I wanted to see if I could determine, in situations where I’d want to provide global access to a bunch of functions, if there was any real advantage to using the Singleton pattern rather than a static class.
What if you wanted to derive a new class from either of these? Since static classes are implicitly sealed, you’d have to remove the static modifier from the Static.Globals class -- in which case you no longer have a static class, just a regular class with a bunch of static methods. And besides, isn’t inheriting from a class with essentially no instance behavior kind of… well, pointless?
On the other hand, it looks like there could be some value in subclassing Singleton.Globals. Listing 5 shows a naive first attempt.
Let’s think about this for a minute. The functionality of a singleton relies on the fact that the only the class can create an instance of itself. That means that the derived class will inherit the same functionality, and thus will only be able to create an instance of Globals. In other words, a reference to Globals2.Instance will always yield an object of type Globals. As written, there’s no way to create an instance that actually lets us call the DoMoreStuff method.
So, how would we even create an instance of Globals2, in order to leverage its additional functionality? We’d certainly have to give Globals2 its own private field to hold the instance, and then give it its own Instance property to access that field. This Instance property would have to hide the inherited behavior of Instance from Globals, as in Listing 6.
This works, but there’s hitch – it is now possible to create multiple separate instances of the Globals class – one from Globals.Instance, one from Globals2.Instance, and so on for each derived class. And therein lies the rub: if you can create multiple instances of a class, the class is no longer a singleton, right? It would seem that allowing inheritance on a singleton breaks the pattern definition of “one and only one instance.”
Upon further study, you’ll find another important limitation of this approach. As we’ve seen, it’s possible to derive classes from a base class that are intended to be singletons. However, the one thing you cannot do, under the inheritance scenario presented above, is to use the the base class to enforce the singleton behavior; that is, you can’t actually guarantee that any class derived from it will be a singleton. Consider Listing 7.
Listing 7 shows a Globals3 class that inherits from the singleton Globals, but is itself not a singleton -- there is no restriction here on how many Globals3 instances you can create using this construct. Therefore, with simple inheritance, it’s up to the developer to hand-implement the full singleton pattern in each derived class.
The verdict would appear to be that, while you can subclass a singleton, there are a number of pitfalls to using this approach.
As we’ve seen, with inheritance, you need to track instances, and provide a point of access to the instance, in each individual derived class. Next, I wanted to see if there was a way to improve on this.
In Listing 8, I’ve opted to use a GetInstance method rather than an Instance property, so I could add some factory logic. (Again, I realize you’d have to add locks and such to make this thread-safe.)
Here, in the Globals singleton base class, I’m using a generic Dictionary to track instances of any classes derived from it. The logic in the GetInstance method assures that no more than one instance of any particular subclass can be instantiated.
This certainly does make it easier to provide singleton behavior to derived classes, as the subclasses don’t have to implement their own singleton pattern or store their own instances. (Of course, it’s still possible to break the singleton pattern as before, but at least now you’d kinda have to go out of your way to do it.)
I guess I can see where this Singleton/Factory pattern could be handy for situations where you’d want to pull the derived type you want to instantiate out of a configuration file, for example. But I’m also seeing how much additional complexity is being introduced here. Listing 9 makes my YAGNI bell go off big time!
Hmmm… so much for that “simplest thing that could work” rule.
And once again I would point out that, because this allows you to create multiple instances of the Globals class (one for each derived type), we still seem to be stretching the concept of “singleton” well beyond its original meaning.
At first glance, it would appear that there are at least two distinct advantages of a singleton over a static class: 1) the ability to create (meaningful) derived classes, and 2) the ability to add factory-like behavior. But after a little further investigation, I’m still doubting the suitability of the Singleton pattern for most situations where access to a simple global object is required.
First, I still haven’t reconciled myself to the fact that, in order to extend the functionality of a singleton beyond that of a regular static class, you really need to allow more than one instance, which appears to violate the spirit of the whole singleton concept.
Second, I’m not comfortable that it doesn’t appear to be possible to guarantee that a class derived from a singleton will always be a singleton (maybe there’s a way to do that I just haven’t figured out yet?)
Third, singletons appear to me to add a layer of complexity that just isn’t required unless you’re attempting to pull off something really fancy.
Maybe someone will come along, blow me away with a cool implementation I haven’t considered, and make me a singleton fan-for-life. Until then, I’ll probably be sticking with those good ol’ fashioned static classes.