DEV Community

Cover image for What inverts in the dependency inversion principle?
Rui Figueiredo
Rui Figueiredo

Posted on • Edited on • Originally published at blinkingcaret.com

What inverts in the dependency inversion principle?

Have you ever wondered why the dependency inversion principle is named the way it is?

The dependency inversion principle is often described as "Depend on abstractions, not on concretions.". But from that description it is hard to make up what is inverted.

If we would draw a class diagram of a class structure where the dependency inversion principle was not present and then we changed it so that it would not violate the principle, would the arrows indicating dependencies invert? Well, let's try and see.

I'm shamelessly basing this example from the one from here, taken from Uncle Bob's butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod.

Imagine you have a Copier class, and that class takes input from a keyboard and sends it to a printer. The Copier class depends on a Keyboard class to read keystrokes from the user and a Printer class to print those keys (Fig 1).

public class Copier{
    private readonly Keyboard _keyboard;
    private readonly Printer _printer;

    public Copier(Keyboard keyboard, Printer printer){
        _keyboard = keyboard;
        _printer = printer;
    }


    public void Copy(){
        int c = _keyboard.Read();
        while(!_keyboard.IsEndingCharacter(c)){
            _printer.Write(c);
            c = _keyboard.Read();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Diagram of copier class depending on concrete classes keyboard and printer
Fig 1 - Copier class that violates the dependency inversion principle

And now we are going to apply the dependency inversion principle, i.e., instead of depending on concrete classes, Copier will depend on abstractions (Fig 2). Those abstractions will be:

  • IReader - defines a contract for getting input from a source
  • IWriter - defines a contract to writing data to an output

-

public class Copier{
    private readonly IReader _reader;
    private readonly IWriter _writer;

    public Copier(IReader reader, IWriter writer){
        _reader = reader;
        _writer = writer;
    }


    public void Copy(){
        int c = _reader.Read();
        while(!_reader.IsEndingCharacter(c)){
            _writer.Write(c);
            c = _reader.Read();
        }
    }
} 
Enter fullscreen mode Exit fullscreen mode

Diagram of copier class obeying the dependency inversion principle
Fig 2 - Copier class that does not violate the dependency inversion principle

Now, Keyboard and Printer are implementations of Reader and Writer, and Copier does not depend on them any more, meaning you can now copy from and to other things, for example, from the keyboard to a file on disk.

There are many advantages in doing this. Now, Copier can be reused without requiring the Keyboard and Printer classes. Changes in implementation details of Keyboard and Printer are guaranteed to not affect Copier if the contract (method signature) that IReader and IWriter define doesn't change.

But, do you see any clearly inverted dependencies between the diagrams?

Me neither.

But let's try to describe the relationship between the Copier class and it's dependencies.

At first, the Copier class depends on the low-level Keyboard and Printer classes.

After, the Copier class depends on the high-level Reader and Writer. It is this invert from low to high that can be though of as the "inversion" part of dependency inversion.

It turns out that a long time ago higher level modules depending on lower level modules was something that was considered good practice (before Object Oriented Design). The name "Dependency Inversion" is alluding to this.

Here it is, more eloquently put, directly from the source (section: The Dependency Inversion Principle):

One might question why I use the word "inversion". Frankly, it is because more traditional software development methods, such as Structured Analysis and Design, tend to create software structures in which high level modules depend upon low level modules, and in which abstractions depend upon details. Indeed one of the goals of these methods is to define the subprogram hierarchy that describes how the high level modules make calls to the low level modules ... Thus, the dependency structure of a well designed object oriented program is "inverted" with respect to the dependency structure that normally results from traditional procedural methods.

Top comments (2)

Collapse
 
dvanherten profile image
Dave van Herten

Could it be this:

Before: Copier will break if keyboard renames the Read method.
After: Keyboard will break if IReader interface changes (on behalf of the Copier's needs).

Dependencies are inverted.

Collapse
 
thefuquan profile image
Redouane M. BOUDELLA

That was a good read.

I think we can say that before the inversion, high level module depends on low level modules coz the copier should know about the keyboard and printer modules in order for it to call their functions, and the low level modules may know nothing about the high level ones.

After the inversion, the high level modules introduces a protocol, a contract aka an interface that the low level modules should know about in order to implement them, that is the inversion as the high level modukes now may/should know nothing about the low level ones