DEV Community

Cover image for Understanding Dependency Inversion principle
Marc Guinea
Marc Guinea

Posted on

Understanding Dependency Inversion principle

"Depend upon abstractions, [not] concretions."

Dependency Inversion is the "D" of the SOLID principles promoted by Robert C. Martin.

There are a lot of articles talking about it, but I want to explain it as easily as possible through an example.

When you create a new service, create first its interface, then implement it in a class.

Example

A service that calculates the average of two numbers.

Interface has the contract that will always be respected

interface AverageCalculator
{
    public function calculate(float $first, float $second): float;
}
Enter fullscreen mode Exit fullscreen mode

Then, the implementation (which can be anything... a call to an external API, package, your own implementation...)

use AverageCalculator;

class InMemoryAverageCalculator implements AverageCalculator
{
    public function calculate(float $first, float $second): float
    {
        return ($first + $second) / 2;
    }
}
Enter fullscreen mode Exit fullscreen mode

When you inject it in a controller, never inject the class, inject the interface. Let Service Container resolve it.

use AverageCalculator;

class Somewhere
{
    private AverageCalculator $calculator;

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

    public function __invoke(float $first, float $second): float
    {
        return $this->calculator->calculate($first, $second);
    }
}
Enter fullscreen mode Exit fullscreen mode

Service Container knows what is the implementation (InMemoryAverageCalculator) so we just need to configure it if required.

Benefits

There are several benefits of this approach:

  • You force yourself at first step to define a contract (interface) knowing what you want.
  • You can change the implementation at any time without having to change everywhere it is used.
  • Concretions will not affect to the design of your application because your interface rules the inputs / outputs.

Conclusion

When injecting an interface, you are depending upon an abstraction, you don't set the class that implements that logic but yo define what to expect from that class.

This brings a lot of flexibility and adaptation to changes.

Remember:

Abstraction === Interface
Concretion === Class with implementation

Discussion (2)

Collapse
jefrancomix profile image
Jesús Franco

Which Service Container will know that you have only one implementation of the abstraction?

Collapse
mguinea profile image
Marc Guinea Author

No one, for that, it’s important that they provide a way to solve it. For example, in Laravel you can use “contextual binding” laravel.com/docs/8.x/container#con...