Dependency Inversion Principle... Say what???

Dependency Inversion Principle

noun [C] / dɪˈpen.dən.si ɪnˈvɜː.ʒən prɪn.sə.pəl /

  • "A principle in OO-programming which states that high-level modules should not depend directly on low-level modules. Instead, both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions."

Well, that clears it up... errm... Not really! I mean, I can read. I understand the words. I am fairly bright. I even think that I 'kinda' know what it means. But it is still kind of vague...
... And that is exactly what it should be.... Vague. That's what a principle is about. It's a guide line, a moral rule, a code of conduct, a fundamental source, a basis (I think you get the point). Nothing more, nothing less!

Principles are just like politicians, they say a lot of stuff but they never provide a real solution. For example, in my hometown the local authorities enforce a policy called "The polluter pays". Period. It doesn't say what pollution exactly is. It doesn't state how it should be paid. Should it be paid in money? And if so, how much? When are you considered a polluter? It doesn't really give an answer, it simply says that when you are going to live here... be aware, we tend to let you, the polluter, pay for the damage you cause to the environment!

Applying this to the Dependency Inversion Principle, it just says, when writing our code we should try to stick to the fact that high(er) level objects should not depend on concrete implementations of low(er) level objects. It doesn't say how, why or when. It even does not say that we have to invert "everything" in the code that we write from now on. It just means that, this is our way of thinking!

Dependency Injection

But, what about Dependency Injection? Is that the same? Is it also vague? Nope... Dependency Injection is one of the ways of implementing the Dependency Inversion Principle. Let's try an analogy. In my spare time I am a musician. So a music analogy it is! Here goes...

Let's say that you recently discovered a kick-ass rock band called The Licks & Grooves All-Stars (insert band name of your liking here) which plays all of your favorite funky tunes.

A band usually consists of:

  • a band leader (that guy who tells other musicians in the band what to play, when to play certain grooves, licks, solo's and all kinds of stuff.
  • other musicians that follow the instructions of the band leader, in order to produce awesome groovy tunes as a band

The band leader is our high-level object. He is the one who is responsible for songs being played. However, he cannot do it alone. He needs the other musicans, to produce some cool music. No musicians, no music. In other words our band leader depends on the other musicians. So the other musicians are our low-level objects in this case.

Because of the busy touring schedule of the band, each member has at least one stand-in/substitute musician. If a member cannot make it to a gig, the substitute musician will take over and the gig can still go on instead of being cancelled. Every musician has his own unique style of playing. Let's take the drummer (yes, a drummer is also a musician!!!) and his substitute colleague for example. They could be implemented like this:

Low-level objects

The Drummer...

public class DaveGrohl {
    public void beatTheCrapOutOfTheBassDrum(){
        System.out.println("BOOOOOM");
    }

    public void whackTheSnareDrum(){
        System.out.println("TAK!!");
    }
}

... and his substitute

public class CharlieWatts {
    public void gentlyHitTheBassDrum(){
        System.out.println("dum");
    }

    public void strikeTheSnareDrum(){
        System.out.println("chik");
    }
}

As you can see, they basically do the same thing... They both can play a bass drum and a snare drum. They just do it in their own unique way!!!

High-level object

For the sake of keeping things simple, The Licks & Grooves All-Stars band consists of a band leader and a drummer only. Assume that our band leader wants to play "We Will Rock You!" (A famous song from a band called Queen). The code might look something like this:

public class BandLeader {
    private String drummer;

    public BandLeader(String drummer){
        this.drummer = drummer;
    }

    public void playWeWillRockYou() throws NoSongPlayedException {
        if (drummer.equals("Dave")){
            DaveGrohl daveGrohl = new DaveGrohl();
            daveGrohl.beatTheCrapOutOfTheBassDrum();
            daveGrohl.beatTheCrapOutOfTheBassDrum();
            daveGrohl.whackTheSnareDrum();
        } else if (drummer.equals("Charlie")){
            CharlieWatts charlieWatts = new CharlieWatts();
            charlieWatts.gentlyHitTheBassDrum();
            charlieWatts.gentlyHitTheBassDrum();
            charlieWatts.strikeTheSnareDrum();
        } else {
            throw new NoSongPlayedException("No drummer available!!!");
        }
    }
}

If we want to play "We Will Rock You" with "Dave Grohl" on drums, this would be our client code:

public class GigApp {
    public static void main(String[] args) throws NoSongPlayedException {
        BandLeader bandLeader = new BandLeader("Dave");
        bandLeader.playWeWillRockYou();
    }
}

The console would show:
BOOOOOM
BOOOOOM
TAK!!

If we pass "Charlie" into the constructor of our Band Leader, the output would be:
dum
dum
chik

Excellent!!! Mission acomplished! Our band leader can handle two different drummers and still play the same song. And we didn't even use Dependency Injection!!!

It works, doesn't it?

Yes it works. But, There is only one problem though: Not only does the band leader has to know what to play (Two bass drum hits followed by a hit on the snare drum). He also needs to know how he has to address each drummer to make them play that sequence of notes. And on top of that, he also needs to know how to create/construct each drummer.

So he is essentially stuck to these two drummers. If we want to add more drummers to the band then we would be in really big trouble! Each new drummer added to the band means an extra else-if clause in the BandLeader.playWeWillRockYou method.

And to make things worse, what if the sequence of notes changes and the band leader wants to add an extra hit to the snare drum when playing We Will Rock You? Every if clause must be altered then!!! Maintenance hell will surely follow now, if the band and/or the song keeps changing...

To solve these problems, we are going to invert the control regarding object creation. You can invert lots a things as a programmer like the flow, interfaces and object creation (This is called Inversion of Control). Nowadays, the term Inversion of Control is considered the same as Dependency Injection. But in reality IoC is a little bit broader. Inversion of control with regard to object creation is called Dependency Injection! That sounds cool, so let's see how a little bit of Dependency Injection works out for the band:

High-level object (in the case of DI)

We want two things solved:

  • The band leader should only have knowledge of what.to play (Two bass drums followed by a snare drum). He should not be bothered with knowing how to address each drummer in order to let them play what he wants
  • We don't want the band leader to be responsible for creating/constructing each drummer. (See above: "He should not be bothered with knowing how to address each drummer..."

Looking at the requirements, the ideal implementation of our Band Leader is:

public class BandLeader {
    private Drummer drummer;

    public BandLeader(Drummer drummer){
        this.drummer = drummer;
    }

    public void playWeWillRockYou() throws NoSongPlayedException {
        if (drummer == null)
            throw new NoSongPlayedException("No drummer available!!!");

        drummer.playTheBassDrum();
        drummer.playTheBassDrum();
        drummer.playTheSnareDrum();
    }
}

What just happened here? We made an abstraction (a simplification) of Charlie and Dave. They are drummers, so we created a Drummer Interface.

public interface Drummer {
    void playTheBassDrum();
    void playTheSnareDrum(); 
}

The band leader depends on an interface now (which is good, in this case). He can now speak in one language to a drummer. The band leader can interact with any drummer, as long as the drummer object implements the Drummer interface.

Low-level object (in the case of DI)

public class DaveGrohl implements Drummer {
    @Override
    public void playTheBassDrum() {
        System.out.println("BOOOOOM");
    }

    @Override
    public void playTheSnareDrum() {
        System.out.println("TAK!!");
    }
}

... and

public class CharlieWatts implements Drummer{
    @Override
    public void playTheBassDrum() {
        System.out.println("dum");
    }

    @Override
    public void playTheSnareDrum() {
        System.out.println("chik");
    }
}

Our main client code looks quite similar. The only difference is that we now pass a "Drummer" into the Band leader instead of a String. And now for our band leader it doesn't matter how many drummers we add to the band. As long as a concrete implementation of a drummer knows how to behave like a drummer, everything is peachy.

Instead of Dave Grohl telling the band leader how he should be addressed. It is the Band leader (or better, the abstraction) saying to Dave how he should play the drums if he wants to be part of the band... Inversion of Control!

We can now truly inject a drummer into the band leader.

public class GigApp {
    public static void main(String[] args) throws NoSongPlayedException {
        BandLeader bandLeader = new BandLeader(new CharlieWatts());
        bandLeader.playWeWillRockYou();
    }
}

Is this really better?

Yes it actually is. Our band leader object is much cleaner. No messy if statements. When a new drummer is added to the line-up, no more changes to the band leader object is required.

If the structure of a song changes, it would mean a single modification. Without Dependency Injection we would have to change each if statement in this case.

A bonus would be, easier unit testing. We could now easily use a Mock or a 'fake' Drummer.

So... We should use Dependency Injection all the time now? Hm, no... Certainly not. It depends on the requirements and the likeliness of changes to come. It wouldn't make much sense to implement Dependency Injection if the band has now (and in the future) got only one drummer... The first version of the band leader would be an okay alternative in that case

It all depends...