The singleton follow-up – a deeper look into singletons in unity3d

mt_doom

This post it’s a bit different than usual. This is not just a tutorial but rather “the talk”… or a grownup discussion depending on your degree of advancement. And yeah, for some of you it’s going to be like “Look kiddo, I’ve been doing this shit long before you even saw your first keyboard”, for those of you, I’m sorry, I hope this won’t be extra cringy and just jump to the “What’s the solution?” paragraph.

We’re now going to discuss a little bit of architecture, or as some of you were thinking in last post “why singletons can break everything for everyone”.

sex-talk-kids-know-more

The reasons behind

Up until now maybe you thought that the problem is doing stuff with code, hitting up on the keyboard until the pixels behave properly, but hacking a system until it does what you want with the least possible effort is the aim of all of us, including those of us that are seen using some more exoteric stuff like zenject.

The thing is: not everyone deals with the same time horizon. If you are just doing a short game jam, a single megalithic script with all your code in it could even be a good idea (i.e.: if your problem is writing fast enough and alt-tabbing it’s too slow for you).
But let’s say you are going to develop a project that’s going to stay 10 years, better believe that spending one month just planning ahead may not be enough. And if you are using an engine, with a 10-yr horizon you could bet that maybe not even the engine is going to stay all that time. The engine developer may shut down and you may have to change it 6 years from now for all you know.

So in some cases you just need to plan for maintenance a lot and do stuff that it’s not that immediate because of issues that would come up only a few years into development.

afterburner-inspection-897513_1280

How in hell are singleton involved?

The thing is, when I think singleton in unity, I think static class with an inspector. This is since the other ways to use singletons have quite serious issues that a long term project cannot ignore. And by the way, even that won’t be good in many cases.

The most important issue here is maintenance. Maintenance cost is sometimes so high that it’s just cheaper to throw everything away and build it from scratch again. And for maintenance there’s an important concept that every dev should tatoo on his right arm memento-style: dependency management.

When class A uses class B, A depends on B meaning that if you make changes to B you are going to have to change A too.

This doesn’t render justice to the issue. Let’s put it another way:

In a project where every class depends on every other, just change one function and you’ll have a fun time rewriting almost all of it.

And what do singletons have to do with this? As I said in last post, they are basically a global variable on steroids, so they put hidden dependencies in your code. This means:

In a project where every class hiddenly depends on every other, just change one function and you’ll discover by surprise that you now must have a fun time rewriting almost all of it.

So when you plan your architecture you better mind it and ask yourself “how is this class going to affect the others when I change it?”

that's quite unstable
that’s quite unstable

Instantiation issues

One of the big problems with singletons is their instantiation (or rather, the dependencies it entails). Who instantiates a singleton and when? If you do it like I’ve shown in my last tutorial, you’ll leave that to unity by just putting a GameObject with a component in your scene and keep it there forever. But the singleton pattern allows for a host of other solutions.

Another solution is called Lazy loading, which means that you load the singleton when is first used, something like this [DANGEROUSLY WRONG EXAMPLE]:

public class LazySingleton {
    static LazySingleton instance;
    public static LazySingleton Instance 
    {
       get 
       {
          if (instance  == null) 
          {
              instance = new LazySingleton();
          } 
          return instance;
       }
    }
}

Now, with current Unity implementation and without ever thinking of multi-threading that may not sound too much of a problem. But let’s say that 4 years from now they go on hard on multi-threading (or that you just don’t want to waste those many extra CPUs), then you won’t know if two classes that try and access the instance get the same one or not.

Why you don’t know? because they might both do the if (instance==null) at the same time and therefore get two instantiations done. You therefore have to lock your code and make it thread-safe.

Loading time(s)

Using MonoBehaviour and putting it on a GameObject in your initial loading scene takes this issue away from you, but still leaves one window open for troubles: what if you want to use that singleton in one of your Awake functions in that same scene?

If you remember my Event System tutorial that’s one issue that could require quite extreme solutions, but there is a more general issue here that is explained in Sebastiano Mandala’s blog better than how I can do here in a few words.

Let’s just say that Unity hides away a quite crucial step: the code’s entry point. You’ll never have the pleasure of being sure that the row X of the class Y is really the first thing that’s executed, at least not by just looking at the code. You’ll have to tamper with Script Execution Order settings, hope the next update won’t screw it up, do it every time you want to import your code base on a new project, hope it’s bug free.

Spoiler: that’s bad.

455252569_7b5bcdff02

What’s the solution?

So, we’ve come to understand that what’s bad in singletons is (mainly) two things:

  • they hide dependencies
  • they need to be instantiated carefully

About the hidden dependencies, that’s more than anything a matter of implementation inside your singleton: if you make it so that his state needs management and coordination across its users, that’s where the hidden dependency creeps in. And that’s the symptom of a bad design. If that is the situation you are in, consider using command pattern and having another class manage explicitly the singleton’s state, so that the users can just make a request without needing any coordination among each other.

While the hidden dependency is a universal problem, the instantiation is quite hard to manage in Unity. Luckily there are solutions. Yeah, plural. Which means there’s no best and safest here, I’ll highlight just two of them, but I encourage to seek for more (and to write about it in the comments if you want to share).

ragecomic

Option A: automate everything

One solution is to take away from Unity the control and go all in with frameworks like the aforementioned Zenject or Svelto.ECS.

What it does is to manage for you the instantiation of classes, of course you need a solid understanding of what Inversion of Control and Dependency Injection are and why they’re needed or you’ll end up in a horrible mess, but on the other side you get to have your code layout practically already decided with a safe and sound architecture that’s Unity-independant. This will also enable you a lighter maintenance process.

On the con’s side there’s now a framework running in the background that will consume resources, will cache information and generate garbage without you even noticing. In these conditions keeping up with memory budget gets harder, in some cases this is therefore just not an option. Also, readability of your code takes a hit, since now you must mind unwritten implications of using a reference and since the system forces you to make a bit more boilerplate code.

In this solution you just don’t use singletons because you don’t need them. You already have global access to services (the framework does it for you) and as for uniqueness you can just use a static class.

art-1837073_1280

Option B: handcrafting

One other solution is to carefully think about dependencies and manually manage instantiations (AKA filling the dependency graph). This means again to take away control from Unity, but by using the tools that it gives you. This also means that you just define an entry point for your code (for instance by using a dedicated scene that gets loaded for first) and use a dedicated script to manage all the objects’ instantiations, so now the singleton doesn’t manage his own instantiation, it just wards off against duplicates.

This has of course the big advantage of readability: everything is laid down clearly in your code, nothing stays hidden any more and you know what gets done when. Another big plus is performance, since you are in charge of doing everything you can put that extra care in it to avoid wasting a single bit.

The problem here is that now you are in charge, there’s no standard to protect you from bad decisions. Maintenance gets a bit harder and if you work in a team it requires surveillance to check that no-one caves in to deadline pressure and starts cutting corners. It also means that you will have to plan ahead and consider the implications of your design, wich means time shall be devoted to this.

In this scenario you can use singletons but you’ll have to care for everything and be mindful of the risks they carry. Again, my approach here is to think static class with an inspector, but the good way to go may also extend further, you just need to remember that a singleton is no excuse for not abstracting what can be and not to follow the SOLID and IoC paradigms.

keep-calm-and-hakuna-matata

 

Some non-problems

Sometimes I also heard as an argument against singletons something along the lines of : “it breaks solid because it has to deal with its own instantiation, which by definition violates the Single responsibility“. Now, technically, that’s undeniably true. Practically it isn’t. The only reason we’ve got to write boilerplate code to get a singleton is because this is not a feature of the language.

If we were in a world where static classes were not a thing and only static fields existed, to create a static class would imply some tinkering, just the same way. Would that violate the single responsibility? Abso-fucking-lutely. Would anyone argue that therefore we shouldn’t use static classes? probably someone, this doesn’t mean we should consider it a good reason not to use static classes.

If you decided to have a singleton, that’s on the same level of deciding to have a static class. The only difference is that you can’t do it with just one word in your language. The real issue is when you instantiate it, but that procedure is not what makes a singleton a singleton. You can delegate instantiation to another class and only check for uniqueness in the singleton itself (something that you wouldn’t need to do if there was a language keyword). If someone doesn’t delegate this responsibility on another class, is not due to the pattern but to implementation.

Another quite widespread criticism is that singletons carry state around the application’s lifetime and that makes automated testing harder. Again, this is undeniably true: they do carry state and state does make tests harder.

But the thing is that it’s not just singletons that have this issue! You would get this problem with any interaction with external components that just don’t always give every single time the same result. So except for pure functions you already deal with this for all external dependencies you have in any class.
Also, if you have given the singleton a state it’s probably because you needed to represent something that will still need a stateful representation even if implemented elsewhere.
You’ll therefore get the same result with any other pattern in terms of testing problems.

So, why I did it that way?

Now it should be more apparent why I picked that specific design to implement singletons within Unity:

  • inheriting from MonoBehaviour delegates the instantiation to the engine, so no thread-safety measures and locks are needed
  • making it a component of a GameObject enables to use the inspector, which means a designer can tweak the parameters
  • using Awake to initialize the reference leaves room for other classes to use it on Start
  • creating a class from which to inherit the singleton-ness helps to keep different responsibilities in different files and enhances readability

In other words, although I didn’t use a great deal of Unity’s features, it’s a very Unity-dependent code in its design.

And by the way, if you don’t make the instance variable publicly accessible and write all your public functions as static functions that access the instance, you’ll really basically get a static class with an inspector.

public class StaticWithInspector : MonoBehaviour
{
    static StaticWithInspector instance;
    public static StaticWithInspector Instance
    {
        set
        {
            if (instance == null)
            { instance = value; }
        }
    }

    void reallyDoIt() { }

    public static void DoSomething() {
        instance.reallyDoIt();
    }
}

See? here we have it, the Instance property allows to check for uniqueness and the usage will be exactly the same of a static class. Of course you can use this in combination with the script of the last time.

That’s all folks!

I hope that in this post there was something of value even if this time there was no script to grab. Last tutorial left so much untold that I felt the need to write about it. I’ve also received a lot of useful feedback, wich by the way has shown me that there’s interest even in something that’s not just a basic coding how-to.

Special thanks go to Sebastiano Mandala’, Christian Meneghini, Claudio Freda and Lars Kokemohr for their remarks.

As usual, if you want to keep up with my stuff, consider registering with my newsletter. For any comments discuss down here or hit me up on Twitter.

P.S.: I’m currently looking for a gamedev job, if you are interested have e look at my portfolio.

Share
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

Leave a Reply

Your email address will not be published.