Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Probably the most understandable explanation of Dependency Injection I ever read.

Discussion in 'Scripting' started by TokyoDan, Jul 24, 2017.

  1. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    Just found this recent (March 2017) paper (Bachelor’s thesis)by Niko Parviainen on Dependency Injection and Inversion of Control in Unity. It is probably the most sensible, simple, and easy to understand explanation I've ever read (And I've read a lot of them over that last two years).

    The author refers to Zenject a lot and also to Adic. It ends with a game example.
    Dependency Injection in Unity3D
    Also here is the game project (MirrorPuzzle) on Github.
     
    Michael-Ryan likes this.
  2. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Dependency injection has always struck me as going a heck of a long way to avoid a GetComponent call. Its a Red Queen's race, running really fast just to stay where you are.
     
    Ryiah and lordofduct like this.
  3. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    I don't know. After I added multiplayer and online networking my last game got so hard to work on because of all the things that DI is intended to solve. If you think that DI is just to avoid GetComponent than you don't really know what DI is about. It is to keep things from depending on things that depend on things to the point that later modifying just a simple thing becomes a nightmare or it breaks something so far away that you have no idea how or why one affects the other.

    But yes, some DI frameworks can be so involved and complicated that it is almost impossible to understand (the implementation, not the concept) And it kind of kills whats good about Unity. That's why I like Adic which seems to be very lightweight.
     
  4. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Sure. I get the point of it. But DI isn't the only way to manage dependencies.

    That is one of my big issues with the article you linked, and most articles on DI in Unity. They generally start with 'Find is bad, Singletons are bad, I hate the inspector'. And end with 'Therefore DI'.

    I can manage dependency for much less effort then it takes to force a dependency injection framework into Unity.
     
    TokyoDan and lordofduct like this.
  5. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,513
    My beef with Dependency Injection is that if you don't worship it (cause hey, I've used it... it's got its uses)... but if you don't worship it. People come out of the wood work and say thing like "you don't really know what DI is about".

    I always like the phrase from James Shore:
    "dependency injection is a 25-dollar term for a 5-cent concept"
    http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html
    (and this was back in 2006! The year I started programming professionally)

    And honestly... his explanation is perfect. DI is just that the creator (usually a factory) gives an object the fields it uses, rather than letting the object create it.

    The various DI frameworks out there create a... a superfluous syntax through which to do this. Which always feel like some variation on the composite pattern (or component pattern at that).

    It's interesting because you might notice I'm tossing around very well known patterns here... factory, composite. DI to me has always just been a 'step' in those patterns.

    Like DI is this amalgamation pattern that introduces people to the concept of interfaces/contracts.

    Do I use DI?

    ALL THE FREAKING TIME!

    Do I use a DI framework?

    NOPE!

    In the end... DI to me sounds like in the early oughts, when Java was at its peak, and .Net was gaining traction in the Enterprise realm. The old engineers, the old school procedural, still write Cobol on the regular, engineers were sitting around grumbling about these new-fangle concepts. And some marketing savy young engineer came up with a real fancy term to wow the CTO/manager, but really just showing off a basic principal of OO design and contract design... and it took off.

    A 25 dollar word, for a 5 cent concept.
     
    NotaNaN, Ryiah, cstooch and 2 others like this.
  6. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Now that qualifies as the most understandable explanation of dependency injection ever.

    (And one can't help but notice that the Unity inspector itself is a dependency injection system.)
     
    lordofduct likes this.
  7. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,513
    I made this point a couple days ago in another thread. And of course, the wood work came out to tell me why there's so much more to it, since it enables the same ability on none unity classes.

    Which yeah... it does. By facilitating another framework that you have to create dependencies against.

    Which is the humorous part of it all... DI frameworks, an attempt at decoupling dependencies... create an entire framework you're to depend on!

    Really, my beef isn't with DI (I use it...), it's with DI frameworks.

    Not to say I think frameworks are terribad, hell I have an entire framework of code I carry from project to project with me as well (which is also showing quite a bit of bloat... need to trim that bastard). It's just, you know... I think the irony of it is funny.
     
    NotaNaN and Kiwasi like this.
  8. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    How do you do it? If there is a better way I'd like to use it.
     
  9. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    Well, saying "going a heck of a long way to avoid a GetComponent call" does imply a shallow understanding of what it's about. And I don't worship it. Actually I hate it. But I hate the way my last game ended up more. So much that I haven''t coded anything for the last 2.5 years. Instead I've been on a quest to find something that will keep me from ending up in that nightmare of highly coupled and brittle code again. I still haven't found it. At least not one that solves my problem without being overly complicated (seemingly just to make code monkeys feel smarter than others), or that destroys or takes away everything that is good about Unity.
     
  10. kru

    kru

    Joined:
    Jan 19, 2013
    Posts:
    452
    The only reason DI is in any way controversial here is because we don't have access to any constructor, or consistent parameterized initialization method for MonoBehaviors or ScriptableObjects. So, to get the niceness of injected dependencies, we have to rely on frameworks that add complexity, or do without. If we could AddComponent<SomeComponent>(parameter1, parameter2, ...) and have this be the only way to create an instance of that component, enforced by the compiler, life would be good. Instead, all we have is extension methods, naming conventions, runtime exceptions, and public-shaming to enforce good code. Sad.

    It is very strange that this concept has a political air on these forums. You're either Pro-DI or Anti-DI, and you must hate the other side with a passion!
     
    piersb, Michael-Ryan and TokyoDan like this.
  11. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    One general issue with DI is it doesn't actually enforce a good dependency structure. It just moves the dependency structure outside of your code and into a framework. But a poor dependency structure is still poor, no matter where it lives.

    So focus on getting your dependency structure right, and most of the problems will go away.

    I tend to use a layered system approach. A system is a group of classes that are inherently closely coupled. Within a system I tend to just use interfaces for separation. I then wrap each system in its own namespace, which is a convienient way of documenting dependencies between systems.

    I then arrange my systems into layers, with each system only allowed dependencies on previous layers. Each system has a well defined entry and exit point. Disparate systems that have nothing to do with each other communicate through events or messages.
     
    TokyoDan likes this.
  12. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,513
    I don't know. I hang out on a lot of programming forums, many enterprise oriented (as most of my programming history is enterprise). And DI is just as controversial a topic.

    I wouldn't agree with this either. For example, both Kiwasi and I aren't Anti-DI, nor are we Pro-DI (correct me if I'm wrong Kiwasi, don't mean to presume, but that's the gauge I've read).

    I more or less see myself, to continue the political analogy, a moderate. I'll criticize a conservative for going full moron, but when the liberal cheers at my back, I turn around say "now wait a second, you're not that bright either ya know."

    I will say in a setting of unity, I personally find the component based approach fitting for most dependency injection. I obey the interface/contract rules that DI often totes around, because yes, that works very well. I don't depend on (have a field for) a "MobTypeAWalkingAnimatorController", I have a IWalkingAnimatorController that all walking animation controllers implement. And then attach the necessary one to my GameObject.

    The one thing I find immensely useful from DI frameworks is the way it approaches singletons/global services. And this most heavily noticeable, just like Kiwasi pointed out in another thread, that most DI frameworks used in Unity boil down to some sort of Service Locator.

    But injector (factory) part of the DI pattern... I don't create a lot of that in code. Not all team mates write code (well), nor understand those concepts. They do understand GameObjects with components (which is arguably a form of dependency injection). I don't find heavy need for it in game design... aside from the service locator.

    Procedural generation stuff maybe could benefit for it... but not everyone does procedural generation.

    I don't know... DI is just such an enterprisey need to me. The ease of changing dependency on the fly to meet a clients needs. A product that is ever evolving as time moves forward, and is maintained primarily by engineers and their lackies.

    Games are so much more collaborative in those who work on it, from artist to coder, I find it so much overkill. Like you brought a 150 foot crane to help install a family pool in the back yard. Sure... you COULD use it... but why?
     
    Ryiah likes this.
  13. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    Heya, Woodwork here. :p I feel like you're misusing the word "dependency" here. In "dependency injection" it means a class that depends on another class. It doesn't mean "don't use any plugins; write everything yourself". Using an asset doesn't make it a "dependency" in that sense. Zenject doesn't require adding new dependencies into your classes. You could always delete it and just call constructors or initializers manually if you wanted to.
     
  14. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,513
    It was a humorous joke. I was purposely twisting it.

    But yeah... that fact you don't NEED its dependencies, and just call constructors or initializers is my point. DI frameworks vs DI...
     
  15. BlackPete

    BlackPete

    Joined:
    Nov 16, 2016
    Posts:
    970
    It's not so much as being anti-DI.

    It's that over my career, I've seen "paradigm shifting" frameworks come and go, with similar (sometimes hyperbolic) statements being made each time.

    So when yet another one comes along, I do my best not to roll my eyes before I learn more about what it's supposed to offer. In the case of DI, my reaction was pretty much the same as James Shore: "Oh... that's all it is?"

    Honestly, the most valuable thing I've got going is experience. I take a look at a new thing, gauge the pros and cons, and sometimes take the best it offers and incorporate it into my own code, leaving behind the worst of it (or at least minimize the worst of it where I can).

    If you were to ask me to name a pattern I found most useful in my career, I'd probably name the MVC (or MVVM) pattern. Being able to completely replace the UI without touching the logic is a good place to be. Funnily enough, a lot of that is done using DI.
     
    Ryiah, Kiwasi and lordofduct like this.
  16. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,513
    Right, a good tool for the right job doesn't make the right tool for all the jobs.
     
    Ryiah and Kiwasi like this.
  17. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    Well, the I is for "inject" which is what you get out of a framework. The biggest benefit to me is unit testing. I kept hitting walls trying to implement unit tests in Unity because most of my code used GetComponent<> or depended on certain things all being in a scene in play mode. Being able to easily substitute mocks for things that would otherwise be components is pretty nice. Zenject's made unit testing like fifty times easier.
     
    lordofduct likes this.
  18. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,513
    I could definitely see that.
     
  19. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I'm not Anti-DI specifically. I just fail to see how it would be useful for me and my projects. I keep participating in these threads is just in case I'm wrong. Its still possible that someone posts something that gives me a decent use case for it.

    Perhaps that's my problem, I'm not a big fan of unit testing either. I tried it once on a project, and found that I was basically writing the same code twice. Once to do the job, and a second time in the unit test to make sure the job was done properly. Trouble was most of the time the only way to make sure the job was done properly was to do the job again. Which means if I had a mistake in my logic with the class, I would probably make the same mistake writing my unit test.

    After that experience I just went back to hitting play mode in the editor, with the occasional debug.log statement. I'll admit I've only used unit testing once, I may be missing the point of it.

    I just mock up the components themselves. If you are using interfaces, its pretty easy to do. I find this especially useful for procedural generation. I'll plug in a component with an IRandom interface that actually generates sequential numbers. Then once the generation code works, I'll replace it with appropriate noise.

    Mockups is an 'advantage' of DI frameworks that gets thrown around that isn't really true. You can do mockups without using a DI framework just fine.
     
  20. BlackPete

    BlackPete

    Joined:
    Nov 16, 2016
    Posts:
    970
    One case where I find unit testing really useful is if you're developing a server as a service. Especially if it uses some sort of a database. Saves time on re-deploying a server if you're able to test each API call rather than running the client and trying to hit them all.

    Another case is multiplayer testing. It's nice to have an early out before you spend the time updating the client on each machine, setting up a network session, etc...

    Of course, this is just on the subject of unit testing. Nothing really to do with DI frameworks.
     
    Kiwasi likes this.
  21. kru

    kru

    Joined:
    Jan 19, 2013
    Posts:
    452
    Except testing the rest of Unity is 50 times harder. RRRRRRRRRRRRRRRRRRRRRRRRAAAAAGEEE! I need those core unity MonoBehaviors to implement some mockable interfaces. At the very least I want an ITransform.
     
    lordofduct and makeshiftwings like this.
  22. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    I used to think the same way. "Pffft, unit tests? I'll just write code that doesn't have bugs instead!" Like most things in software architecture, none of it is something you "need"... you can code without testing, without decoupling, without any sort of architecture... you could even just use a live electrical wire to telegraph-tap zeroes and ones directly onto the pins of your CPU. ;) To me, the question is never "Do i NEED this" but "Will this make my life easier in the long run?" Unit tests are one of those things that are a small amount of pain up front that might save you huge amounts of pain down the line when your project gets more complex. If you've got a battery of unit tests that you run every time you compile, you'll instantly know if that one tweak you made in one place caused something somewhere else to explode. You could try doing a full manual test of every single thing in your game in play mode every time you change anything, but that takes too long and people inevitably decide that they "probably" didn't break anything with their change and only do very limited testing, which means you won't find the bug until two months later after you've changed fifty other things.

    You can, DI Frameworks just make it easier. Again, to me it's not about whether I absolutely need it, but whether it makes my life easier. Without a DI Framework, you have to write lots of boilerplate code to construct and initialize everything in the correct order and keep track of it all manually. With DI it handles walking the tree of dependencies itself and figuring out what order everything needs to happen in.
     
    Michael-Ryan and Kiwasi like this.
  23. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    Just to be clear. I never said that I like DI, or think it is the greatest thing out there. I just said in my OP that Niko Parviainen's paper on Dependency Injection is probably the most sensible, simple, and easy to understand explanation I've ever read.

    And most of the other ones I've read just confused me and make me NOT want to code.
     
  24. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    Ha! This sound like the way Trump affects people.
     
  25. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    That might be it. None of my projects have been super complex.

    That actually sounds evil. I like to control my own dependency graph. The idea of handing it off to a framework isn't particularly appealing.

    I suppose on a big enough project I could be persuaded to give up control. But it sounds more like using the framework to compensate for not setting up the dependency graph properly in the first place.
     
  26. mahdiii

    mahdiii

    Joined:
    Oct 30, 2014
    Posts:
    856
    I do not really know why we need dependency injection frameworks when we can use GetComponent<Interface>().
    We can make testable components as well.
    I can make several prefabs with different classes that implemented the same interface.
     
  27. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    No one necessarily needs the frameworks, but in complex systems they can help to manage all the dependencies in a more maintainable manner.

    If a developer is experienced enough to leverage its benefits properly, why not? If it makes them happy, let them do it.
    You can just as well make code quality worse when you use a DI framework in a naive manner.

    As for GetComponent itself, it's not really comparable, because it's a completely different approach.
    It's more like a service locator that's deeply anchored in the core of the engine. The fun fact is, that it's implementation in Unity enables you to conveniently use the very opposite of DI. And sometimes, that convenience can harm code quality (but not necessarily needs to, like always it depends to which extent you're (ab)using it).

    But there's still a DI system built into Unity, as already mentioned by @Kiwasi.
     
  28. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    GetComponent is an example of a service locator.

    I would say the general academic consensus today is that dependency injection provides all the pros of service locator plus more, and service locator has all the cons of dependency injection except one which I'll get back to..

    in that sense, dependency injection is largely considered the preferred choice, all other things being equal. This is why you sometimes hear the service locator referred to as an anti-pattern. It's not actually bad, there's just a perfect substitute.

    Back to the one con: a tenant and, IMO, one of the main advantages of dependency injection is to keep the construction of your program centralized; the so called "composition root". If you're using a framework which hides the main entry point of your app and controls the creation of your components without any reasonable hook -- e.g. Unity -- then your composition root becomes fragmented and you lose the main pro of dependency injection.

    I believe frameworks that attempt to alleviate that issue in Unity often end up introducing a number of other complexities and more or less ruin the advantage that dependency injection offers.
     
  29. mahdiii

    mahdiii

    Joined:
    Oct 30, 2014
    Posts:
    856
    Yes. GetComponent is a service locator. Dependency injection is different from IOC and definitely from service locator.
    Let's look at some examples:


    Code (CSharp):
    1. public class Class1:MonoBehaviour{
    2. // You need gameObjects or scriptableObjects implement IInterface2
    3.    [SerializeField] private GameObject _obj;
    4.    private IInterface2 _data2;
    5.  
    6.    private void Start(){
    7.       _data2=_obj.GetComponent<IInterface2>();
    8.    }
    9. //Inject dependencies in the method
    10.     public void Func(IInterface1 data1){
    11.  
    12.     }
    13. }
    14. //----------------------------------------------------------------------------
    15.  
    16.  
    17. public class Class1{
    18.  
    19.    private IInterface2 _data2;
    20. //Inject dependencies in the constructor
    21.    public Class1(IInterface2 data2){
    22.       _data2=data2;
    23.    }
    24. //Inject dependencies in the method
    25.     public void Func(IInterface1 data1){
    26.  
    27.     }
    28. }
    What is important is that they both use interfaces but the first approach needs a reference to get component. So yes, it depends on a gameobject.
     
    Last edited: Dec 9, 2018