Most important, for me, is making it easy to follow the Single Responsibility Principle.
DI/IoC makes it simple for me to manage dependencies between objects. In turn, that makes it easier for me to break coherent functionality off into it's own contract (interface). As a result, my code has been far more modularized since I learned of DI/IoC.
Another result of this is that I can much more easily see my way through to a design that supports the Open-Closed Principle. This is one of the most confidence inspiring techniques (second only to automated testing). I doubt I could espouse the virtues of Open-Closed Principle enough.
DI/IoC is one of the few things in my programming career that has been a "game changer." There is a huge gap in quality between code I wrote before & after learning DI/IoC. Let me emphasize that some more. HUGE improvement in code quality.
Constructor Injection has the advantage that it makes the dependency explicit and forces the client to provide an instance. It can also guarantee that the client cannot change the instance later. One (possible) downside is that you have to add a parameter to your constructor.
Setter Injection has the advantage that it doesn't require adding a parameter to the constructor. It also doesn't require the client to set the instance. This is useful for optional dependencies. This may also be useful if you want the class to create, for example, a real data repository by default, and then in a test you can use the setter to replace it with a testing instance.
Interface Injection, as far as I can tell, is not much different than setter injection. In both cases you are (optionally) setting a dependency that can be changed later.
Ultimately it is a matter of preference and whether or not a dependency is required. Personally, I use constructor injection almost exclusively. I like that it makes the dependencies of a class explicit by forcing the client to provide an instance in the constructor. I also like that the client cannot change the instance after the fact.
Often times, my only reason for passing in two separate implementations is for testing. In production, I may pass in a DataRepository
, but in testing, I would pass in a FakeDataRepository
. In this case I'll usually provide two constructors: one with no parameters, and another that accepts a IDataRepository
. Then, in the constructor with no parameters, I will chain a call to the second constructor and pass in a new DataRepository()
.
Here's an example in C#:
public class Foo
{
private readonly IDataRepository dataRepository;
public Foo() : this(new DataRepository())
{
}
public Foo(IDataRespository dataRepository)
{
this.dataRepository = dataRepository;
}
}
This is known as Poor Man's Dependency Injection. I like it because in production client code, I don't need to repeat myself by having several repeated statements that look like
var foo = new Foo(new DataRepository());
However, I can still pass in an alternate implementation for testing. I realize that with Poor Man's DI I'm hardcoding my dependency, but that's acceptable for me since I mostly use DI for testing.
Best Answer
Inversion of Control is still a concept that applies well. You don't need to be doing dependency injection to apply IoC, although in .NET they do tend to go together often.
The big drive behind dependency injection in .NET is avoiding depending on concrete implementations. In Ruby, as you mentioned, things are much more open and you can replace implementation of a class at run time, so there's much less need to create "interfaces" and explicitly inject dependencies.
It can be argued that interfaces and IoC/DI aren't strictly necessary in .NET either (even when it comes to testing -- there are mocking frameworks that will allow you to eliminate the need to interface everything under the sun), but it's more pronounced in Ruby.