Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is Dependency Injection Chaining an Anti-Pattern?

Here is the problem, lets say we are making a video game and want to use Dependency Injection. Here is what we have:

Game Class  // This is just the code to keep track of the overall game logic
Character Class // This would be the guys in the game, good and bad guys both
Weapon Class // The weapons for the characters

So normally when I do Dependency Injection I would inject the current location on the maps, and game state into the character so my character would have the info to know where to create itself, etc. Then I have the character create the weapon and inject the values as to the strength of the weapons, as well as some other general game state from the Game Class etc.

This almost seems like an anti-pattern to me. I say that because now you have (at least it seems so to me) code that is very brittle and hard to change. If we want to change the game state information that is passed we are forced to change all three classes. We make the original change to the Game class, then modify Character and then finally modify the Weapon class as well. That is a lot of work especially if you are going 5 levels deep rather than just the 3 here. Though yes it would allow for easier Unit Tests than no DI.

This again sounds like bad practice. Is this the way things are typically done? What is we sort of had a 'MotherShip' pattern where everything is at the top level. So instead of game creates character creates weapon we let game (or some other class) create all of them.

This way if we wanted to add a new weapon to a character the game class could just create the weapon itself and inject it. Not sure what to do on this one. Thanks

like image 238
Matthew Stopa Avatar asked Jan 23 '23 22:01

Matthew Stopa


2 Answers

Chaining or nesting dependencies is a perfectly natural practice (although I have to agree with tster that your example sound a bit strange), but I think I can understand why you find it brittle - that is, if you inject concrete types into their consumers.

The trick is to introduce an interface at every level of dependency so that you can vary implementation independently of the consumers. As an example you should define an IGame interface and inject it into the Character class instead of the Game class itself.

Even so, interfaces with a lot of deep nesting can still be brittle because you might need to change the interfaces themselves too often. This is best addressed by striving to conform to the Hollywood Principle and Law of Demeter as much as possible.

like image 61
Mark Seemann Avatar answered Jan 28 '23 14:01

Mark Seemann


Design questions are tough to answer, especially when the example doesn't seem to make sense. In this case, it makes not sense that a Character would create a Weapon. A character should be responsible for that one character. Maybe you can add a weapon to a character, but, unless the character is a weapons maker, I don't think it would actually create a new Weapon class.

BTW, I think a better pattern than either one of these would be the Factory pattern. Create a class who is responsible for creating weapons. And a class which is responsible for creating characters. That way, if there are 3 or 4 places which need to be changed when a character is created, that factory can handle that behavior and it is contained for future changes.

like image 37
tster Avatar answered Jan 28 '23 15:01

tster