Global Variables vs DI

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • Global Variables vs DI

      So I've got about 1/4th of my engine up and running using the GCC4 code base as the back bone, but last night I hit what is considered a code smell in c# / web development as I realized a lot of dependencies were being pulled from global variables.

      I had planned on forking my code base and re-factoring it to inject dependencies through client class' constructors / init methods as opposed to allowing them to grab dependencies from public properties off the global application object but I'm sure there's got to be a reason why you guys did it this way, just not sure what it is yet...

      An example would be to extract an interface from the ResCache and inject a weak pointer to this interface into the GameCode4_Hlsl_VertexShader as opposed to allowing it to grab a ResCache from the g_pApp object in order to retrieve the hlsl file.

      (Disclaimer: I'm new to c++ so maybe the answer is trivial and my noobness is keeping me from seeing it so I apologize if this is the case :))

      Interested to hear other peeps take on this.

      Thanks!
      -bullgoose

      The post was edited 1 time, last by bullgoose311 ().

    • I believe the answer is that it simplifies the engine overall. Since the focus of the book is on the architecture of major components, certian design desicions were made so that it would be easier for the readers to understand the major system components without getting distracted by all of the little intricate details of the engine. It's much easier for noobs (like myself) to understand the same globals referenced everywhere than to have to trace back dependancies introduced through init function calls and the like.

      That's my take on it anyway. :D
    • You're correct, global variables should generally be avoided, but there are cases where it's much cleaner to use globals.

      Let's use the resourse system as an example. If you passed the resourse system as a parameter to every single system that needed to access resourses, that would certainly remove it from the global space. What if you did that for eveything? Soon, you'd have something like this:

      Source Code

      1. virtual void VInit(weak_ptr<ResCache> pResCache, weak_ptr<EventMgr> pEventMgr, weak_ptr<ProcessMgr> pProcessMgr, weak_ptr<GameApp> pGameApp, weak_ptr<GameLogic> pLogic, weak_ptr<SomeOtherSystem> pSomeOtherSystem);


      Even worse, every VInit() funcion will have a different signature. This make inheritence and function overloading a nightmare.

      I've seen (and use) a few patterns to help solve this. On The Sims, we have a services class that holds all of these manager type objects and very few truly global objects (though I still consider theseb global, even though they're hidden behind a class). On my own games, I do a combination. I have a global g_pApp variable and a few other global managers (they mostly follow the singleton pattern). I use a trick of hiding objects behind namespaces as well, but the effect is the same.

      -Rez
    • Thanks for the responses guys.

      Yea I understand wanting to have the initialize method's signature look the same across various components, and if this weren't a game engine I'd just say "right that's why they go in the constructor, they're implementation details that shouldn't be exposed through any public methods"....but I do remember there being a specific chapter or 2 in the beginning of the book mentioning that initialization through constructors was a bad idea so sounds like that's out. I won't be re-factoring after all, thanks again for the clarifications!
      -bullgoose
    • Even with constructors, you generally want them to have the same signature within a system. Many game objects are instantiated through factories, so they have to have the same signature or it gets really messy really fast. And now the factory code is tightly coupled to a bunch of other systems.

      Also, you're correct that the constructor is only used for initialization that can't fail.

      -Rez