I've recently come upon a lot of content on the web in regards to the inversion of control pattern. One of the best articles in this regard is Martin Fowler's Inversion of Control Containers and the Dependency Injection pattern article. After reading the article and playing around with a couple of the frameworks (see Castle Project & Spring.Net) available that aim to ease the use of an Inversion of Control Container a thought popped into my head: Isn't Inversion of Control just a fancy name for the "abstract factory" or "strategy" pattern thoroughly explained by Gamma in his famous design pattern book? Being a pattern fanatic I had to explore a bit more and this post is basically what I have surmised on this interesting subject.
To start I want to make one thing very clear: the phrase "Inversion of Control" is being overused/misused (like so many other phrases) and Martin Fowler's distinction on what is Inversion of Control (the general definition) and what he calls Dependency Injection, which is what we would be discussing, is very clear and to clarify the issue please read the section titled "Inversion of Control" in his article.
In order for us to start comparing dependency injection with other patterns let's get a clear definition of dependency injection first. My explanation here will be brief in order to provide a general overview and the major points in relation to dependency injection for a thorough definition either read Martin Fowler's article or just do a little search on "Inversion of Control" you can't miss the vast array of detailed explanations that are out there! J
One major problem that causes coupling and headaches in software design is when a designer creates a class A and then his/her class needs to use class B in one way or the other and he/she creates an instance of class B directly. For example:
class A {
B myB = new B();
void f() {
//do something with myB
}
}
class B {
//implementation of class B
}
The problem with the above design is that class A is dependent on class B in such a way that if for any reason (testing, future expansion, etc.) we need to replace B's implementation with something else (something that supports the same interface, but has a different implementation) we need to rewrite code. Dependency injection is the simple concept of creating class A in such a way so that we can later on "inject" its dependency on the "right" class not a fixed implementation. In other words what if we could create class A that would use an instance of B but the actual implementation of that was instantiated would be left for later on. So A could use B1 or B2 or any other class that implemented the B interface. Sounds too abstract? Let's talk about concrete examples:
Suppose we are going to use ADO.net to access a SQL server database and you write this code in your DAL layer components:
SqlDataAdapter a = new SqlDataAdapter(…);
//other lines of code that use the data adapter
The problem with the above code is that we have made ourselves dependent on the Sql implementation of ADO.net and in the future when our program needs to do the same exact thing with the Oracle classes we have to rewrite code. Now some people might be jumping up and down at this point screaming "use OleDbDataAdapter or use OdbcDataAdapter since they are vendor neutral ways of accessing data." Yes we could do that but that misses the whole point of this exercise: we are trying to create a DAL component that can be injected with the right dependency to use either Sql or Oracle or some other connection mechanism, we don't want to use a general method such as OleDbDataAdapter since it is too general and we will lose some of the features of native connectivity (i.e. performance). Anyhow let's not get bogged down on the specifics of the example and just assume that in developing the above mentioned DAL layer components we want our components to be independent of any specific implementation and allow us to inject (I love using this term!) them with the right implementation.
OK first things first: we need to make sure our DAL components try to use something a little more abstract than just SqlDataAdapter. Obviously this is necessary to make sure that we can later on replace the SQL based implementation with something else. Fortunately the ADO.net designers had designed some general interfaces for the most common ADO.net classes and in this case we have the IDataAdapter interface. So my DAL layer code would look like this:
class DAL
{
public IDataAdapter Adapter;
public void Save(/* … */)
{
//use the Adapter variable to do the saving
}
//other functions with similar implementations
}
The issue here is how to initialize the Adapter member variable so that it can be used by the DAL class. Here is where dependency injection (or Inversion of Control or IoC) comes into play.
The dependency injection pattern states that an outside component sometimes known as the Inversion of Control Container will take care of making sure the Adapter field (in this example) is filled with the right object (implementation) before it's used. How it does that is based on three variations of the pattern:
Type 1 IoC (or what Martin Fowler calls interface injection): you make sure that the DAL class implements an interface that has an "inject" method for assigning the Adapter property. Then during the application configuration or a similar phase based on some configuration we would inject into the DAL class the "right" implementation of the IDataAdapter that we need. For example if the config file says SQL Server based then our configuration code would create a SqlDataAdapter and inject into the DAL using the interface:
interface IAdapterInjectable {
void InjectAdapter(IDataAdapter a);
}
class DAL : IAdapterInjectable {
public void InjectAdapter(IDataAdapter a) {
Adapter = a;
}
private IDataAdapter Adapter;
public void Save(/*…*/) {
//save using the Adapter
}
}
Type 2 IoC (or what Martin Fowler calls setter injection): your DAL class implementation has to have a property setter that the configuration code will use to "inject" (by setting the value of the property) the right adapter into the class:
class DAL {
public IDataAdapter Adapter {
get {
return _Adapter;
}
set {
_Adapter = value;
}
}
private IDataAdapter _Adapter;
public void Save(/*…*/) {
//save using the Adapter
}
}
Type 3 IoC (or what Martin Fowler calls constructor injection): I guess this would be pretty obvious. In this method the constructor of our DAL accepts an IDataAdapter and when it's created the code responsible for its creation has to pass the right adapter implementation to it.
In all three cases the dependency injection has happened by somehow assigning the right implementation to the class that is dependent of an abstract interface and it has been assumed that we can take care of this assignment in our "configuration" section very easily. The reality is that configuring these classes with the right injection can sometimes be hard and in other cases is not worth the effort so like any other design pattern IoC should only be used when it is necessary and the extensibility it provides is worthwhile. The good thing about readymade IoC frameworks such as Spring.net or Windsor class of the Castle Project is the fact that they take care of the configuration section and also provide that configuration based on an XML setting. So all you have to do is create your classes, make them dependent on an abstract interface and allow the framework to fill in the blanks (or inject the dependencies) based on some XML configuration. For a very good example of this please see this article in MSDN.
The Big Question:
Now let's get back to the issue of this post: Is the IoC or dependency injection pattern just another fancy name for already existent patterns? Let's see what we can compare the IoC to or better yet if we didn't know about IoC how would we go about designing to cover for the above problem?
Before being exposed to Inversion of Control, whenever I had to deal with flexibility requirements such as the above I would either design my classes using Abstract Factory or the Strategy pattern. Each situation had different details therefore I would pick between the two patterns based on the requirements but they where my main choices. So for example if a design got to the point where my code needed to create a concrete implementation that might change in the future or might not be known until later on AND was fixed for the duration of the program execution I would go with the Abstract Factory pattern combined with a polymorphic singleton (see here for an explanation).
If in other scenarios I needed to "configure" my code to use different methods and the programmer/designer who was using that class had the right information to make the decision in regards to which implementation to use I would go about using the Strategy pattern. The Strategy pattern provides enough flexibility so users of my code could configure it (or plug into it) the right implementation. And finally the above 3 different types of IoC would be different ways to assign the strategy implementation to the context (my code).
So what's the big deal? If the strategy, abstract factory and many other "old" patterns could take care of that level of flexibility why do we need another fancy name, other than the fact that "injecting dependency" into your code just sounds too cool?
Well if you look deeper into what the IoC is trying to achieve first of all you realize that IoC is not just a fancy name but a totally different point of view toward this problem. Although the Abstract Factory and Strategy pattern each try to resolve the problem that IoC tackles in different ways both of them are general patterns and techniques for different (although partially overlapping) problems therefore the reader sometimes loses his/her focus on the problem. Dependency injection (or IoC) will give you a better more focused pattern in regards to what we are trying to achieve (decoupled components that would otherwise be tightly coupled due to the type of usage that exists between them).
Plus the articles, documents and frameworks surrounding IoC cover a lot more than a flexible way of encapsulating creation logic. They also provide us with a very strong and flexible base set of classes and readymade components that take care of a lot of the detailed implementation issues in regards to setting up the environment necessary for achieving true configurable component independence. Therefore a lot more reuse and stability is achieved when these frameworks and the IoC concepts in general are used as opposed to designing using Abstract Factory or Strategy from scratch.
Finally this pattern, like so many other patterns, techniques and concepts, is built on top of pre-existing ideas and implementations and there is nothing wrong with that. And not only is it built on top of these existing ideas it also provides a very good encapsulation and focus on the problem and its solution as compared to the more general Abstract Factory or Strategy pattern.