When I started programming one of the biggest mysteries to me was how to provide access to things like SoundPlayer, Renderer, ErrorLog, StateMachine etc to very deeply "nested" objects, like the player.
Let's say I create my StateMachine in main(), inside the constructor of StateMachine I initialize it's member: GameState, which in turn initializes it's member Level which in turn initializes it's member Player.
Player wants to play a sound when he jumps, so he would need to call soundPlayer.playSound(SOUND_JUMP). But Player doesn't have a reference to a SoundPlayer object. He also wants to draw himself at the center of the screen, so he needs a reference to a Renderer and the Window, to get it's dimensions. What do?
After a lot of inconclusive articles (or failure to understand them), I decided to just use a static global class, since I wanted to generate results as fast as possible and I kinda got stuck with it.
Actually, I used a mixture of dependency injection through constructors, dependecy injection through methods and global variables...
Here is an example (not actual code):
class Player
{
private:
SoundPlayer& _soundPlayer;
Sprite _sprite;
public:
Player(SoundPlayer& soundPlayer) :_soundPlayer(soundPlayer) {} // Dependency injection, storing the reference inside the object
void Render()
{
Renderer* renderer = Renderer::GetInstance(); // Global static horrible singleton!
renderer->Render(_sprite);
}
void Shoot(ParticleSpawner& particleSpawner) // Dependency injection through function argument, not stored
{
particleSpawner.Spawn(PARTICLE_BULLET);
}
};
What I really don't like, are the static GetInstance() function calls everywhere. It just isn't clear that this class depends on the other, it just hides somewhere inside of a random method. At least in the constructor it's clear, you can't even create an instance if you don't pass it's dependency in.
Inside the methods parameters it's already a little more awkward, because you're happy you could create an instance of the object, but then it suddenly needs an instance of another class.
The worst part is when I have a deep "component hierarchy" or whatever it's called. I read the term "call-tree" before and think it fits quite well.
Player needs a reference to an instance of SoundPlayer, otherwise it can't be created, so it's owner ALSO needs that reference to pass it in, but it itself DOESN'T need it, except for creating the player, so it also puts it into its constructor as a required parameter, so now THIS class ALSO needs a reference to SoundPlayer, even though it has no intention of ever using it, but one of its components needs it, to in turn construct one of its components, which in turn needs it to construct one of its components, which in turn... feels kind of wrong to me :|
I got so tired of typing it all out that My state machine just has a #define to pass it all in.
Kind of like this:
class StateMachine
{
private:
Renderer& _renderer;
SoundPlayer& _soundPlayer;
ErrorLog& _errorLog;
FileSystem& _fileSystem;
NetworkSystem& _networkSystem;
InputDevice& _inputDevice;
BreadMakingSystem& _breadMakingSystem;
SystemsManager& _systemsManager;
// ...
State* _gameState;
public:
StateMachine(Renderer& renderer, SoundPlayer& soundPlayer, ErrorLog& errorLog, FileSystem& fileSystem, NetworkSystem& networkSystem, InputDevice& inputDevice, BreadMakingSystem& breadMakingSystem, SystemsManager& systemsManager):
_renderer(renderer),
_soundPlayer(soundPlayer),
_errorLog(errorLog),
_fileSystem(fileSystem),
_networkSystem(networkSystem),
_inputDevice(inputDevice),
_breadMakingSystem(breadMakingSystem),
_systemsManager(systemsManager)
{
}
enum StateType
{
STATE_GAME,
STATE_INTRO,
STATE_TITLE,
STATE_OPTIONS
};
void SwitchState(StateType state)
{
#define STUFF_TO_PASS renderer, soundPlayer, errorLog, fileSystem, networkSystem, inputDevice, breadMakingSystem, systemsManager
switch (state)
{
case STATE_GAME:
_state = new GameState(STUFF_TO_PASS);
break;
case STATE_INTRO:
_state = new IntroState(STUFF_TO_PASS);
break;
case STATE_TITLE:
_state = new TitleState(STUFF_TO_PASS);
break;
case STATE_OPTIONS:
_state = new OptionsState(STUFF_TO_PASS);
break;
}
}
};
And inside of the GameStates constructor, again a #define to keep passing it on to it's children.
I was asking myself what the best way to do all of this would be, while refactoring Renderer::GetInstance() out and instead rewriting 100 constructors to accept a reference to Renderer instead. I just thought "What the hell am I doing? Is this really better?!"
Before I go and rewrite everything I want to make sure I do it correctly this time.
This is something that really irritates me about programming, there never seems to be a definitive correct way and I'm only satisfied with perfection, even though I'm nowhere good enough to even achieve anything close to it :P
Should I just leave it like this or is there a better way?