Today we'll have a look at a well known pattern, the Singleton Pattern. Most people have already heard about this one.
The definition: "Ensure a class has only one instance and provide a global point of access to it."
Why would you want to only have one instance of a class?
Well, you could have one Configuration class for example, which reads some configuration settings at runtime, after which they remain available to your application. It makes sense to only have one instance of this class, since having multiple could cause you to suddenly end up with different configuration settings.
Now, there are different Singleton Pattern implementations out there, which makes it interesting to have a look at it anyway.
First of all, let's look at the basics of how a Singleton works. Since we're working with our game example, a good singleton would be a reference to our hardware, the GraphicsCard, we should have only one instance of it, who knows what would happen if we start mixing up various instances :)
To make sure there is only one instance of a certain class, we let that class itself manage it's instantiation, instead of creating it ourselves using the new() operator. Preventing others from calling new() on our class is done by making the constructor private. This way nobody can instantiate the class directly.
But how do we get an instance back then?
We provide a static method in the class itself, returning an instance of itself. By doing this, we can create one instance upon the first request and keep returning the same instance upon further requests.
This is also a form of lazy loading, since it only gets created when it is requested for the first time.
Where's the catch you might ask? Multi-threading! This simple implementation of a Singleton is not thread-safe! It is possible for multiple threads to both execute the null check together, both evaluate it to true, since there is no instance yet, and then both threads create an instance, ending up with multiple instances instead of one.
Luckily, there's a solution. I'll cut straight to the point and give the thread-safe, lazy loading version. There are other thread-safe implementations out there, which you can read more about at Implementing the Singleton Pattern in C#.
As you can see, the actual instance is held inside a private sealed nested class, which is inside the GraphicsCard class. When the Instance property is called for the first time, it will instantiate a GraphicsCard in a thread-safe way, thanks to the Nested class, and upon subsequent requests, the same instance will be returned.
It's elegant, and doesn't use any special language keywords like lock() in C# or synchronize in Java. I think it's quite easy to set up a Singleton like this, once you've done it once, you can do it everywhere.
With C# and generics however, we can be even more lazy, which is why I tried out throwing the Singleton Pattern into the following code.
Now the only thing we need to do when we want to create a singleton is create a class, inheriting from Singleton passing the class itself in as the generic T. The usage of both classes is identical afterwards, as you can see from this small snippet:
That's it, one of the easiest to understand patterns conceptually, but with some common pitfalls when implementing it.
There is one big issue bothering me with the generics approach however, hopefully someone can help me out on this, in an elegant way. As it is coded right now, you can do new GraphicsCardGeneric(), which is something I don't want, since you run the risk of ending up with multiple instances again. I can't add a private constructor however, since the generics break down then, requiring a public parameterless constructor to do new T() inside the Nested class.
If anyone has a nice solution to get around this, while keeping generics, please let me know. Personally I feel safer using the non-generics solution for now, since it locks down new() instantiation of the Singleton.
As usual, the project is available again to look at for further detail.
Some additional information on the Singleton pattern: