If you look at your three definitions, the distinction is subtle, but they essentially mean the same thing.
What it all amounts to is providing what the class needs (its dependencies) through parameters in it's constructor. That's all. There are numerous Dependency Injection frameworks out there that seek to formalize this process, but they all amount to the same thing.
Dependency Injection always seeks to provide only those dependencies that are needed, when they are needed.
I have experience with Ninject, not Castle Windsor, but the principles should be the same.
To understand dependency injection, it helps to recall how you wrote code without it. Typically, the DepartmentsController
constructor would look like something like this:
public DepartmentsController()
{
_repository = new DepartmentRepository();
}
The controller depends on the repository, and by placing it in the constructor we ensure that every DepartmentsController
is instantiated with a repository and begins its life in a valid state. By doing this, however, we make it more difficult to later swap one implementation of DepartmentRepository
for another. In particular, if we want to isolate the DepartmentsController
and test it independently, we're in trouble. How do we test this class without invoking actual calls to the database through DepartmentRepository
?
Your first example improves on this scenario by creating a "seam" between the objects: a point where the two are glued or stitched together that can be separated easily, especially for tests. Instead of creating the DepartmentRepository
itself, your controller can ask for it:
public object GetService(Type serviceType)
{
return _container.Kernel.HasComponent(serviceType) ? _container.Resolve(serviceType) : null;
}
This is called the Service Locator pattern, and some consider it bad practice. By exposing Castle Windsor in this way, you have actually lost some of its benefits. In particular, your DepartmentsController
now depends on the Service Locator and its GetService
method. I won't cover all of the supposed disadvantages of Service Locators, but consider what happens when your application grows and you now have numerous classes (potentially all of them) dependent upon this single class.
Your third example improves upon the Service Locator pattern by making the container invisible to your class:
public DepartmentsController(IDepartmentRepository repository)
{
if (repository == null)
throw new ArgumentNullException("repository");
_repository = repository;
}
Notice how this version has no knowledge of the kernel / container or a service locator. Instead, we have made its true dependencies explicit in the constructor rather than the means to those dependencies. In fact, by writing your classes this way, you can construct entire class libraries that are ignorant of Castle Windsor and will work perfectly fine with Ninject, another DI framework, or no container at all, without modifying the code.
How you actually register your components (the difference between examples two and three) is partly a matter of scale.
container.Register(
Component.For<IDeliveryItemRepository>().ImplementedBy<DeliveryItemRepository>().LifestylePerWebRequest(),
Component.For<IDeliveryRepository>().ImplementedBy<DeliveryRepository>().LifestylePerWebRequest(),
Component.For<IDepartmentRepository>().ImplementedBy<DepartmentRepository>().LifestylePerWebRequest(),
...
is fine for a smaller application. If the number of classes grows, you have to stomach a never ending list of bindings or find a better method. One improvement is to favor convention over configuration: if Castle Windor assumes every IDeliverItemRepository
has a DeliveryItemRepository
implementation, the bindings above can be skipped. However, you haven't solved anything if most of your classes require exceptional configuration; you still have a long, omniscient kernel / container registration method.
Instead, you should probably explore something like your second example. This way, your ConfigureWindsor
method can use reflection to find any IWindsorInstaller
s present in your application without knowing about specific interfaces or implementations. You can break all of your bindings into separate modules (Ninject's name for this concept) that are discovered at runtime.
To address your last question: Castle Windsor won't know which of your N
implementations to choose without you telling it. You can accomplish this through a mixture of the concepts discussed above: primarily by convention, but with IWindsorInstaller
s when needed to specify important details like lifecycle and conditional bindings.
Best Answer
If you're making a UI for the GPS data, it will invariably have a dependency on GPS data of some sort. Having that coupling isn't bad - quite the opposite. By having your user control depend on an explicit interface, you're letting the compiler be able to tell you when it's missing and you're telling your users what it needs to work.
By making it an interface though, you're effectively decoupling your control from the implementation of the GPS data provider allow it to change freely. How the dependency gets supplied is up to the user, who could even use their own data source if they wanted to. No need for accursed IoC containers if they're not warranted.