This c# based Dependency Inversion Principle tutorial will try to explain this very simple (and important) principle in layman terms. It is often confused with “Dependency Injection“. Just to put things straight, “Dependency Injection” is one of the ways you can implement “Dependency Inversion Principle” in your code.
As usual, first I will discuss the “WHY” before “HOW”.
WHY
Simple. To write simple easily maintainable, testable, extensible and flexible code.
“Dependency Inversion Principle” is the “D” in S.O.L.I.D principles which were put forward by Robert C. Martin aka Uncle Bob.
S -> Single Responsibility Principle
O -> Open / Closed Principle
L -> Liskov Substitution Principle
I -> Interface Segregation Principle
D -> Dependency Inversion Principle
Each of these topics merit a separate post.
Collectively these are considered as the foundation of Object Oriented Programming. Following them leads to software which is easy to maintain and extend if needed.
My advice to budding programmers is to master these 5 principles before touching design patterns.
These are the fundamentals. We can’t ignore them.
HOW
This is what Uncle Bob has to say on Dependency Inversion Principle:
1- High-level modules should not depend on low-level modules. Both should depend on abstractions.
2- Abstractions should not depend upon details. Details should depend upon abstractions.
This is such an technical (awful?) way to say such a profound thing. As a newbie programmer all I could say was “What?”.
So as usual I will put it in caveman simple terms.
1 – High-level modules should not depend on low-level modules. Both should depend on abstractions.
One sentence at a time.
1.1 High-level modules should not depend on low-level modules.
Best way to understand software patterns is to map it to real world examples. So I will dig up my old Radio example. Again.
A radio needs battery. But in real world, have you seen radio’s specifying which “Brand” of battery to use? Nope. All it will say is that use AAA, AA or some other type of battery. But never the brand.
But still when we write code for a radio we end up with this:
public class Radio { public DuracellBattery Battery { get; set; } public Radio() { Battery = new DuracellBattery(); } public void Play() { Battery.Start(); //some code } }
And client code typically will be
Radio radio = new Radio();
If we talk in terms of modules, then Radio is our high level module. And Battery is low level module. And high level module is depending on the low level module here. An object of low level module is getting created in the constructor of the high level module. High level module (Radio) knows about the low level module (DuracellBattery). It is tied to low level module. Strong coupling. Run away.
1.2 Both should depend on abstractions.
To remove the coupling we need to have abstractions. I will use interfaces here to create the abstraction (You can use abstract classes too). We will create an interface for the Battery. Lets call it IBattery.
public interface IBattery { void Start(); }
And then our DuracellBattery will implement the interface.
public class DuracellBattery: IBattery { void Start() { //Some code } }
So our low level module now depends on the interface. It is tied to implement what the interface dictates.
What about our high level module?
Code changes to
public class Radio { public IBattery Battery { get; set; } public Radio(IBattery battery) { Battery = battery; } public void Play() { Battery.Start(); //some code } }
See? All the references of DuracellBattery are gone now. No coupling. Party time.
Of course the client code will change from
Radio radio = new Radio();
to
Radio radio = new Radio(new DuracellBattery());
And in case you were wondering why the word “inversion” then this is the answer. See how the responsibility of creating the Battery object has been taken out of the Radio class and put into the client code. The role reversal. The inversion. You get the drift.
2 – Abstractions should not depend upon details. Details should depend upon abstractions.
Well we have abstraction in form of an interface. The question is : Who should define the abstraction?
In other words who should decide what the interface should contain? Should the Battery say that I know all about starting up, finding the remaining charge and size specs. So let me put Start(), ChargeRemaining() and GetSize() functions in the interface.
NO.
The boss here is the higher module. That is the Radio. And he says all I want is Start() and so the interface contains Start() only. Take that.
To put is bluntly, it is the high level module like Radio in our case which should define the interface. And low level modules like DuracellBattery should implement it.
There. Done.
In case you missed out I will like to repeat again. Key word is “Principle” in Dependency Inversion Principle.
P-R-I-N-C-I-P-L-E. It is a principle. A guideline. And there are many ways to implement this. And again, “Dependency Injection” happens to be one of them. Other is service locater pattern. Here in this example I used the dependency injection to illustrate the Dependency Inversion Principle.
Nice Post, simple, clean and very descriptive.
Thanks for this one Buddy.
nice article. very simple
Thanks for the article.