Should I avoid using Dependency Injection and IoC?

Question!

In my mid-size project I used static classes for repositories, services etc. and it actually worked very well, even if the most of programmers will expect the opposite. My codebase was very compact, clean and easy to understand. Now I tried to rewrite everything and use IoC (Invertion of Control) and I was absolutely disappointed. I have to manually initialize dozen of dependencies in every class, controller etc., add more projects for interfaces and so on. I really don't see any benefits in my project and it seems that it causes more problems than solves. I found the following drawbacks in IoC/DI:

  • much bigger codesize
  • ravioli-code instead of spaghetti-code
  • slower performance, need to initialize all dependencies in constructor even if the method I want to call has only one dependency
  • harder to understand when no IDE is used
  • some errors are pushed to run-time
  • adding additional dependency (DI framework itself)
  • new staff have to learn DI first in order to work with it
  • a lot of boilerplate code, which is bad for creative people (for example copy instances from constructor to properties...)

We do not test the entire codebase, but only certain methods and use real database. So, should Dependency Injection be avoided when no mocking is required for testing?



Answers

Although IoC/DI is not some silver bullet that works in all cases, it is possible that you didn't apply it correctly. The set of principles behind Dependency Injection take time to master, or at least, it sure did for me. When applied right, it can bring (among others) the following benefits:

  • Improved testability
  • Improved flexibility
  • Improved maintainability
  • Improved scalability

From your question, I can already extract some things that might have gone wrong in your case:

I have to manually initialize dozen of dependencies in every class

This implies that each class you create is responsible of cresting the dependencies it requires. This is actually an anti-pattern known as Control Freak. A class should not new up its dependencies itself. You might even have applies the Service Locator anti-pattern where your class requests its dependencies by calling the container (or an abstraction that represents the container) to get a particular dependency. A class should just define the dependencies it requires as constructor arguments.

dozen of dependencies

This statement implies that you are violating the Single Responsibly Principle. This is actually not coupled to IoC/DI, your old code probably already violated the Single Responsibility Principle causing it to become hard to understand and maintain for other developers. It's often hard for the original author to understand why others have a hard time maintaining code, since the thing you wrote often fits nicely in your head. Often the violation of the SRP will cause others to have trouble understanding and maintaining code. And testing classes that violate SRP is often even harder. A class should have half a dozen dependencies at most.

add more projects for interfaces and so on

This implies that you are violating the Reused Abstraction Principle. In general, the majority of components/classes in your application should be covered by dozen abstractions. For instance, all classes that implement some use case probably deserve one single (generic) abstraction. Classes that implement queries also deserve one abstraction. For the systems that I write, 80% to 95% of my components (classes that contain the application's behavior) are covered by 5 to 12 (mostly generic) abstractions. Most of the time you don't need to create a new project solely for the interfaces. Most of the time I place those intrrfaces in the root of the same project.

much bigger codesize

The amount of code you write will not be any different, although the practice of Dependency Injection only works great on SOLID code and SOLID promotes small focussed classes. Classes with one single responsibility. This means that you will have many small classes that are easy to understand and easy to compose into flexible systems. And don't forget, we shouldn't strive to write less code, but rather more maintainable code.

ravioli-code instead of spaghetti-code harder to understand when no IDE is used

This is kind of true. Dependency Injection tends to decouple classes from one another. This can sometimes make it harder to browse to a code base, since a class usually depends on an abstraction instead of a concrete classes. In the past I found the flexibily that DI gives me outweigh the cost of finding the implementation by far. With Visual Studio 2015 I can simply do CTRL + F12 to find the implementations of an interface. If there is just one implementation, Visual Studio will jump right to that implementation.

slower performance

This is not true. The performance doesn't have to be any different than working with a code base of only static method calls. You however chose to have your classes with a Transient lifestyle which means it you new up instances all over the place. In my last applications I created all my classes just once per application, which gives roughly the same performance as only having static method calls (among orher benefits), but with the benefit of being very flexible and maintainable. But note that even if you decide to new complete graphs of objects for each (web) request, the performance cost will most likely be orders of magnitude lower than any I/O (database, file system and web services calls) that you perform during that request.

some errors are pushed to run-time adding additional dependency (DI framework itself)

These issues both imply the usage of a DI library. DI libraries do object composition at runtime. A DI library however is not a required tool when practicing Dependency Injection. Small applications can benefit from using Dependency Injection without a tool; a practice called Pure DI. Your application might not benefit from using a DI container, but most applications actually benefit from using Dependency Injection (when used correctly) as a practice. Againt: tools are optional, writing maintainable code isn't.

new staff have to learn DI first in order to work with it

This is kind of true, but Dependency Injection itself isn't actually hard to learn. What is actually hard to learn is to apply the SOLID principles correctly, and you need to learn this anyway when you want to write applications that need to be maintained by more than one developer for a considerate period of time. I rather invest into teaching the developers on my team to write SOLID code instead of just letting them crank out code; that will surely cause a maintenance hell later on.

a lot of boilerplate code

There is some boilerplate code when we look at code written in C# 6, but this isn't actually that bad, especially when you consider the advantages it gives. And future versions of C# will remove the boilerplate that is mainly caused by having to define constructors that take in arguments that are null-checked and assigned to private variables. C# 7 or 8 will surely fix this when record types and non-nullable reference types are introduced.

which is bad for creative people

This is bullshit. I've seen this argument used over and over again as an excuse to write bad code by developers who didn't want to learn about design patterns and software principles and practices. Being creative is no excuse for writing code that no one else can understand or code that is impossible to test. We need to apply accepted patterns and practices and within that boundary there is enough room to be creative, while writing good code. Writing code is not an art; it’s a craft.

Like I said, DI is not appropriate in all cases, and the practices around it take time to master. I can advise you to read the book Dependency Injection in .NET by Mark Seemann; it will give many answers and will give you a good sense how and when to apply it, and when not.

By : Steven


Was there a reason why you didn't choose to use an IOC Library (StructureMap, Ninject, Autofac, etc)? Using any of these would have made your life much easier.

Although David L has already made an excellent set of commentaries on your points, I'll add my own as well.

Much bigger codesize

I am not sure how you ended up with a larger codebase; the typical setup for an IOC library is pretty small, and since you are defining your invariants (dependencies) in the class constructors, you are also removing some code (i.e. the "new xyz()" stuff) that you don't need any more.

Ravioli-code instead of spaghetti-code

I happen to quite like ravioli :)

Slower performance, need to initialize all dependencies in constructor even if the method I want to call has only one dependency

If you are doing this then you are not really using Dependency Injection at all. You should be receiving ready-made, fully loaded object graphs via the dependency arguments declared in the constructor parameters of the class itself - not creating them in the constructor! Most modern IOC libraries are ridiculously fast, and will never, ever be a performance problem. Here's a good video that proves the point.

Harder to understand when no IDE is used

That's true, but it also means you can take the opportunity to think in terms of abstractions. So for example, you can look at a piece of code

public class Something
{
    readonly IFrobber _frobber;
    public Something(IFrobber frobber)
    {
        _frobber=frobber;
    }

    public void LetsFrobSomething(Thing theThing)
    {
        _frobber.Frob(theThing)
    }
}

When you are looking at this code and trying to figure out if it works, or if it is the root cause of a problem, you can ignore the actual IFrobber implementation; it just represents the abstract capability to Frob something, and you don't need to mentally carry along how any particular Frobber might do its work. you can focus on making sure that this class does what it's supposed to - namely, delegating some work to a Frobber of some kind.

Note also that you don't even need to use interfaces here; you can go ahead and inject concrete implementations as well. However that tends to violate the Dependency Inversion principle (which is only tangenitally related to the DI we are talking about here) because it forces the class to depend on a concretion as opposed to an abstraction.

Some errors are pushed to run-time

No more or less than they would be with manually constructing graphs in the constructor;

Adding additional dependency (DI framework itself)

That is also true, but most IOC libraries are pretty small and unobtrusive, and at some point you have to decide if the tradeoff of having a slightly larger production artifact is worth it (it really is)

New staff have to learn DI first in order to work with it

That isn't really any different than would be the case with any new technology :) Learning to use an IOC library tends to open the mind to other possibilities like TDD, the SOLID principles and so forth, which is never a bad thing!

A lot of boilerplate code, which is bad for creative people (for example copy instances from constructor to properties...)

I don't understand this one, how you might end up with much boilerplate code; I wouldn't count storing the given dependencies in private readonly members as boilerplate worth talking about - bearing in mind that if you have more than 3 or 4 dependencies per class you are likely to be in violation of the SRP and should rethink your design.

Finally if you are not convinced by any of the arguments put forth here, I would still recommend you read Mark Seeman's "Dependency Injection in .Net". (or indeed anything else he has to say on DI which you can find on his blog). I promise you will learn some useful things and I can tell you, it changed the way I write software for the better.



The majority of your concerns seem to boil down to either misuse or misunderstanding.

  • much bigger codesize

    This is usually a result of properly respecting both the Single Responsibility Principle and the Interface Segregation Principle. Is it drastically bigger? I suspect not as large as you claim. However, what it is doing is most likely boiling down classes to specific functionality, rather than having "catch-all" classes that do anything and everything. In most cases this is a sign of healthy separation of concerns, not an issue.

  • ravioli-code instead of spaghetti-code

    Once again, this is most likely causing you to think in stacks instead of hard-to-see dependencies. I think this is a great benefit since it leads to proper abstraction and encapsulation.

  • slower performance Just use a fast container. My favorites are SimpleInjector and LightInject.

  • need to initialize all dependencies in constructor even if the method I want to call has only one dependency

    Once again, this is a sign that you are violating the Single Responsibility Principle. This is a good thing because it is forcing you to logically think through your architecture rather than adding willy-nilly.

  • harder to understand when no IDE is used some errors are pushed to run-time

    If you are STILL not using an IDE, shame on you. There's no good argument for it with modern machines. In addition, some containers (SimpleInjector) will validate on first run if you so choose. You can easily detect this with a simple unit test.

  • adding additional dependency (DI framework itself)

    You have to pick and choose your battles. If the cost of learning a new framework is less than the cost of maintaining spaghetti code (and I suspect it will be), then the cost is justified.

  • new staff have to learn DI first in order to work with it

    If we shy away from new patterns, we never grow. I think of this as an opportunity to enrich and grow your team, not a way to hurt them. In addition, the tradeoff is learning the spaghetti code which might be far more difficult than picking up an industry-wide pattern.

  • a lot of boilerplate code which is bad for creative people (for example copy instances from constructor to properties...)

    This is plain wrong. Mandatory dependencies should always be passed in via the constructor. Only optional dependencies should be set via properties, and that should only be done in very specific circumstances since oftentimes it is violating the Single Responsibility Principle.

  • We do not test the entire codebase, but only certain methods and use real database. So, should Dependency Injection be avoided when no mocking is required for testing?

    I think this might be the biggest misconception of all. Dependency Injection isn't JUST for making testing easier. It is so you can glance at the signature of a class constructor and IMMEDIATELY know what is required to make that class tick. This is impossible with static classes since classes can call both up and down the stack whenever they like without rhyme or reason. Your goal should be to add consistency, clarity, and distinction to your code. This is the single biggest reason to use DI and it is why I highly recommend you revisit it.

By : David L


This video can help you solving your question :)
By: admin