SHAME! SHAME! SHAME!

Introduction

After receiving a lot of positive feedback for my Batman article I decided to ask my buddy Bruce if he wanted to help me out for my last article of the SOLID series. And guess what he said? “Absolutely not!” So I asked the other super heroes to help me out. But apparently they are all “to busy” to help “some freaky nerd with his computer problems”. Stupid heroes. They are definitely nothing like in the comics I read. Reading comics. That is exactly what I should do now. I take out my BatComicbookReader, lie down in my comfy bed and open up the newest Batman’s Fall – Revenge of the nerds.pdf. Error! Only .bat files allowed. Darn you Batman. You got me once more!

This article is all about payback. Hence the inversion of dependencies. Hence the Dependency Inversion Principle.

The Problem with this problem

Do you see what Batman did here? The reason why I am unable to open up my pdf file using the BatComicBookReader is that it only supports BatComicBooks. The dependencies look as follows:

Or on a code basis

[pastacode lang=”java” manual=”public%20class%20BatComicBookReader%20%7B%0A%09%0A%09BatComicBook%20batComitBook%20%3D%20new%20BatComicBook()%3B%0A%0A%09public%20String%20readBatFile()%20%7B%0A%09%09return%20batComitBook.read()%3B%0A%09%7D%0A%09%0A%7D%0A%0Apublic%20class%20BatComicBook%20%7B%0A%09%0A%09private%20File%20batFile%20%3D%20new%20File(%22stupidbatmancomic.bat%22)%3B%0A%09%0A%09public%20String%20read()%20%7B%0A%09%09String%20content%20%3D%20getContent(batFile)%3B%0A%09%09return%20content%3B%0A%09%7D%0A%7D” message=”” highlight=”” provider=”manual”/]

According to the Dependency Inversion Principle this is an issue. It states that

  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.

Let us look at the first part. High-level modules should not depend on low-level modules. In our example the high-level module is the BatComicBookReader whereas the low-level module is the BatComicBook. The DIP already points out the solution. Both should depend on abstractions. Well, lets try that.

Or on a code basis

[pastacode lang=”java” manual=”public%20interface%20BatComicBook%20%7B%0A%09public%20String%20read()%3B%0A%7D%0A%0Apublic%20class%20BatComicBookImpl%20implements%20BatComicBook%7B%0A%09%0A%09private%20File%20batFile%20%3D%20new%20File(%22stupidbatmancomic.bat%22)%3B%0A%09%0A%09%40Override%0A%09public%20String%20read()%20%7B%0A%09%09String%20content%20%3D%20getContent(batFile)%3B%0A%09%09return%20content%3B%0A%09%7D%0A%7D%0A%0Apublic%20class%20BatComicBookReader%20%7B%0A%09%0A%09BatComicBook%20batComitBook%20%3D%20new%20BatComicBookImpl()%3B%0A%0A%09public%20String%20readBatFile()%20%7B%0A%09%09return%20batComitBook.read()%3B%0A%09%7D%0A%09%0A%7D” message=”” highlight=”” provider=”manual”/]

Now I can swap out as many .bat as I want by injecting them into the BatComicBookReader. But I am still unable to read my Batman’s Fall – Revenge of the nerds.pdf file. One way could be to invent a PdfComicBookReader, a pdf comicbook and a pdf file. A better approach though is to take into account the second part of the DIP. Abstractions should not depend upon details. Clearly the detail is the BatComicBookImpl.class. Instead of only allowing .bat formats, why not allow every format? The new dependency looks like this.

The code looks as follows.

[pastacode lang=”java” manual=”public%20interface%20ComicBook%20%7B%0A%09public%20String%20read()%3B%0A%7D%0A%0Apublic%20class%20BatComicBook%20implements%20ComicBook%7B%0A%09%0A%09private%20File%20batFile%20%3D%20new%20File(%22stupidbatmancomic.bat%22)%3B%0A%09%0A%09%40Override%0A%09public%20String%20read()%20%7B%0A%09%09String%20content%20%3D%20getContent(batFile)%3B%0A%09%09return%20content%3B%0A%09%7D%0A%7D%0A%0Apublic%20class%20ComicBookReader%20%7B%0A%09private%20ComicBook%20comitBook%3B%0A%09%0A%09%2F%2F%20Dependency%20Injection%20ftw!%0A%09public%20ComicBookReader(ComicBook%20comicBook)%20%7B%0A%09%09this.comitBook%20%3D%20comicBook%3B%0A%09%7D%0A%09%0A%09public%20String%20readBatFile()%20%7B%0A%09%09return%20comitBook.read()%3B%0A%09%7D%0A%09%0A%7D” message=”” highlight=”” provider=”manual”/]

The concept of Dependency Inversion finally lets me enjoy all comicbooks. Not only the stupid ass .bat files. Bruce, if you are reading this. You can buy my idea for “one millon dollars”.

Bonus knowledge

The pattern used in the example above is called Dependency Injection. This is a technique to decouple the creation of objects from the objects that are using them. The created objects are typically injected into the classes by either its constructor, called constructor injection, or by setter methods, called setter injection. Dependency Injection is enabeling Dependency Inversion.

What have we learned?

The real intent behind dependency inversion is to decouple objects to the extent that no client code has to be changed simply because an object it depends on needs to be changed to a different one. That achieves loosely coupling as each of its components has, or makes use of, little or no knowledge of the definitions of other separate components. It achieves testability and replaceability because components in a loosely coupled system can be replaced with alternative implementations that provide the same services.

PS: No nerds or bat(man)s were harmed during writing.

Categories: Blog Posts