As mentioned in my post on AOP, I've been learning more about Inversion of Control (IoC) and Dependencey Injection (DI). I'll explain my take on these a bit more in this post.
IOC and DI have been gaining ground lately in a number of areas. Microsoft built the widely accepted Enterprise Library and the Composite UI Application block on top of an IoC library that they call Object Builder. They chose this design because it gives them a very powerful way to manage a large number of dependencies in these highly-integrated frameworks.
For example, in Enterprise Library, the Exception block uses the services of the Logging block, which in turn uses the services of the configuration block to get it's settings. All of these blocks implement provider patterns so that users can provide their own custom implemetnations for a specific piece of the functionality.
The designers of the framework wanted an easy way to "stitch" these things together without having hard-coded dependencies beteen them. Otherwise, how would they be able to pull in a custom implementation that was written after Enterprise Library shipped.
So, they used some design patterns to help solve the problem. They could have used the Abstract Factory Pattern to solve the problem. However, they chose to use Dependency Injection, which takes this idea to the next level and reverses the typical approach to the situation.
Dependency Injection is one way to implement Inversion of Control. IoC reverses the way that objects are stitched together. The classic way is for a class to instantiate other classes that it needs to use. IoC says NO!
IoC says that the "container" should instantiate and provide the dependencies. With the DI approach, the container uses configuration and/or attributes to instantiate dependent classes. Then the IoC container sets a property on the object to "inject" the dependency into the object.
Well, it turns out that this is useful in a number of scenarios:
If I can use configuration to inject a data access stub into the application during development, then I have made it much easier to test my business logic. You can do this without DI, but your code has to know that it is going to be stubbed and then figure out which implementation to load. With DI, my code doesn't care whether it's stubbed or not. I just write the logic to get the job done.
Insulation from Change:
If I am config driving the implementation that is injected into an object, then it is simpler for me to provide alternate implementations as requirements or the environment changes.
Loose Coupling:
If my object uses the "New" keyword to create a class, then the two items are tightly coupled. This can be a good thing, but it make the system rigid. If I design around a goal of loosely coupled items, then I'll probably end up organizing my functionality into more logical units. If each unit is well organized, then my system becomes simpler. It's easier to understand and easier to maintain.
Why Not?
DI and IoC require developers to re-learn a very core habit of using the New keyword for everything. They have to understand how to create their code in a way that it works in an IoC container. This is not trivial.
Ideally, the development staff would not have to undersand how to design an IoC implementation. Rather they would have a framework, tooling and guidance in place to assist them. This framework would show them what code they should write, versus what the framework will provide.
I'm still trying to figure out where DI and IoC should be used. They definately have a home when creating complex frameworks like CAB or Enterprise Library. I'm not sure how far this bubbles up to the typical project.
The main players in the DI an IoC space are:
More to come soon.