#Would it be bad practice to use a lot of singletons?

1 messages · Page 1 of 1 (latest)

random bridge
#

Is it in any way inefficient to use singletons for everything there is only one of, to avoid having a lot of references?
Since you can call on a static variable without referencing, it would save me code time since a lot of these call each other and the persistent "Game Data" object.

For context this is a cooking game, and this would be to do stuff like initializing based on custom level difficulty, playing special effects, keeping track of score, etc.

austere gate
#

It’s fine to do but ideally you’d probably reduce it to a Singleton per major system or have a single singleton that has accessible references to other managers

oblique drift
#

I use it strictly for debugging tools I create, when they won't have to have deep integration into final code and it makes it easy to remove it.

silver rivet
# oblique drift There are a lot of problems that can be created widely using Singleton pattern. ...

That's an interesting read.

I don't think about singletons too much. I have maybe 8-12 of them throughout the current game I'm working on and they typically take the reigns on something important, like audio, networking, or UI management; Things that need to be active in a scene (inheriting from Monobehavior) and are almost always relevant. If you have a lot of smaller ones, I can see it getting messy. I guess I would need to see in what scenario this would get out of control. I feel like, if you're managing the dependencies of your classes well, only a few classes would need to be talking to these singletons on the regular and so the relationship to them would not be very mysterious.

fresh idol
# oblique drift There are a lot of problems that can be created widely using Singleton pattern. ...

The "Side Effects Hurt the Ability to Reason About Your Code" point in the blog post is pretty weak, because that logic doesn't apply just to singletons, but generally anything a method uses which doesn't come from parameters. By the same logic, you would need to pass Unity's Application or System.IO's File around, and a method is forbidden from using anything that doesn't come from parameters somewhere.

silver rivet
#

I think what you'd probably want to avoid is just having a low-level class accessing a singleton directly like that all the time.

fresh idol
#

Sure, side effect makes reasoning about your program difficult, but that's not anything specific to singletons.

#

The Logger.instance.Log() example used in that might as well just be Unity's Debug.Log(), and according to that logic no one should be using Debug.Log() directly.

ornate kiln
#

I agree, the blog post is mixing up some random stuff to target the singleton pattern to be a weak point while the reason for the confusion in code can happen at any time with just bad design, spaghetti code, not write methods clearly to their purpose and mixing up different behaviours into one big function and so on. you name it. So as in many concepts about coding, its a question of how you implement it. Anyone can abuse the singleton pattern, get confused by instances being destroyed, accessing the "wrong" instance and what not, but thats not an issue of singletons per se but of understanding how to set them up.

upbeat quail
#

Really all you need is a single singleton and that being your GameManager to help locate your other services as changing scenes doesn't give an explicit entry point.

#

Usually if a dependency requires communication to a manager that should be through delegates, but sometimes I'm lazy and can't be bothered delegating multiple times upwards the hierarchy so I just reduce that time making those managers singletons.

ornate kiln
#

Just to add another thought. I have for example an InputManager and your said GameManager, i can still reuse the input manager not relying on the gamemanager to hold a reference to it.

upbeat quail
#

Right, you don't need to have a reference to all of these systems on your GameManager if they are completely independent.

ornate kiln
#

I think one important thing to keep in mind for singletons but also in general is to not let every script dictate your instance, which can happen easily if you do not layout your architecture which why singletons can be very dangerous if used incorrectly

upbeat quail
#

A better example would be say you want Player to be a singleton, but what happens if you want multiple Player(s)? Well, you can have a list of Player(s) on the GameManager which would remove that Singleton dependency.

ornate kiln
#

Yeah, good example of a bad use of singletons or misconception if not fully thought through

fresh idol
#

I do agree with the premise of the blog post, which is that "you should look at what you lose for using singleton and decide if the loss is relevant to you."
If you are only ever going to have one instance and implementation of input manager, you won't ever swap it out to something else (eg for testing), then don't pretend your code is any cleaner by passing IInputManager inputManager around in every single method. That's just unnecessary ceremony that buys you nothing in the end.

oblique drift
upbeat quail
#

Yeah, the 'clean code' way is through delegating those interfaces but man that's so much extra code I can't be bothered with sometimes.

oblique drift
#

When you have a clear responcibility chain, it's really helps with maintenability and debugging

fresh idol
# oblique drift But when you do attempt to pass it, it makes you think if you really need to and...

Yes that's actually the root point of "You Incur Incredible Fan-In and Promote Hidden Coupling" in the blog post (except the blog post only saw the surface point of it). Having global access to anything you want may encourage you to just do anything you want without a second thought, whereas making access something difficult (because you have to write all those extra boilerplate to pass it down) can act as a deterrent to reckless coupling and promote more thought.
I would say that not everyone is convinced this is a sound strategy to avoid "bad code."

#

I've definitely seen people got fed up with passing things down being too much boilerplate and they end up passing down one single god object dependency everywhere, and I'd say that's about as bad as abusing singletons.

upbeat quail
#

If we're talking like a dependency like 5 parent's deep to talk to the root that's having to create some link in between each parent so it can notify that root.

oblique drift
#

I know the struggle of debugging something intertwined with elaborate state machine, and anything I can do to make my objects code more readable and debuggable is a huge plus, well worth extra bit of boiler plate. And IDE makes it really easy to do Dependency Injection. Rider especially, it has very intelligent autocomplete, and other tools helping with refactoring.

upbeat quail
#

As far as setting up an observer pattern goes and getting those children to notify those managers without an explicity reference.

small crown
#

IMO you should just go with whatever makes your code both stable and easy to understand if you had to come back to it a year after not seeing it. If that’s many singletons, fine. I feel like there’s a lot of talk out there about avoiding singletons and the solutions often end up being more verbose and harder to understand.

#

The one thing I would say about singletons is you should be mindful about how you access them across your code, because you can just end up with brittle spaghetti if you carelessly just grab whatever you want from them everywhere

slender condor
#

in my experience singletons make everything simpler, up until you reach a certain level of project size or complexity, and then once you've worked on one of these giant singleton spaghetti balls it's very appealing to find a way to avoid them in the future lol

small crown
#

For example a static class or ScriptableObject often is a better method than a singleton MonoBehaviour.

#

Like there’s a lot of basic tutorials out there using singleton MB’s when their purpose is just to have a mediator for getting assets or static data

#

That purpose is better served with SOs

brave wasp
#

I've been a big fan of using SO managers for a few years now

small crown
#

I’ve been liking the pattern of having a “settings provider” singleton SO, where it gets lazy-instantiated automatically with the editor and can store whatever data makes sense as a global property for the game — e.g. score to win, score per enemy killed, assets/prefabs that are instantiated for common things. That way you have a single source of truth for these things rather than having them scattered around the project. Additional benefit from the SO usage being the ability to change values that MB instances are using at runtime without needing find the instances to tweak or to re-enter play.

#

Anyway I guess the gist for OP: yes it definitely can be bad practice and can make your code very brittle, but that doesn’t mean you should just sign off singletons entirely. Be mindful of how you use them to try to limit dependencies and always consider whether there is a more stable but comparably convenient solution.

steel lotus
#

either way, if you're working as a team, your senior proly will nag about it anyway XD

muted vortex
#

I think the best time not to use singletons is when something needs its own separate instance, like per-player data, or when it isn’t really a “manager” or "controller" that controls global logic.

Example: in a multiplayer game, you might have an InventoryManager (singleton) that deals with the overall logic of adding/removing items and handling references. But each player’s actual inventory should not be a singleton, because every player needs their own instance of it.
So I kind of look at it like this:

The core logic (stuff that only makes sense to exist once) = singleton.

The things that use that logic or represent individual entities = not singletons.

It’s also true that you could technically make everything a singleton and still make it work, but that usually just makes things messy and manage from my experience.
Sorry, I find it difficult to explain about the concepts, but I just know when to use them. It comes naturally eventually hehe!
Hope this reply is helpful.

upbeat quail