loading...
Cover image for Building a PHP Framework: Part 6 - Dependency Inversion, Inversion of Control, oh my!

Building a PHP Framework: Part 6 - Dependency Inversion, Inversion of Control, oh my!

mattsparks profile image Matt Sparks ・4 min read

Part 5 of this series discussed test driven development (TDD). Today, I'll be talking about Dependency Inversion, Inversion of Control, Dependency Injection, and other related topics.

What is Dependency Injection?

We'll start with Dependency Injection since it will lead us into our other topics. Wikipedia tells us that:

In software engineering, dependency injection is a technique whereby one object (or static method) supplies the dependencies of another object.

Have you ever wrote code similar to this:

<?php

class Superhero 
{
    public $name;

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

class ComicBook
{
    public $mainCharacter;

    public function __construct(Superhero $superhero) 
    {
        $this->mainCharacter = $superhero;
    }
}

$superhero = new Superhero('Caffeine Man');
$comic = new ComicBook($superhero);

That's Dependency Injection in action. Our ComicBook object depends on our Superhero object so we inject it. Get it?

What is Dependency Inversion?

Comic books aren't always about superheroes. What if we wanted a comic book whose main character was a dog or a vampire or a vampiric dog? Let's re-write our code to allow any type of character to be used.

<?php

interface CharacterInterface
{
    /**
     * All classes implementing this interface must provide a getName method.
     */
    public function getName() : string;
}

abstract class Character implements CharacterInterface
{
    public $name;

    public function getName() : string
    {
        return $this->name;
    }
}

class Superhero extends Character
{
    public $name;

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

class VampiricDog extends Character
{
    public $name;

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

class ComicBook
{
    public $mainCharacter;

    public function __construct(CharacterInterface $character)
    {
        $this->mainCharacter = $character;
    }
}

$superhero = new Superhero('Caffeine Man');
$superheroComic = new ComicBook($superhero);

var_dump($superheroComic->mainCharacter->getName());

$vampireDog = new VampiricDog('Mr. Fangz');
$vampireComic = new ComicBook($vampireDog);

var_dump($vampireComic->mainCharacter->getName());

Because we're coding to an interface we can now create any type of character for use in a comic book. We've completely decoupled our character implementation from our comic book implementation. Think about it – a comic book just wants to be a comic book and to do comic book things. It doesn't care what kind of character you give it, it just knows it needs one. Once the character is in place the comic book can get back to doing the work of a comic book. In other words:

A. High-level modules [ComicBook ] should not depend on low-level modules [Superhero ]. Both should depend on abstractions [CharacterInterface ].
B. Abstractions should not depend on details. Details should depend on abstractions.

What is Inversion of Control (IoC)?

Inversion of Control is sometimes referred to as "Hollywood's Law." As in, "don't call us, we'll call you." Brett Schuchert uses the game Monopoly to explain the concept:

Imagine the Monopoly system created with a number of players. The game orchestrates interaction between players. When it's time to have a player take a turn, the game might ask the player if it has any pre-movement actions such as selling houses or hotels, then the game will move the player based on the roll of the dice (in the real world, a physical player rolls dice and moves his or her token, but that's an artifact of the board game not being a computer - that is, it's a phenomenological description of what's going on rather than an ontological description). Notice that the game knows when a player can make decisions and prompts the player accordingly, rather than the player making the decision.

In traditional procedural programming steps are taken one after the other (do this, then do this, then do this). When a human takes a turn in Monopoly, they're in control:

  1. Buy hotel.
  2. Roll dice.
  3. Move to new spot.

If a computer were in control of the game it would look more like:

Computer: Would you like to make a purchase? 
Human: I would like to buy a hotel. 
Computer: Great, here's your hotel. Do you want to make additional purchases? Human: No. 
Computer: Please roll the dice and move to a new spot.

The flow of the game is completely inverted.

What is an IoC Container?

An IoC Container is a bit of a misnomer. A better and more accurate name is Dependency Injection Container (DiC). The two terms get used interchangeably all the time. Regardless, a DiC is responsible for managing objects – from how they're created to how they're configured. Frameworks use containers to inject dependencies at run-time.

What Does This Have to do With a Framework?

A lot, actually. When you use a framework you're essentially giving it control of your application. Additionally, IoC/DiC containers lay the foundation for most frameworks. I've begun work on the Analyze container, feel free to keep up with the progress on Github. Once it's finished I'm going to do a deep dive into how it works and does what it does.

Further Reading

Inversion of Control Containers and the Dependency Injection pattern
What is Dependency Injection?
DI, DiC, & Service Locator Redux
DIP in the Wild
InversionOfControl

One last thing, be sure to checkout my newsletter! Each week I'll send you a great email filled with updates, great links, tips & tricks, and other non-dev randomness. If you're interested you can sign up by following this link.

Originally Posted on DevelopmentMatt.com

Discussion

pic
Editor guide