Before starting, I must say that this is the first part of avoiding Singletons in an effective way. The second part is about Dependency Injection, but before reading that I suggest to finish reading this post.
Our target should always be creating a clean, maintainable code regardless of you working alone or with a team. I can't imagine a proper Unity game having a small code base in these days. Well, if you provide everything from libraries, then the story may be different. However, being a bit tidy won't bring any harm, right?
You kiddin'! We know Singletons are bad..
If you are here, it is possible that you know the story. Singletons are global to rest of your code base. Every class is accessing to it directly, and they depend on concrete implementation. We don't know when they are called, and their state. They are not testable in a good way, and violating most of the SOLID principles etc. Problems are very well known. So avoiding singletons should be important!
Even though big frameworks and libraries are still using it for the convenience, we should avoid using it and favor some other approach for the sake of clarity and maintainability.
Idea is the same for both of the approaches. Classes carry their only one instance. This instance is going to live during the entire lifespan of the application. We will be able to reach it, since the instance is static.
I agree that Singletons have couple of legitimate usage, but as game developers, we sometimes like to abuse the usage of some design patterns.
It is just easy to access to the ScoreHandler from anywhere in the game. Yes! So what? Games are a bit different than regular software products, and having a global state is not something weird at all. Maybe!.. But there should be a better way instead of spamming all managers and handlers everywhere.
Creating well-defined relationships between classes would naturally decrease the need of having a Singleton. Your design must be coherent as much as possible but we all know that. Regardless of that it still can get messy when the code base is getting bigger.
Having a Singleton Game Manager?
Imagine that you have a GameManager class which contains all the possible services in it and it is a Singleton facade. So all classes that need those services will depend on GameManager. In addition to that GameManager will pick the right handler for the job. Surely, it fixes some of the problems that are introduced before, however it introduces new problems.
We depend on the concrete implementations not abstractions.
GameManager will be way far from having Single Responsibility.
It is still possible that this might be abused by the classes in the lower level. So we may also violate the Dependency Inversion principle.
We can't claim GameManager is testable as well.
Avoiding singletons is not so hard. However, what we are using instead of it shouldn't introduce more problems than we already have. So? The next improvement step would be introducing Service Locator Pattern. This is also a bit controversial and some claims that it is an anti pattern. As long as you don't abuse the pattern, I think it is quite acceptable.
Of course this example can be improved, but this is the simplest implementation I can think of.
We can register instances using the interfaces as type parameter. So we don't have to depend on the concrete implementations.
We can reset the locator anytime we want. So we also gained some control of the service lifespan.
We can use it for unit testing and include or mock any services we want to use in that specific test.
Let's write more code and use it.
In case of a change on a service requirement, we can create another implementation. Then, register it in the ServiceBinder. We don't have to change anything else. This might be a really good usage for performing A/B testing as well.
Unlike Singleton usage, change impact is quite controlled. Service Locator usage won't require any big modification unless we change the interface.
It is possible to improve the ServiceLocator introducing some kind of scope or context. However, my purpose here is just giving the basic idea. You can have different variations as well.
I honestly never seen anything not controversial related to this topic. In an ideal world, we wouldn't need Singletons but world is not that ideal. Even though, Service Locator helps us on avoiding singletons, it has some negative sides, too.
Service Locators are hiding the complexity. This side affect can be a huge disadvantage if you are designing an API. You'd give horrible times to users of the API due to lack of explicitness. However, you can favor this pattern in Unity instead of using lot's of Singleton.
As a final side note, if you have arrived here, understood the Service Locator Pattern and it did feel reasonable, you should definitely invest some time to learn Dependency Injection (DI). It wouldn't be wrong to say that Service Locator is the simplest form of a DI container. There are many DI frameworks out there. Some has a lot of good features such as injecting with parameters, ids, and conditions. So you can have full control on your dependencies. If you feel interested, you can read my post on Dependency Injection in Unity.
Please write comments, criticize it. I would love to make my content more quality with your feedback.
Service Locator Pattern, Unitytips, Why Singletons are bad?