Reputation: 21430
I have seen this question Still need help understanding why Ninject might be better than manual DI but I am still confused by Ninject's usefulness...
I understand that this code...
class Samurai
{
readonly IWeapon weapon;
[Inject]
public Samurai(IWeapon weapon)
{
this.weapon = weapon;
}
public void Attack(string target)
{
this.weapon.Hit(target);
}
}
... Will produce the following "dynamic method that (basically) looks like this:"
delegate(IWeapon weapon)
{
return new Samurai(weapon);
}
How is this useful at all? Without Ninject, I can still do this (as per the Ninject docs "Dependency Injection By Hand" - No Ninject):
class Program
{
public static void Main()
{
var warrior1 = new Samurai(new Shuriken());
var warrior2 = new Samurai(new Sword());
warrior1.Attack("the evildoers");
warrior2.Attack("the evildoers");
}
}
What does using Ninject provide for me that I can't do by just following basic principals of loose coupling? Thanks for helping me understand.
Upvotes: 4
Views: 1786
Reputation: 4262
Darin's answer covers the most significant benefits of using a DI framework, but here's something that I found helpful when using a DI container. Suppose you have an object with these service dependencies:
public class Order : IOrder
{
public Order(IOrderService orderSvc, ICustomerService custSvc)
{
// constructor logic
}
}
Your code could look something like this:
var order = new Order(new OrderService(), new CustomerService()); // manual
or
var order = kernel.Resolve<IOrder>(); // using a DI container
This is working fine and well. Now, all of a sudden, your requirements have changed. Order now needs a shipping service:
public Order(IOrderService orderSvc,
ICustomerService custSvc, IShippingService svc)
{
// constructor logic
}
Imagine you have 10 different places throughout your program where you manually create an Order. If you were handling the injection yourself, you have to find all 10 of those locations in code where you are creating an order and modify them. Doing this 10 times could be a lot of work!
var order = new Order(new OrderService(), new CustomerService(), new ShippingService());
However, with a DI container, once you register a binding, your code in all those 10 places looks like this:
var order = kernel.Resolve<IOrder>();
See how it is the same as before? You didn't have to change anything! All those 10 places where you resolve an order won't change** whether you add 1 dependency or 100. So it can help keep you from having to modify existing code as new dependencies are needed.
** This won't always be the case, but this is a simplified example to show one of the additional benefits that nobody talks much about.
Upvotes: 3
Reputation: 1038850
What does using Ninject provide for me that I can't do by just following basic principals of loose coupling?
Composition Root
All things that you should be handling manually otherwise. This being said, the DI framework is of little importance. It should be fast and offer the specific features you need for your application. But the DI framework should absolutely in no way influence the way you are designing your code and the different layers in your application in a loosely coupled manner (programming against interfaces and abstract classes to weaken the coupling between the different layers of your application).
So think of the DI framework as something that intervenes only in the Composition Root of your application and as a framework that you could replace in a blink of an eye with a different framework or even manually handling your object lifetimes.
For example the code you have shown in your question is very bad as it ties your layers to a specific DI framework. This [Inject]
attribute over there is like a cancer. It means that your application code relies on a specific DI framework.
Upvotes: 1
Reputation: 351
So what if you want to test your program? With your manual method, the test environment is now dependent on Shuriken() and Sword() being defined.
With a framework like Ninject you can create your application such that the dependencies are introduced when you actually run the application. This means you can pass in mocked data so now your test environment will have no dependencies.
Upvotes: 0