Dependency Inversion Principle
Tags: solid, java, ruby, softwaredesign
Published: 2019-11-18 16:20:07

---
title: Dependency Inversion Principle
published: true
description: 
tags: SOLID, Java, Ruby, Software Design
---

The last SOLID rule is the dependency inversion principle. According to Robert Martin's Agile Software Development: Principles, Patterns and Practices, the principle is defined as,

>1) High level modules shall not depend on low-level modules. Both shall depend on abstractions.

>2) Abstractions shall not depend on details. Details shall depend on abstraction 

This is a lot, so let's look at each rule separately.

## High Level Modules Shall not Depend on Low-Level Modules

Let's look at a very simple Java example: 

{% gist https://gist.github.com/Naomi-Dennis/86bcb121205b7ff7ad43214350f5d1ef %}

In this example, `Monitor` is dependent on `DisplayPortCable` and `DisplayPortCable` is dependent on `Laptop`. If `Monitor` wants to use a different cord, say an `HDMI` or `VGA` cord, we will be forced to change `Monitor` directly. We _could_ give `Monitor` multiple constructors for each cord type; we could also create a base class that holds the behavior for all the cords. However, these approaches aren't desirable as it leaves the class rigid and cumbersome to expand and it could become bloated.  A base class may seem like a good idea, but it's too tempting to add behavior for all the cords in one place and simply extend it to the newly created classes. This bloat tends to break the [interface segregation](https://dev.to/naomidennis/introduction-to-the-interface-separation-principle-3bmj) principle. The same holds true for the relationship between `DisplayPortCable` and `Laptop`.

In statically typed languages like Java, it's best to invert the dependency using an interface (or virtual classes in C++). Interfaces allows us to define a low level type for our higher level classes (like `Monitor`) to use. 

#### Inverting a Dependency 

When inverting a dependency, we want the classes that are being depended on (in our example `DisplayPortCable` and `Laptop`) to instead, depend on an interface. Continuing with our example, the relationship between `DisplayPortCable` and `Monitor` looks like so:

{% gist https://gist.github.com/Naomi-Dennis/95d7c1b1e05623a27c645cab42cf4f98 %}

From the example, we create an interface for `VideoCable` and implement the cable we plan on using for `Monitor`. Instead of `Monitor` being restricted to accepting `DisplayPortCable` it can now accept any `VideoCable`. 

There is still a dependency in our code because `VideoCable` is dependent on `Laptop`. This helps brings us to the next part of this principle. 

## Abstractions Shall not Depend on Details. Details Shall Depend on Abstraction

When thinking about the details of a class, I like to think that details mean the behavior and properties of a class. In our example, this means `#connect()` and `#connectedDevice`. Although our `DisplayPortCable` is abstracted, it's still dependent on `Laptop`. The question we ask is the same that we asked before. What if a `VideoCable` wanted to connect to a `Desktop` or a `Smartphone` instead of a `Laptop`? 

And so, we invert the dependency like we did before. 

{% gist https://gist.github.com/Naomi-Dennis/8fb359e7cc1792060a82105d15b9c0e3 %}

### Dynamic vs Static Approaches to Dependency Inversion

The example above was done in Java. Statically typed languages usually have a concrete way of defining abstract interfaces. However, in dynamically typed languages, the idea of an abstract class is harder to implement as the tools to do so are not typically a feature of the language. In cases like this, we can use a technique known as duck typing. 

Duck typing is the idea, that if the behavior of a class walks in a particular way, and talks in a particular way, that way can be abstracted to the dependent classes.

The difference between an interface-esque keyword approach to dependency inversion and duck typing, is the former is written in contract form where the behavior is explicitly defined and enforced; the latter is not explicitly defined and becomes apparent as more object types are used within a class. If we were to convert our above code to Ruby, duck typing wouldn't be obvious because there's only one object type depending on an abstraction. 

{% gist https://gist.github.com/Naomi-Dennis/872cf2221e8be8ae055c314ae5cf218c %}

The issue becomes more apparent as we add more classes. 

{% gist https://gist.github.com/Naomi-Dennis/d56c3dc71071cb144ed561c68255f3c0 %}

Since there isn't a defined contract, we _could_ use any old interface with our new classes and simply ask for the type of `videoConnector` and use whatever interface was defined. However, this not only makes `Monitor` difficult to extend (since we'd have to manually add a new conditional to understand a new type), but it leads `Monitor` to know about the inner workings of other classes which directly violates this principle. `Monitor` has to depend on an abstraction of the object it's receiving. `Monitor` shouldn't care about the object's class, just its behavior. 

To do this, we have to consciously ensure the classes have an agreed upon interface and that they depend on this interface. In the case of our example, we have to dictate if `#connectToDevice()` will expect `videoConnector` to have the `#connect()` or `#connectToSomething()` behavior. 


## In Conclusion

Dependency inversion can be tricky. However, it allows a class to be more flexible and trains us to think about classes in terms of behavior, rather than construction.