Search code examples
design-patternsdependency-injectiondesign-principlesdependency-inversion

Is the dependency inversion principle really present in the context of a DI system?


I managed to understand the dependency injection concept, but I simply don't see where the dependency inversion takes place.

For example, this class has tight dependencies.

class Man
{
    public function eat()
    {
        $food = new Food();
        $food->unpack();
        $food->use();
    }
}

But when applying the DI concept it kinda turns into this:

class Man
{
    public function eat($food)
    {
        $food->unpack();
        $food->use();
    }
}

However, whatever the case would be, the Man will still depend on Food, so I see no inversion of dependencies here.

The only difference is who puts Food on his table.

So I'm asking you to make it clear for me, where and how is the inversion principle applied?


Solution

  • Note that DI is A WAY to follow DIP principles.

    A simple example to understand the concept of "inversion" is to take a static language, like Java.

    With no DIP applied, you would have:

    public class CarStarter {
        public start() {
           new Ferrari().start();       
        }
    }
    

    Here, we would have to break the class if we want to change the Ferrari by Porsche. We say that "CarStarter" depends on a low-level item: the Ferrari.

    So DIP prones: "I do not want the CarStarter to depend on the specific nature of the car, I want the car to depend on the CarStarter !"

    So what would be an easy solution to fulfill this requirement in this case?

    => Make the CarStarter depends on an interface (Car) instead of the concrete element.
    CarStarter expects the interface, so Ferrari MUST implement it. That's why we talk about "inversion":
    Before, Ferrari was "free" of any rules to be implemented.
    But nowFerrari is expected to follow some rules, in this case "implementing the Car interface".

    This way, you'd let your CarStarter be UNAWARE of the concrete classes it uses:

    public class CarStarter {
            public start(Car car) {
               car.start();       
            }
        }
    

    Note that this practice really eases the testing of the CarStarter class, since you could stub/mock the Car.

    EDIT -------

    Here's an excerpt of Uncle Bob (author of DIP principle):

    One might question why I use the word “inversion”. Frankly, it is because more traditional software development methods, such as Structured Analysis and Design, tend to create software structures in which high level modules depend upon low level modules, and in which abstractions depend upon details. Indeed one of the goals of these methods is to define the subprogram hierarchy that describes how the high level modules make calls to the low level modules. Figure 1 is a good example of such a hierarchy. Thus, the dependency structure of a well designed object oriented program is “inverted” with respect to the dependency structure that normally results from traditional procedural methods.

    Thus, to sum up:

    The word inversion comes from the fact that the DIP principle aims to "inverse" the habits of developers:

    Abstraction should not depend upon Details.
    Details should depend upon Abstractions.