Encapsulated Lazy Singleton Pattern – [C#]

posted in: Uncategorized | 1

Background

Singletons are a frequent topic of discussion among hobbyist game developers. The design pattern exists to solve the problem of limiting the creation of an object to only one instance while exposing a global handle to its reference for easy access. A quick web search yields a variety of ways to design a Singleton, but I though I’d introduce my favorite means of implementing the design pattern. Singletons are heavily used by game programmers due to the nature of many ‘global systems’ that all need to interact with one another and be easily accessible from other objects. This is especially true in rushed or small-scope projects when rapid development is prioritized over scalable architecture. I believe this pattern, which I’ve dubbed the ‘Encapsulated Lazy Singleton’, tackles the Singleton problem in an elegant way, offering encapsulation around the Singleton’d class instance and lazy instantiation – you won’t lose it (memory) if you don’t use it.

I will be demonstrating this example using a slightly contrived camera system built in Unity, but the pattern is completely re-usable outside of the game engine.


Case Example

Let’s say I am writing a camera system for the player and I realize that the player’s camera would be a good case for a singleton. There will probably only ever be one instance of the player camera and it sure would be nice to be able to access it whenever I wanted to. It seems like a good fit!

I’ve already written the logic for a general purpose camera that can be positioned in world space and continually updates its position each frame. I’ve named the class, Dynamic Camera.

using UnityEngine;

class DynamicCamera : MonoBehaviour
{
    private void Start()
    {
        // Set up camera here...
    }

    private void Update()
    {
        // Continually update camera...
    }

    public void FadeIn(float duration)
    {
        // Do fade in...
    }

    public void LookAt(Transform transform)
    {
        // Look at the transform...
    }

    // ...
}


Refactor Attempt #1

The class is a Monobehaviour so it lives on an object and is initialized on the Game Object’s start method. Your intuition might be to just throw a static instance reference to itself in its initialization like so.

class DynamicCamera : MonoBehaviour
{
    public static DynamicCamera Instance;

    public void Start()
    {
        Instance = this; // Singleton!
        // Set up camera here...
    }
// ...

Now I could easily access this particular instance of the DynamicCamera like DynamicCamera.Instance.LookAt(). We’ve achieved the Singleton pattern in a sufficiently simple but fragile way. Unfortunately, we have a few problems. It doesn’t ensure you’ll always maintain the same reference to the camera. Some other game object could easily come along and create another object with a DynamicCamera behavior and the Instance reference would change. It also has no safe measure to ensure the Instance is even initialized in the first place. You’ll end up with a null reference trying to access its methods before it’s initialized. Plus, the ‘.Instance’ notation is just plain ugly to look at and doesn’t clearly communicate to the user what is going on.

Let’s take a stab at refactoring the camera another way.


Refactor Attempt #2

I’ve created a new class called MainCamera. It is static and holds a private reference to the DynamicCamera class we want to turn into a singleton.

using UnityEngine;

public static class MainCamera
{
    private static readonly DynamicCamera instance = null;

    static MainCamera()
    {
        instance = new GameObject("Main Camera").AddComponent();
        instance.Initialize();
    }

    public static void Initialize(Transform playerTransform)
    {
        instance.transform.parent = playerTransform;
    }

    public static void FadeInCamera(float duration)
    {
        // Fade out the camera here...
        instance.FadeIn(duration);
    }
}

The MainCamera class includes static methods for operating on its singleton reference. Also notice that it uses a static constructor. We can take advantage of this because it guarantees initialization the first time the class is accessed.

I’ve also re-written the initialization step of the DynamicCamera class as follows.

using UnityEngine;

class DynamicCamera : MonoBehaviour
{
    // Note the lack of 'Start()' method
    public void Initialize()
    {
        // Set up camera here...
    }
// ...

To interact with the player camera, our code looks like MainCamera.LookAt(). We’ve removed the unnecessary .Instance characteristic of the earlier implementation and hidden it within the static class. This refactor also offers complete encapsulation over the DynamicCamera object, guarantees initialization of the singleton instance through the static constructor, and denies any accidental user modification to the instance reference because we’ve made it readonly.

An additional perk is that if our code never accesses the MainCamera class methods, the player camera will never be constructed. Nice and lazy.

I’ve put together a simple example of scene initialization using this pattern. We have a SceneManager node sitting in the scene that loads in everything else.

using UnityEngine;

public class SceneManager : MonoBehaviour
{

	// Initializes the entire scene
	void Start ()
    {
        // Create the player and the boss.
        GameObject myPlayer = new GameObject("The Player");
        GameObject bossMonster = new GameObject("The Boss");

        // Set up player cam
        MainCamera.Initialize(myPlayer.transform);
        MainCamera.FadeInCamera(5.0f);

        // Set up boss camera
        BossCamera.Initialize(bossMonster.transform);
        BossCamera.LookAtPlayer(myPlayer.transform);

        // Keep loading things...
	}

}

You’ll notice I also created another static class called BossCamera. It is another singleton that shares much of the implementation of the MainCamera class but also has some specific features like the ability to LookAtPlayer. Using this pattern, you have the ability to create multiple global instances that share the same underlying implementation! Both my MainCamera and BossCamera can be interfaced with identically and preserve all the characteristics of a Singleton we’re looking for. And if the scene doesn’t have a boss in it, we won’t need to allocate any resources for the BossCamera reference!


Outcome

There you have it. The Encapsulated Lazy Singleton offers everything you’d expect out of your singletons and with additional security. One thing that can not be overlooked is how easy it is to take a completed class and write the static class to hold the private singleton implementation. The internal implementation of the singleton’d class is unchanged and can still be used anywhere else a singleton isn’t necessary!

One Response

  1. I’m really impressed with your writing skills and also with the layout on your blog. Is this a paid theme or did you modify it yourself? Anyway keep up the excellent quality writing, it’s rare to see a nice blog like this one nowadays.|

Leave a Reply