Search Unity

When to use dependency injection

Discussion in 'Scripting' started by mrCharli3, Oct 12, 2017.

  1. mrCharli3

    mrCharli3

    Joined:
    Mar 22, 2017
    Posts:
    976
    I've been playing around with Unity for 2-3 months and finally start feeling comfortable.

    So naturally I'm going to restart my project from scratch and make the code nicer and more scaleable.

    Long story short, I'm used to using dependency injection as a best practice (coming from asp.net MVC development), however it seems a bit anoying in Unity. Does anyone actually use it?

    Should I just use Interfaces for polymorphism instead?
    i.e

    Code (CSharp):
    1. interface IDamagable{
    2.  
    3. public void hit(int dmg);
    4.  
    5. public void kill();
    6.  
    7. }
    Then use this Interface for every object in my game that can take damage?
     
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Personally, the two scenarios in which I most commonly use dependency injection:

    1: UI code. Your game objects, in general, should not know anything about your game's UI, but they should offer up all the hooks they need to for your game's UI to control them as needed.

    2: Editor tools. This one is enforced by the compiler, because runtime code can't know about editor code. But the same concept as the above applies.
     
  3. MaxGuernseyIII

    MaxGuernseyIII

    Joined:
    Aug 23, 2015
    Posts:
    315
    When you write "dependency injection", do you mean the design principle or do you mean the category of framework?
     
  4. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    Those are both true and inversion of control does apply here. I'm not that sure about dependency injection though. Mainly because of this disadvantage:
    "Dependency injection can make code difficult to trace (read) because it separates behavior from construction. This means developers must refer to more files to follow how a system performs."

    Though it does seem that dependency injection is a fairly wide term. Supplying a component through the constructor seems perfectly fine in any case to me, but that's maybe because I'm just used to that. You can throw exceptions if the parameters are required and null and skip any null testing during actual use.

    In the framework design version, things get passed automatically based on requirements, which in my opinion has the above disadvantage of more difficult tracing through the code.
     
  5. McDev02

    McDev02

    Joined:
    Nov 22, 2010
    Posts:
    664
    I use it often as it gives me more controll. I just wrote this code below in another Thread. This is how I often setup projects and where I use dependency injection. I don't like to relate on Unitys code execution.

    I often have a Controller and one or different Entities which relate to it. Either the controller generates the entities or gets its references in the scene. Entity then recieves its controller and all other dependencies it needs.

    Code (CSharp):
    1. public class GameManager : MonoBehaviour
    2. {
    3.     CameraController Camera;
    4.     EnemiesController Enemies;
    5.     FancyStuffController FancyStuff;
    6.     void OnGameLoad()
    7.     {
    8.         //LoadObjects and initialize
    9.         Camera.Setup(this);
    10.         Enemies.Setup(this);
    11.         FancyStuff = new FancyStuff(Enemies);
    12.  
    13.         //Do stuff...
    14.  
    15.         //Start Game:
    16.         Camera.Run();
    17.         Enemies.Run();
    18.         FancyStuff.Run();
    19.     }
    20. }
     
    Last edited: Oct 13, 2017
  6. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    That in my opinion is the reverse of inversion of control what dependency injection should do. Now the GameManager specifically knows that there is a EnemiesController and FancyStuffController. I'd have those controllers look up the GameManager singleton. That way you can be flexible in which controllers are in the scene.
     
  7. McDev02

    McDev02

    Joined:
    Nov 22, 2010
    Posts:
    664
    But that is not how dependecy injection works, it's the exact opposite.

    I don't know the automated way you describe, but from that little ASP code I wrote myself there always where a point where I had to supply an object with a method. Maybe you talk about things like the parameters of a Controller method? That is certainly a fundamentally different concept.
    You could also use things like BroadcastMessage for instance, Event handlers or work with the
    params keyword for passing objects.

    You could handle it optionally as well or simply supply a null parameter.
    GameManager could simply do this. At some point you have to deal with references, but Singleton is the opposite of injection.

    Code (CSharp):
    1. public class GameManager : MonoBehaviour
    2. {
    3.    [SerializeField] CameraController Camera;
    4.      [SerializeField] EnemiesController Enemies;
    5.      [SerializeField] SomeObject SomeObject;
    6.     void OnGameLoad()
    7.     {
    8.         //Wheather Camera or Enemies is defined doesn't matter, they can be null
    9.         SomeObject.Setup(Camera, Enemies);
    10.     }
    11. }
    Adding: I prefer to write code which does not have to much uncertainty in terms of if specific features or components exist or not. Maybe that is a difference, if you want to achieve something that is more generic then you can go with the Singleton Pattern. It is great for prototyping of course but can make things complicated on bigger projects.

    I also make use of what you described but only for Debug and Test purposes or simply Editor code.
     
    Last edited: Oct 13, 2017
  8. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    It depends what kind of GameManager that is.
    It's a matter of what you define it to be.

    GameManager is a very generic term, and you can either use it at the bottom of a whole module/component that anything depends on and has access to.
    Or you use it as the "master" that manages and coordinates the existing components.

    In his example, it looks like it's more application specific that kind of coordinates more self-contained components.

    For instance, if the Enemies type is some kind of manager itself - which it appears to be - it might be be self-contained, so knowing about the GameManager (something that could be very application specific) is nothing it should care about.

    I've long struggled with exactly that, with the decision how to consistenly structure my framework.
    I've come to the conclusion I'd have more freedom if i choose the latter. The smaller components are self-conained and are used by a more specific (yet general-purpose) manager. This applies to many things in the real-world anyway and therefore can be translated into OO structures very clearly, without it being a mess.

    If i ever need to use an instance of that component - that is, only this specific component - I can do it without the need for the managers.

    In contrast to that, if I needed a manager for my handler to work, well... it's IMO not as flexible and brings lots of limitations - at least for something you want to generalize.

    In regards to very application-specific implementations - I'd probably be way less critical about such decisions.
     
  9. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    Yes, Suddoha, I think the bottom line is that you should not focus on one architecture for everything. If indeed EnemiesController is generic with a project specific implementation, the solution is fine. But if I see the code above, it itches to create an abstract Controller class on top of CameraController, EnemiesController and FanceStuffController and change the variable of GameManager into List<Controller>. Central control, but being flexible in what it controls. Much like a plugin system really.
     
  10. McDev02

    McDev02

    Joined:
    Nov 22, 2010
    Posts:
    664
    That would be another option but basically is what I have described with Controller and Entity relationship.
    If you want to go fully felxible then you would have to programm a system like this. But I would not do that just for the sake of it, only use it if it is necessary.

    But back to your example, in contrast to IDamagable you can use a Damagable component. This could be done in various ways, e.g. like so:
    Code (CSharp):
    1. class baseEntity:MonoBehaviour
    2. {
    3.     List<IComponents> Components;
    4.     void OnSendMessage(string msg){
    5.         foreach  component in Components :
    6.             component.RecievMessage(msg);
    7.     }
    8. }
    If Component is a MonoBehaviour then BroadcastMessage would do the thing.
    Code (CSharp):
    1. class Damagable:MonoBehaviour
    2. {
    3.     int health;
    4.     void OnDamage(int value){
    5.         health -= value;
    6.         if( health<=0) Die();
    7.     }
    8. }
    9.  
    10. //Somewhere else:
    11. GameObject Entity;
    12. Entity.BroadcastMessage("OnDamage", 20);
     
    Last edited: Oct 13, 2017
  11. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    I don't know; I kind of agree with jvo3dc. I'd have to see more code to know what is actually happening there, but it seems like GameManager allows access to all the Controllers and all the Controllers have access to GameManager, which means everything is dependent on everything. That's the exact problem that dependency injection is supposed to solve. If everything's dependent on everything then you might as well just make GameManager a Singleton.
     
  12. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    I use it; I use Zenject and highly recommend it. It's open source and updated regularly: https://github.com/modesttree/Zenject

    It's definitely helped me untangle some complicated hierarchies.
     
  13. McDev02

    McDev02

    Joined:
    Nov 22, 2010
    Posts:
    664
    It's actually meant to prevent Singleton not how the relationship looks like. GameManager of course is the high level class in this example. The idea would be that you go down the hierarchy and at every step you hide more and more of the higher level objects. I also often use it to make it simpler, just because I inject GameManger doesn't mean that I store it inside but rather grab 2 or 3 of its public members.

    I only give my view, I used Singletons excessively in a recent project and it ended up in a mess, not using it, only for a few select classes, was a great step forward. In the end it depends on the kind of project.
     
  14. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    That makes sense, but I would just pass the "2 or 3 public members" directly instead of passing everything via GameManager every time. To me, half the point of dependency injection is to let you easily see what something's dependencies are, and having a constructor or initialization method that takes the dependencies as parameters is pretty simple. Always passing everything to everything and hiding what you're actually dependent on kind of makes that harder to do without any real benefit.
     
  15. ArachnidAnimal

    ArachnidAnimal

    Joined:
    Mar 3, 2015
    Posts:
    1,832
    I think I understand what "Dependency Injection" is from everything I've been reading.
    The OP provides the example code showing uses of Interfaces (for decoupling). Not using Interfaces would result in coupled code. How does not using the interface approach have to result in using dependency injection?
    The OP is asking "should I use dependency injection or this interface? ". I guess I just don't fully understand the question.
     
  16. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542