I think I have a decent grasp of what Dependency Inversion principle (DIP) is, my confusion is more around dependency injection.
My understanding is the whole point of DI is to decouple parts of an application, to allow changes in one part without effecting another, assuming the interface does not change.
For examples sake, we have this
public class MyClass(IMyInterface interface)
{
public MyClass
{
interface.DoSomething();
}
}
public interface IMyInterface
{
void DoSomething();
}
How is this
var iocContainer = new UnityContainer();
iocContainer.Resolve<MyClass>();
better practice than doing this
//if multiple implementations are possible, could use a factory here.
IMyInterface interface = new InterfaceImplementation();
var myClass = new MyClass(interface);
It may be I am missing a very important point, but I am failing to see what is gained. I am aware that using an IOC container I can easily handle an objects life cycle, which is a +1 but I don’t think that is core to what IOC is about.
Edit
Here is an expanded example:
void Main()
{
IRepository repository = new AddRepository();
var maths = new PointlessMaths(repository,5);
Console.WriteLine(maths.Result);
}
public interface IRepository
{
int DoSomething(int id);
}
public class AddRepository : IRepository
{
public int DoSomething(int id)
{
return id + 1;
}
}
public class SubtractRepository : IRepository
{
public int DoSomething(int id)
{
return id - 1;
}
}
public class PointlessMaths
{
public int Result {get;set;}
public PointlessMaths(IRepository repository, int id)
{
Result = repository.DoSomething(id);
}
}
9
In your example, you haven’t gained anything. Your intuition is correct. If you have only one class implementing an interface, there is no gain, because it may always stay that way. There is no limit to the degree to which we can add interfaces and clutter the code.
3
In this example, I would not call using a DI container “better practice”. However, code bases tend to grow and become more complicated. Imagine we have a dependency graph like this:
A
/
/
B C
/
/
D
The code to construct this graph without a DI container would look something like this:
ID d = new D();
IB b = new B(d);
IC c = new C(d);
A a = new A(b, c);
Note that the same instance of D is used for for B and C. But what if we decide that they each need their own instance? Now we have to duplicate the code to create a new instance of D, and the result looks like this:
ID d1 = new D();
IB b = new B(d1);
ID d2 = new D();
IC c = new C(d2);
A a = new A(b, c);
Nontrivial applications can easily have far more complicated dependency graphs than this, and the code’s complexity will scale accordingly. Contrast the above with the equivalent code using a DI container:
var iocContainer = new UnityContainer();
iocContainer.Resolve<A>();
Note that the complexity does not scale with the number of dependencies. Even when you consider the container’s configuration, that only scales with the number of unique dependencies (e.g. you’ll only need one entry for D even if it ends up creating a hundred instances). In addition, some dependencies (like concrete classes) can be automatically resolved without any configuration at all.
DI containers also provide the flexibility to specify, in configuration, whether or not you want D to be a singleton (for example), in addition to plenty of other resolution, construction, and lifecycle options. These may also be more readable using your container’s configuration syntax than implementing them yourself.
The more complex your dependency graph becomes, the more benefit you’ll get from a DI container.
Also, popular DI containers often integrate well with particular platforms (e.g. ASP.NET MVC, WCF, etc.).
4
Passing in an implementation of the interface should be preferred. The real benefits come with more complex examples. What happens if the creation is determined by runtime behavior? Passing in a factory rather than the instance solves that, but can get unweildy. And what if the creation timeframe was not clear cut (the consuming class could not just call the factory, but would have to wait until you set everything up).
Further, what happens when the class you’re instantiating isn’t the concrete version either? If all you need is a IFoo
some factory is providing the implementation. Some concrete implementations need some dependencies injected and others need others (or none).
IoC containers are a giant hammer. Usually just passing in the concrete implementation of an interface is clear, functional, and flexible. But sometimes the design grows complex enough that you need IoC containers to manage the tangle of dependencies spread throughout your object graph.
1
Using DI container as a service locator is in fact an anti-pattern, it is widely discussed. Sometimes you have to do this but generally speaking you should avoid this. Basically by calling container.Resolve you are not doing any IoC and IoC is what DI containers intent to do.
What we really use DI containers for is to inject dependencies when needed using custructor parameters or property injection. You register your classes that implement your interfaces well in advance, when your application initialises/starts and there you specify all the details – simple initialization, using parameters, using factories and so on, specifying the life scope, etc, etc. When all this is done you don’t really need to care as long as your constructors have parameters, where your registered classes can be injected to. So when you do this
You of course have to have the bootstrapper that resolves your main application class but since this class would already have injected parameters, all the rest can go automatically. And if you need ISession or ICustomerRepository or IWhatEverElseYouNeed, you will get it without worrying to instantiate it every time you need it.
The question is, where is the knowledge about which concrete implementation of a dependency is needed, and the setup (e.g. configuration or other dependencies) this dependency needs is best kept. This may or may not be in the dependant object. As always, the answer depents. But consider a complex object which needs other dependencies and maybe configuration data like urls or passwords. This dependency may also be the dependency of other objects. In this case, the knowledge of the setup should clearly not be located in the dependant objects, because it would be redundant and/or clutter class relationships and thus create more overall complexity than with a DI-Container, which keeps this knowledge in one place.
You could of course implement a factory for this case. But what if your application has lots of complex depenendcies, which all need some kind of factory. Now you have the problem of factories floating around, all containting part of the knowledge how the dependencies of the application are interconnected. Using a container in this case, adds much to the clarity and transparancy of the system. DI-Containers at this level are often used as a factory of factories.
The more complex the application gets, the more it is useful to have a central place interconnecting the parts.