Share via


Dependency injection and good design

I helped preparing a meeting on dependency injection on my team and we had that meeting last week and it lead to a number of interesting discussions. Before we go into that I have to explain the title (which was the same as the title for this blog post). If you look at attributes people tend to claim being attributes of a good software design it involves correctness, adaptability, loose coupling, maintainability etc. interestingly enough is that most (if not all) attributes people tend to come up with are all part of a "super attribute"; testability. So by achieving testability we also achieve a number of other desired attributes. Given that we want to achieve testability on unit level the need for dependency injection is almost impossible to get away from. There are a number of patterns for dependency injection and I have to come back to that in future posts because the list is getting long nowadays.

However related to this we had a lot of interesting discussions. The one that surprised me the most was the argument that dependency injection through design required more lines of code than the untestable original. The argument made was that more lines of code is harder to understand. Excuse me, but that is like saying that the variable name "r" is easier to understand than "distanceFromCenter". Sure I agree that a very long method or class is hard to understand but the addition of dependency injection does not change that. Functionality and dependencies are separate and the understanding of one should not affect the other. Now related to this is a difference in preference by developers. To generalize there are two types of developers when it comes to understanding code; the ones who execute the code in their head and the ones who like to read the code as a book. The former hates lots of small methods since it means jumping back and forth in the code and the latter hates long methods since they need to "extract method" in their head (I'll probably revisit this in the future too).

Another interesting observation was that most patterns for dependency injection changes objects to now expose these dependencies in either properties of constructors. This can be a problem for a software company that ships code libraries for others to use since people may either use a constructor wrong or inject their own objects as dependencies resulting in the object not working as expected. While some of you might think that "well, if you gave it ***, naturally it is doing ***" there is still a problem with this. The customer might not know that they did something wrong and generate a support case that takes a lot of time to investigate just to figure out that the customer did something wrong. Apart from the wasted time there may also be bad-will from this. Good thing is that there is an easy solution; do not make your dependencies public. In .Net for example you can use PrivateObject (which uses reflection) to access private things. pattern is still the same, you would just be using a trick to lower your support costs.

The last interesting observation I made during the meeting was the reaction to the use of a static factory for dependency injection. While static factories minimize the number of added lines to a class a lot of people reacted to what happened to the test code. Several people commented on how setting up the factory and then calling some random method on some random object made it unclear if the dependencies were really being used. I found this particularly interesting since I personally am not a big fan of the static factory pattern but for other reasons.

I guess the bottom line is that there is no single pattern for dependency injection that always work great. But you should have a few in your toolbox and use whatever is best in any given situation.