Singletons vs. Static Classes
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#.
namespace Samples.Singleton
{
public class Globals
{
private static Globals instance = new Globals();
protected Globals()
{
}
public static Globals Instance
{
get { return instance; }
}
public void DoSomething()
{
// do something here
}
public void DoSomethingElse()
{
// do something else here
}
}
}
Listing 1
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.
namespace Samples
{
using System;
using Globals = Singleton.Globals;
public class Program
{
public static void Main(string[] args)
{
TestEquality();
}
private static void TestEquality()
{
Globals objA = Globals.Instance;
Globals objB = Globals.Instance;
if (ReferenceEquals(objA, objB))
{
Console.WriteLine("Instances are equal");
}
else
{
Console.WriteLine("Instances are not equal");
}
//// OUTPUT: Instances are equal
Console.ReadLine();
}
}
}
Listing 2
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.
namespace Samples.Static
{
public static class Globals
{
public static void DoSomething()
{
// do something here
}
public static void DoSomethingElse()
{
// do something else here
}
}
}
Listing 3
Consuming the DoSomething method is pretty much the same either way, as shown in Listing 4.
private static void CallMethods()
{
// call to static class method
Static.Globals.DoSomething();
// call to singleton class method
Singleton.Globals.Instance.DoSomething();
}
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.
Inheritance
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.
public class Globals2 : Globals
{
public void DoMoreStuff()
{
// do more stuff here
}
}
Listing 5
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.
public class Globals2 : Globals
{
private static readonly Globals2 instance = new Globals2();
public new static Globals2 Instance
{
get
{
return instance;
}
}
public void DoMoreStuff()
{
// do more stuff here
}
}
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.
public class Globals3 : Globals
{
// This is NOT a singleton
public new static Globals3 Instance
{
get
{
return new Globals3();
}
}
public void DoEvenMoreStuff()
{
// do even more stuff here
}
}
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.
Factory Implementation
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.)
namespace Samples.SingletonFactory
{
using System;
using System.Collections.Generic;
public class Globals
{
private static Dictionaryinstances = new Dictionary ();
protected Globals()
{
}
public static Globals GetInstance(Type type)
{
if (!instances.ContainsKey(type))
{
instances.Add(type, null);
}
if (instances[type] == null)
{
instances[type] = (Globals) Activator.CreateInstance(type, true);
}
return instances[type];
}
public void DoSomething()
{
// do something here
}
public void DoSomethingElse()
{
// do something else here
}
}
public class Globals2 : Globals
{
private Globals2()
{
}
public void DoMoreStuff()
{
// do more stuff here
}
}
public class Globals3 : Globals
{
private Globals3()
{
}
public void DoEvenMoreStuff()
{
// do even more stuff here
}
}
}
Listing 8
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!
private static void CallMethods2()
{
// call to static class method
Static.Globals.DoSomething();
// call to singleton factory method
SingletonFactory.Globals.GetInstance(typeof(SingletonFactory.Globals)).DoSomething();
}
Listing 9
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.
Conclusion
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.
Subscribe to this blog for more cool content like this!
You've been kicked (a good thing) - Trackback from DotNetKicks.com
Thank you for submitting this cool story - Trackback from DotNetShoutout
Over the weekend in between run throughs that I did prepping for a webcast, I did some catch-up blog
Pingback from Reflective Perspective - Chris Alcock » The Monring Brew #337
9efish.感谢你的文章 - Trackback from 9eFish
Pingback from Friday Links #49 | Blue Onion Software *
Pingback from Weekly Links #51 | GrantPalin.com
Pingback from Interesting reads | Coded Style
Pingback from Interesting reads | Coded Style
.NET Introduction to MapReduce for .NET Developers Working With the ANTS Profiler to Optimize SharePoint
If you are looking to follow this series, be sure to subscribe to my RSS feed at http://feeds.jasongaylord