If you really want to go down the singleton route over dependency injection, which I strongly recommend erring against doing just as much as CandiedOrange, then your second solution is not much better in my opinion.
If you are actually going to have persistent, globally accessible objects in your system that are lazy initialized and open Pandora's box, you might as well exploit that idea. I like the solution of simply having, at this point:
play_sound("Explosion");
... cute little code, like the days of turbo C that's easy for beginners to comprehend with minimalist code snippets for tiny applications without understanding layers of abstractions.... just as we can start a console application and output to the global cout
immediately without constructing a console object ourselves and having to pass it around as a parameter.
If you are going to have globally accessible, persistent states, you might as well use it to create this cutesy API you can invoke anywhere. There is at least a cute benefit there to being able to just start up a new project and immediately invoke functions like outputting to a console, playing a sound, or drawing a line to the screen without initializing, acquiring, and passing around resources. That can all be done with a very simplistic procedural syntax and not like Drawer::get().draw_line(...)
, simply draw_line(...)
.
Why do I care about the definition of a System
in this context or even the full definition of an AudioManager
and have compile-time dependencies to both? I want to play a sound, that's it. The implementation of play_sound
could still retrieve the singleton instance for your System
and retrieve the AudioManager
from it and invoke the play_sound
method on it, but that can be an implementation detail of play_sound
. If I'm not concerned with specific instances of a state, which I won't be if there's supposed to be persistent global state instantiated once, then I'm only concerned with what to do, not who provides it. I just want to call play_sound
and have a sound play. I don't care who or what provides the functionality if I'm not required to have this info.
void play_sound(const string& sound)
{
System::getAudioManager()->playSound(sound);
}
When I see devs go happy crazy with singletons and Singleton::get().do_something()
calls all over the place, the first thing that comes to mind is why not just do_something()
and make the retrieval of the singleton an implementation detail and hide the singleton and its type definition from the client? I can't see any negative consequences to doing this that aren't already introduced by the singleton itself.
Does it have some overhead (performance) or cons?
Those calls to get()
a singleton can be relatively a little bit expensive if you are doing them en masse in a loop that only does trivial low-level things with the singleton object. That gets cheaper if you forego lazy construction and initialize everything in a way such that it's guaranteed to be constructed already by the time the client attempts to retrieve it. Also making the singleton get()
method thread-safe if you lazy construct is not entirely trivial (ex: double check locking). It's easier to just avoid the lazy construction than trying to make get()
thread safe when it lazy constructs, which will also yield a more efficient solution that makes singleton retrieval very cheap when the get()
method no longer has to deal with lazy initialization.
The best SE solution is still not to use singletons at all, but if performance is a concern and you insist on sticking with them, it at least helps to forego the lazy initialization and initialize in advance prior to calls to get()
.
theAudioManager->PlaySound(...)
wheretheAudioManager
is a global.