You gain a few things by using IoC and DI:
1. Easier replacement of the underlying implementation:
What I mean by this is that you can change the implementation is one place and then safely assume that everything that's injecting your interface is using the new implementation instead.
Today you have
kernel.Bind<ITaxCalculator>().To<TaxCalculator>()
Tomorrow you may have
kernel.Bind<ITaxCalculator>().To<BetterTaxCalculator>()
Using IoC, you have to simply change this one line and everything else will inject the new implementation. Without it, you'd have to go all over your code and change all the classes that are using TaxCalculator
to BetterTaxCalculator
.
Now this is not always something you'd want to do of course but there may be times that you'd need to do this (specially if you're doing your injections via XML and you want to switch the strategies without changing the code).
2. MUCH easier handling of interdependencies.
Easier to explain with an example. Let's say today this is your class:
public class TaxCalculator
{
public TaxCalculator() { }
}
Without dependency injection, you create an instance like this:
var taxCalculator = new TaxCalculator();
With:
kernel.Get<ITaxCalculator>();
Tomorrow's implementation:
public class TaxCalculator
{
public TaxCalculator(IAdder adder, ISubstractor substractor) { }
}
Without dependency injection, you create an instance like this:
var taxCalculator = new TaxCalculator(new Adder(), new Substractor());
With:
kernel.Get<ITaxCalculator>(); // it's the same!!!! :-o
This may be hard to appreciate but in larger apps with many levels of hierarchy, this is a godsend! What this means is that users of my ITaxCalculator
do not need to know which interfaces are required to create an instance of a TaxCalculator
which means, if I decide to add a new IMultiplier
to my constructor in a week, I don't have to touch the code for the users of my API. The only thing they need to change is the part that they setup their DI (by injecting an IMultiplier).
You will most likely only have one place in your entire code that injects your interfaces but may have a lot of places that need an ITaxCalculator which would've needed changing if you weren't using a DIF.
3. Management of the lifecycle of your objects.
With most DI frameworks, it's very easy to define if you want your objects to be short lived, global singletons or, maximum n
instances or any other fancy behavior you want.
I'm sure there are more but these were the ones I could think of for now.