Understanding SOLID Principles: Dependency Inversion
Tags: beginners, computerscience, oop, solid
Published: 2021-03-07 07:11:28

At last, our journey has come to an end.

But now it's time to face the final boss.

DIP aka Dependency Inversion Principle

The final principle of the SOLID bosses.

Gear up, drink your mana potion, and prepare for combat.

## Table of Contents 

- [What is the dependency inversion principle?](#1)
- [Example](#2)
- [Why should I follow this principle?](#3)
- [Conclusion](#4)

## What is the dependency inversion principle? <a name="1"></a>

Let's google that. 

We get this:

> The Dependency Inversion Principle (DIP) states that high-level modules should not depend on low-level modules; both should depend on abstractions. Abstractions should not depend on details. Details should depend upon abstractions.

I get it, it sounds confusing. 

But let's break this down:

1. Client: Your main class/code that runs the high-level module.
2. High-Level Modules: Interface/Abstraction that your client uses.
3. Low-Level Modules: Details of your interfaces/abstraction.

What it basically says that imagine you have a car and your different components are:

1. Client: You as the person driving the car.
2. High-Level Modules: The steering wheel and the gas/brake peddles.
3. Low-Level Modules: Engine

**Abstractions don't depend on details.**

For me, it doesn't matter whether my engine has changed or not, I still should be able to drive my car the same way.

**Details should depend upon abstractions.**

I would not want an engine that causes the brake to double the speed.

## Simple Example <a name="2"></a>

Imagine you have a budget reporting system that reads from a database.

It will look something like this:

```php
<?php 

class BudgetReport {
    public $database;

    public function __construct($database)
    {
        $this->database = $database;
    }

    public function open(){
        $this->database->get();
    }

    public function save(){
        $this->database->insert();
    }
}

class MySQLDatabase {
    // fields
    
    public function get(){
        // get by id
    }

    public function insert(){
        // inserts into db
    }

    public function update(){
        // update some values in db
    }

    public function delete(){
        // delete some records in db
    }
}

// Client
$database = new MySQLDatabase();
$report = new BudgetReport($database);

$report->open();
```

Everything works fine, but this code violates the dependency inversion principle because our high-level module `BudgetReport` concretely depends on the low-level module `MySQLDatabase`.

This also violates the open-closed principle because what if we wanted a different kind of database such as MongoDB?

We will have to change the `BudgetReport` class to have if-else statements for it not to break. 

To fix this is pretty simple, instead of concretely relying on the database class, we should use an abstraction. We will create a `DatabaseInterface` which will implement any kind of database we need and finally we can inject our database through the constructor.

```php
interface DatabaseInterface {
    public function get();
    public function insert();
    public function update();
    public function delete();
}

class MySQLDatabase implements DatabaseInterface {
    // fields

    public function get(){
        // get by id
    }

    public function insert(){
        // inserts into db
    }

    public function update(){
        // update some values in db
    }

    public function delete(){
        // delete some records in db
    }
}

class MongoDB implements DatabaseInterface {
    // fields

    public function get(){
        // get by id
    }

    public function insert(){
        // inserts into db
    }

    public function update(){
        // update some values in db
    }

    public function delete(){
        // delete some records in db
    }
}

class BudgetReport {
    public $database;

    public function __construct(DatabaseInterface $database)
    {
        $this->database = $database;
    }

    public function open(){
        $this->database->get();
    }

    public function save(){
        $this->database->insert();
    }
}

// Client
$mysql = new MySQLDatabase();
$report_mysql = new BudgetReport($mysql);

$report_mysql->open();

$mongo = new MongoDB();
$report_mongo = new BudgetReport($mongo);

$report_mongo->open();
```

Now our `BudgetReport` does not depend concretely on the database class but on its abstraction `DatabaseInterface`. This approach also follows the open-closed principle because to add any new database we don't have to change the `BudgetReport` class. We just need to add a new database class that implements the `DatabaseInterface`.

## Why should I follow this principle? <a name="3"></a>

Because code that violates this principle may become too coupled together, and that makes the code hard to maintain, unreadable, and prone to side effects.

## Conclusion <a name="4"></a>

In summary:

1. The Dependency Inversion Principle (DIP) states that high-level modules should not depend on low-level modules; both should depend on abstractions. Abstractions should not depend on details. Details should depend upon abstractions.
2. Code that doesn't follow this principle can be too coupled, which means you will have a hard time managing the project.

If you have any questions, leave them down in the comments section.