What is Dependency Inversion? Is it IoC?
It’s not completely important that we understand the specifics of each of these names, because many people end up using them interchangeably. It’s pretty unlikely that we are going to correct all that in this blog post.
What is important is that we understand the concepts and ideas behind each of these topics and understand what architectural problems each one is trying to solve.
Today, we are going to focus on Dependency Inversion and get a little bit into inversion of control.
Dependency inversion is the root
It all started with this simple concept introduced by Uncle Bob in his article in the C++ Report of May 1996, The Dependency Inversion Principle.
If you have the time to read through that article, do yourself a favor and do it. You don’t need to know much C++ to understand it, and it really sheds a light on the problem Bob was trying to solve.
Bob talks about this principle in his excellent book, “Agile Software Development, Principles, Patterns, and Practices, and Agile Principles, Patterns, and Practices in C#”
This principle is actually very simply stated:
- High-level modules should not depend on low-level modules. Both should depend on abstractions.
- Abstractions should not depend upon details. Details should depend upon abstractions.
I think this concept is easily misunderstood and misapplied, because the reason why we apply this principle is often neglected.
If you are familiar with IoC and Dependency Injection, you can see how both are based from this definition of Dependency Inversion.
What problem does dependency inversion solve?
Dependency inversion solves the problem of higher level modules being dependent on and coupled to the interfaces of lower level modules and their details.
Let me give you a real world example of a current problem that could be solved by dependency inversion.
Take a look around your house and count up all the devices that have batteries that must be charged somehow.
Things like:
- Digital camera
- Cell phone
- Camcorder (flip cam)
- Wireless headphones
- Game controllers
What do all of these things have in common? They don’t have a charging interface in common. Some use micro-usb, some use mini-usb, some use their own funky plug.
So as a result, you can’t just have one thing that charged all your devices. You have to have a different thing for each device. Your home’s “charging mobile devices module” is dependent on the device. If you change your cell phone, you need a new charger. If you upgrade your camera, you need a new charger.
The dependency is going the wrong way. Lower-level appliances are defining the interface that your home has to use to charge them. Your home’s charging capability should define the interface the devices have to use. The dependency should be inverted. It would make your life a lot easier.
Let’s look at one more example of a place where I would bet dependency inversion is used. Now, I don’t know about Walmart’s IT structure, but I would venture to guess that when they receive invoices from all of their many distributors the invoices come in the file format that Walmart specifies and not the other way around.
I would bet that Walmart specifies the schema for all of the data it receives from its many business partners. Let’s assume they do. In this case they have inverted the dependency, actually they have inverted the control. Instead of their vendors controlling their interface, Walmart controls the vendors through their interface.
What this means is that every time the vendor changes their internal system they have to still conform to Walmarts interface instead of Walmart having to make changes to accommodate each vendors changes to their format.
Back to your code…
Now let’s look at a code example to see how dependency inversion helps us out. Let’s say you are creating a high level module for parsing log files and storing some basic information into a database.
In this case you want to be able to handle several different log files from a number of different sources and write some common data they all share to a database.
One approach to this kind of problem is to have your module handle each kind of log file based on what kind of data and format it contains and where it is. Using this approach, in your module you would handle various kinds of log files based on the interface those individual log files present to you. (When I use interface here, I am not talking about the language construct, but the concept of how we interface with something.)
Using this approach, in our module we might have a switch statement or series of if-else statements that lead us to a different code path depending on what kind of log file we are processing. For one log file we might open up a file on disk, and read a line, then split that line based on some delimiter. For another perhaps we open a database connection and read some rows.
The problem is the log files are defining the interface our higher level code has to use. They are in effect “in control” of our code, because they are dictating the behavior our code must conform to.
We can invert this control, and invert the dependencies by specifying an interface that the log files we process must conform to. We don’t even have to use a language level interface.
We could simply create a data class called LogFile that is the input to our module. Anyone who wanted to use our module would first have to convert their files to our format.
We could also create an ILogFileSource interface that classes could implement to contain the logic of parsing log files from different sources. Our module would depend on ILogFileSource and specify what kind of methods and data it needs to parse the log files instead of the other way around.
The key point here is that our high level module should be controlling the interface (non language construct kind) that the lower level modules need to adhere to instead of being at the whim of the interfaces of each lower level module.
One way to think of this is that lower level modules provide a service to higher level modules. The higher level modules specifies the interfaces for that service and the lower level module provides that service.


