0

I need help (preferably by way of a practical example) to understand why/if the following implementation of IoC/DI (in this case using Ninject) provides an architectural advantage:

using (IKernel kernel = new StandardKernel())
{
    kernel.Bind<ITaxCalculator>()
          .To<TaxCalculator>()
          .WithConstructorArgument("rate", .2M);

    var tc = kernel.Get<ITaxCalculator>();
    Assert.Equal(20M, tc.CalculateTax(100M));
}

As far as I can see the pattern doesn't really ensure greater probability of re-use of the interface by using the interface as a DI. We could just as easily achieve a guarantee of interface re-use by plain inheriting the interface within a 'TaxCalculator' class, and we would then have a more simple and readable way of calling a 'CalculateTax method'.

I'm not interested in the unit or integration test benefits of the pattern right now, just a tangible example or even abstract justification of how the above pattern can help to achieve any benefit (for example loose coupling) in a way that can't be achieved without using DI.

Chris Halcrow
  • 401
  • 2
  • 6
  • 14
  • 5
    It is not Dependency injection that makes re-use higher. It is act of creating proper abstractions. And DI is unrelated to that. And creating proper abstractions is magic onto itself. – Euphoric Feb 03 '15 at 07:35
  • *Dependency Inversion* achieves looser coupling between objects, which can facilitate code reuse if the code is, in fact, reusable. DI containers are one mechanism by which inversion can take place, but there are others (handing dependencies to a constructor method, for example). See also http://programmers.stackexchange.com/a/205685 – Robert Harvey Feb 03 '15 at 07:55
  • 1
    I think you are talking about IoC rather than DI. IoC does not have many advantages, it's simply a preference. When you invert the control you are losing it, some people (most) don't mind. – Den Feb 03 '15 at 09:15
  • 2
    Your example isn't even using dependency injection. It may be using a DI framework, but it isn't using the part of it that performs dependency injection. It's just using it as a glorified programmable Factory, in a situation where the Factory pattern isn't even useful. Try it in a real, non-trivial application that evolves over a long period of time, and then you may see some advantages. – Jules Feb 03 '15 at 11:14
  • Jules +1 for pointing out that my example implements constitutes a factory pattern, although I've seen the passing of an interface in this way referred to as DI, as the interface is essentially a dependency of the contracted objects. – Chris Halcrow Feb 03 '15 at 21:39

1 Answers1

4

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.

kha
  • 156
  • 1