I have the tendency to overuse singletons...

Hello people!

In recent years, I have acquired the addiction of using too much Singletons in my projects. All the managers that I project have already started to create a Singleton…

Now I ask myself the question: is this healthy?

Do you often use many Singletons in your projects?

Is it working fine?

6 Likes

So you have a megaton of singleton?

5 Likes

This is the most important question, followed immediately by “Is it maintainable?”

You can spend a ton of time writing “correct” code, that complies with every “best practice” and recommended pattern and style guide ever written. Or you could actually get your game made in under a decade. I suspect it would be very difficult to do both.

PS - I use singleton managers all the freaking time.

9 Likes

Hi, I’m orb and I’m a singleton addict. I can manage it. I made a singleton to manage my singletons…

10 Likes

Singletons are often the best way to do something, especially manager classes.

I know people seem to despise Singletons, but you do need different classes able to communicate with each other. It almost seems unavoidable, but I would love to hear how you can build a game of any sort of complexity without at least one globally available object.

are they causing problems for you? i tend to have 2 or 3 singletons in each project, and often will use my main game manager singleton as a entry point for something similar to the service locator pattern.

as long as its manageable for you just stick to it, if its not manageable look into what you can do to improve things.

I also make pretty heavy use of creating things that dont need to exist in the scene as a ScriptableObject and dragging it reference into anything that needs it, or referencing all of these objects from one singleton and having a method for finding the instance based on a generic type parameter.

Using singletons itself is not a problem, using them when it’s not necessary could be one.

If some object is conceptually required to be unique in its existence, then actually prohibiting multiple instantiation of the class by using singleton pattern could be even considered as a best practice.

But if you find yourself relying on the pattern, simply because it’s easier to obtain references that way, then probably it’s time to find out what other options there are, like turning some of them into ScriptableObjects where appropriate, or refactoring your code to introduce some type of class that act as a context or service locator that holds references to other crucial services, or considering to introduce a DI(dependency injection) container like Zenject (personally, I found it to be very powerful and quite easy to use).

Are there any moments where you spend hours dealing with a problem and at the end of it think ‘I wish I hadn’t used a singleton here’?

I used to use singletons a lot, and had a lot of those moments. Now I tend to avoid them like the plauge. If you aren’t having those moments, then you probably don’t need to change your architecture. But if you do game design on the fly as you write code, singletons will be the bane of your existence.

I would rephase that one to “How much time do I actually spend maintaining it?”

Maintainable code is good, if you actually do code maintenance. If you never do maintenance, you’ve just wasted your time writing maintainable code.

1 Like

What are drawbacks or disadvantages of singleton pattern? - Stack Overflow gives some of the basics.

Using good design does not inherently take more time. It will very quickly save you time.

But more to the context of unity. Most of what people use singletons for is wrong. There are cases where you need them. Usually in the context of retaining instances dealing with IO. Sometimes unity’s own limitations mean you need to cache stuff to avoid reloading it between scenes. But you should never expose that publicly. So even if you actually need a singleton, you don’t need a public singleton.

Dependency injection can help here, but just saying use dependency injection still doesn’t tell you ok how do I handle stuff that needs to persist for the life of the game.

The answer to that is usually a singleton of some type. But again it’s not public. The approach I prefer is to to use lazy caching. You can just call methods to set everything, or you can have the internal logic manage that automatically, by loading stuff as it’s called for and caching it if needed.

You can certainly use something like Zenject, but it’s not necessary. You can start out here simply by just creating base classes that derive from monobehavior. Lazy caching helps here because that way your base class doesn’t need to use Start/Awake. These base classes can provide protected methods to access what you were originally providing via singletons. As your project grows you can add more feature specific base classes.

If it’s something you only use on one scene… don’t use a static class (necessarily) because that memory never gets freed up. Other than that Singletons in general are fine. They are hard to test and they sometimes make it hard to know where an error originated from but they’re not evil.

If I had one suggestion, it would be to use a static class as a lightweight container to house your managers if you’re using static at all. The manager itself can be a regular class. The static class can have a property with a private set and the static constructor can set that property to an instance of your manager, that way it only happens once. You still only work with a single instance of your manager and it’s still a Singleton, but not static. That allows you to implement interfaces with your managers.

I don’t know if you’re using static classes at all or just basically the Singleton pattern for Monobehaviours but it’s something to keep in mind.

The main benefit of using a DI container like Zenject in this context, could be that with such a framework, you can manage lifecycle of your components in a declarative manner.

Of course, it’s not the only way to solve the potential problem of singletons, but being able to just declare “this service should be persist through the game” is quite a neat feature.

Actually, if I remember correctly, DI containers became popular once it began to be used as a replacement for Service Locator pattern in enterprise applications, which is specifically designed to mitigate (somewhat insufficiently) the shortcomings of using singletons.

1 Like

When your projects drag on as long as mine do, “development” and “maintenance” are pretty much the same thing. :stuck_out_tongue:

2 Likes

I see writing a maintainable code to be more of a habit than of an investment.

It’s not unlike the use of Prefabs, in my opinion. Those who just started to learn Unity might fail to grasp the benefit of using them, or having difficulty learning how to create or instantiate them. And if a project already has thousands of duplicated game objects, it might not worth the cost of replacing them all with Prefabs.

However that doesn’t mean that one shouldn’t bother learning how to use a Prefab until one is involved in a project with a lot of maintenance tasks.

As you know, creating a Prefab doesn’t incur too much overhead once you understand the concept and get accustomed to work with them. And its long term benefits probably far outweigh the cost involved in learning it for the first time.

And I believe the same can be said of various best practices regarding writing maintainable code. One might find it less intuitive to read and write code using the Factory pattern, for instance, compared to just using a long series of if-else statements. And probably the cost of refactoring an existing codebase to replace every such conditional statements would be prohibitive, if the project is large.

But like Prefabs, once you understand the concept of Factory pattern and get accustomed to use it, you would start using them as easily you would use plain if-else statements. The only difference would be you’ll begin to see people who haven’t experienced the same process complain that your code is ‘too complex’.

Probably it’s just a matter of a trade off between keeping your codebase clean, and making it accessible to other developers in your team, if they have widely varying degrees of experience.

2 Likes

I would define it further as just a way of writing code. Code maintenance and large refactors is not something I even do anymore. Unless I just completely missed something and didn’t catch it until much later, but with experience that happens less and less.

What I do is I spend probably 10% of my day making small refactors. If I touch a piece of code I leave it better then when I started. Sometimes I have to stop and do an hour or so of work, because maybe what I want to add pushed it over a line.

The thing is it’s a chicken and egg problem here. You won’t do it until you get to the point where you understand the benefits. And you won’t understand that without doing. The best way to overcome this is to get on a team with great developers and work beside them. I learned more doing that then anything else I can point to. Working beside people that write good code, if you have any ego at all, makes you a better developer faster then anything else.

2 Likes

There is a high correlation between people who hate singletons and people who have used singletons stupidly. Just about every time I’ve ever heard someone rail against singletons, they have a story that usually boils down to using a singleton in a situation where it never belonged or expanding onto a singleton past the point where it made sense.

They are very context sensitive, which flies in the face of maintainability that wants code to be context agnostic. Then it’s also good to stress KISS and single responsibility. They should serve one straightforward function, and any modification should require re-evaluating everything about them.

I think we are approaching the same concept from different angles. There are some aspects of ‘good’ code that I know will pay off. I do these all the time pretty much by default. There are a few other aspects of ‘good’ code that I know will lead to me doing more work for no benefit, so I tend not to do them.

I’m not entirely sure of this. Any programmer can easily recognize the point where code is causing them problems. Its pretty easy to notice when a minor change in game design means a lot of work. And then its just a few google searches away to find the best practices designed to solve the exact problem you are facing.

That would be me. Almost every time I’ve used a singleton, I get to a point where it no longer makes sense to use one, and then I need to switch to something else. Based on this general experience I don’t start down the path of using a singleton.

And if I do use one, I tend to code it very defensively. Its probably not going to do the job in the long term, so I might as well be ready to remove it from the start.

1 Like

We use singletons for things like

MaterialManager.Instance.GetMaterial(transform);

However I wouldn’t have done it like this in a none game, then I would have used a DI container and if needed configured the MaterialManagers lifetime to singleton. Anyway, now I’m babbling.

To communicate between components we use a EventAggregator, which is a much more decoupled way than singletons

Singletons are great! But only as long as they are used correctly. From what I can tell, most people use them when they need easy access to a class from anywhere in their code. Just make it a singleton, and you can call MyManager.Instance from anywhere to get a reference. This is not a good reason to use a singleton, and if this is your only purpose you should use a different approach.

However if what you want is to make sure there is only one instance of a class at any time, then the singleton is the correct solution. For example if you have some limited system resource, it makes sense to have just one instance of a class for interacting with it. The most common, but perhaps not the best example, is the log writer. You only have one log file, so it makes sense that all interaction with the log goes through a singleton class.

If you only want easy access to an object from anywhere in your code, you are better off using some kind of inversion of control. This is your Service Locator or Dependency Injection. You don’t actually need to use any of these to have inversion of control, simply passing the object as a parameter could also be considered inversion of control.

In my opinion, this is not a solved problem in object oriented programming. Dependency Injection is the most recent attempt at solving this, but for me I don’t like how you need to set up all the relations in a single place, and then have to rely on black magic for the references to be resolved in all your other classes.

My favorite solution is to use “manual” inversion of control, and just pass the objects along in the constructors. Yes it might make the parameter list a bit longer, but I don’t really see this as a big deal. The problem with doing this in Unity though is that classes derived from MonoBehaviour shouldn’t have constructors, and it really doesn’t fit with Unitys component based paradigm either.

I’ve had my bit of back-and-forth on this, I’ve tried singletons, dependency injectors, service locators and just setting references in the inspector. I’m currently using the service locator pattern in Unity, and it works reasonably well. Managers just register/unregister with the locator in OnEnable/OnDisable, and I can have single or multiple instances registered.

It works, it lends itself well to testing as long as I use interfaces, and the flow is relatively easy to follow. It’s no silver bullet though, so if anyone comes up with a better solution I’m all ears.

5 Likes