-
Notifications
You must be signed in to change notification settings - Fork 2
Singletons
The 2DOpenTKFramework utilizes the Singleton Design Pattern. Design patterns are, by principle, well-thought out solutions to common programming problems. Many programmers have encountered these problems before, and have used these "solutions" to remedy them. If you encounter these problems, why recreate a solution when you can use an already proven answer?
What problem are we trying to solve?
We have several managers that are central to the framework. These are: GraphicsManager, TextureManager, InputManager and SoundManager. Who should own these classes? The game window, the actual game class? When you think about it, each game object that wants to draw something will need access to the Graphics Manager.
Because so many classes will use these managers it makes sense to make them global. We will only ever need one instance of each of these managers, after all we only need one obect to manage sounds. Our problem is as follows, we have a single global instance of an object, who should own it, where should the object be created? This problem is solved by the Singleton Pattern.
Previously, in the WinformGames framework when we wanted to draw an object we passed the appropriate managers and contexts in trough arguments to the actual draw function? Why don't we do this again? Good question. Passing references to managers trough function arguments is also a pattern, it's the Dependency Injection Pattern. This would also be a valid solution, but you will find that singletons are much easyer to work with.
There are Many Design Patterns out there. We will cover them one by one as we learn more software engineering. In the 2DOpenTKFramework we will focus on singletons.
Not only is it important for you to understand singletons to use these managers, but you may also want to use them in your own cde where appropriate. For instance, you will only ever have one Game class, one UI Manager class. Pretty much any time you can call something a manager it is fit for the singleton pattern.
#Implementation
A singleton will have a private static variable that is a reference to the only instance of the class in existance.
The constructor of a singleton will be private. This assures us that no class outside the singleton can create a static instance.
Every singleton will have a static GetInstance that returns the private instance variable. This can be a method, or a getter. GetInstance will need to check if the private instance variable exists, if it doesn't it needs to be created.
Should you use a getter, or a function to get the singleton instance? Both approaches are valid, but 90% of the time you will see a getter be used to save keystrokes not having to type ( ) constantly.
#Example
Here is an example singleton class
class MySingleton {
private static MySingleton instance = null;
public static MySingleton Instance {
get {
if (instance == null) {
instance = new MySingleton();
}
return instance;
}
}
private MySingleton() {
}
public void MemberFunction() {
Console.WriteLine("Calling a non-static member function");
}
}
And this is how you might use it
public static void Main(string[] args) {
MySingleton a = MySingleton.Instance;
MySingleton b = MySingleton.Instance;
if (a == b) {
Console.WriteLine("A and B point to same instance of the singleton");
Console.WriteLine("Because only one instance exists");
}
else {
Console.WriteLine("Whoops, this is embarrasing. Something went wrong!");
}
// All three of the below do the same thing
a.MemberFunction();
b.MemberFunction();
MySingleton.Instnace.MemberFunction();
}
#Lazy Instantiation
The above code uses what is known as Lazy Instantiation. This means that no instance of the singleton exists until you call the Instance getter on the class. This is because the Instance getter is the only thing that ever calls new on the class to create an object instance.
Lazy Instantiation is not the only way to do things. We can also implement explicit instantiation (we will cover that next). Given the two, which method should you use with your singletons?
90% of the code you see will use Lazy Instantiation. As a matter of fact, when most people thing singleton they automatically think of Lazy Instantiation. This is due in large part to the fact that Lazy Instantiation is simple and easy to use, while Explicit Instantiation is a bit more involved.
#Explicit Instantiation
Explicit Instantiation possibly adds two things to lazy instantiation, a CreateInstance and a DestroyInstance function. Having a DestroyInstance technically breaks the singleton pattern, so most implementations only add the CreateInstance function, for the sake of compleateness we will demonstrate both
public class ExplicitSingleton {
public static ExplicitSingleton Instance { get; private set; }
public static CreateInstance() {
Instance = new ExplicitSingleton();
}
public static void DestroyInstance() {
Instance = null;
}
private ExplicitSingleton() {
}
public void MemberFunction() {
Console.WriteLine("Calling a non-static member function");
}
and this is how you can use the class:
public static void Main(string[] args) {
ExplicitSingleton.CreateInstance();
ExplicitSingleton a = ExplicitSingleton.Instance;
ExplicitSingleton b = ExplicitSingleton.Instance;
if (a == b) {
Console.WriteLine("A and B point to same instance of the singleton");
Console.WriteLine("Because only one instance exists");
}
else {
Console.WriteLine("Whoops, this is embarrasing. Something went wrong!");
}
// All three of the below do the same thing
a.MemberFunction();
b.MemberFunction();
MySingleton.Instnace.MemberFunction();
ExplicitSingleton.DestroyInstance();
}
Should you use Explicit Instantiation? NO. Learn Lazy Instantiation and use that.
