Dependency Inversion Principle

In the last article we learned about the Interface Segregation Principle. In this article we will be going over the Dependency Inversion Principle.

The Dependency Inversion Principle, is the fifth SOLID Principle for OOP.

For those new to SOLID, S.O.L.I.D Stands for:

  • Single Responsibility Principle
  • Open Closed Principle
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle

This article is the fifth of a five post series that will cover the SOLID Principles.

A higher class should never depend on a lower class, but only depend on an abstraction of that class.

This principle is one of my favorite principles, because it allows us to decrease the coupling of classes, making our code overall easier to maintain, and test. Both good things!

So let's think of an Orchestra. We have a Conductor, who directs all the musicians, playing their instruments. We also have 'chairs' if you will. For instance, each instrument has a first chair, and second chair, and so on.

Let's call our Conductor our Higher Class, and our musicians lower classes. Let's further say our Conductor has a concert to perform, and he requires all his musicians to perform a piece. However, his first chair tuba player (a lower class) is out due to a tuba related injury. Now, the Conductor (a higher class) could say, well we can't perform our piece because our first chair tuba player is injured, or the conductor can temporarily promote the second chair Tuba player Ted who had nothing to do with Fred's Tuba related injury I promise!

This is the idea behind Dependency Inversion. Our Higher class isn't dependent on a lower class, it has the ability to swap out the lower class with another similar class if required, and the music still gets made.

Let's see what this looks like using our Garden Outputter examples.

Class GardenOutPutter {  
    private $garden;
    Public function __constructor(Garden $garden)
    {
        //This is normal dependency injection.
        $this->garden = $garden;
    }
}

The class above is how were doing this in our previous examples. We are injecting our Garden class dependency into our GardenOutPutter class. In this example the GardenOutPutter is our higher class, and our Garden is our lower class.

Note: In the above example, even if we pass our Plot class into our GardenOutPutter class, it will still check out as a Garden class becuase Plot is a subclass or child of the Garden class.

What if our garden example required a bit more flexibility though, and all of our classes we wanted to send to our GardenOutPutter class didn't extend our Garden class?

We need to create a GardenInterface.

Interface GardenInterface  
{
    public function quantity();
}

Now our Garden, Plot, Field, PlanterBox need to implement the GardentInterface interface. Then we need to make some slight changes to our GardenOutPutter class.

Class GardenOutPutter {  
    private $garden;
    Public function __constructor(GardenInterface $garden)
    {
        //This is dependency inversion injection
        $this->garden = $garden;
    }
}

Now when we pass any class that implements our GardenInterface Interface the $garden variable will hold that class object.

Inversion of Control Containers

So far in all of our code examples in the last five articles, we have been instantiating our classes, and passing dependent classes into higher classes. However, when thinking about using a framework like Laravel, how would we pass a class into a controller?

A controller is just a class, like any other class, but it follows the front-controller design pattern. It acts like a logic gate keeper so to speak, handling requests, and formatting responses. But when we land on our index page to our website, and we load our [email protected], how would we pass a dependent class to that method?

We do this through Inversion of Control Containers in Laravel. What this allows us to do is bind a class to an interface. So in our HomeController index() method we can do something like this:

class HomeController  
{
    ...
    private $garden;
    public function index(GardenInterface $garden)
    {
        ...
        $this->garden = $garden;
    }
}

This should look familiar so far. However, this isn't going to work yet, because we haven't passed a class that implements the GardenInterface to this method. We still need to bind a class to that Interface.

To do this we do the following:

$this->app->bind('App\NameSpace\GardenInterface', function () {
    return App\NameSpace\PlanterBox;
});

This code, when ran will tell Laravel, when ever you see a method requiring a GardenInterface class, pass it the PlanterBox class.

Now in our HomeController index() method, our $this->garden variable will hold the class object of PlanterBox.

Now where does this code actually go? In Laravel 5.* in the app/Providers directory their is a AppServiceProvider Class. The above code will go in the register() method in that class.

Laravel loads service providers when the application boots up, so code in the register and boot methods will be run before any controllers are loaded. This allows us to create this binding for our controllers.

Now, we have decoupled our Controllers from our Gardens. Now if we need to write a test, we can mock our garden class so our tests are only testing the HomeController. Also, if we needed to swap our PlanterBox class out with a Field class later on, we just need to update our binding to point to the new class. As long as Field is also implementing the GardentInterface Interface it will now load a Field Object into $this->garden in our [email protected] method.

Thinking about this in a more Laravel way, instead of Garden classes we often use Eloquent Models in our controllers. So what we could do instead is do Dependency Inversion for our Models, and that way when we write tests we can mock for instance the User model, and run tests that don't interact with our DB.

There are lots of other good examples on how this is a powerful tool, but for the purpose of this article, I won't be going into further examples. However, I used Dependency Inversion and Injection extensively in my earlier articles about building a REST API in Laravel 4. While it's a bit outdated for Laravel version numbers, the same principles apply.

comments powered by Disqus