Dependency Injection, Inversion of Control and Containers

Posted February 15, 2014 - 5 min read
Topics:  

If you are starting your career as a software developer, maybe you have heard about some words like “DI” or “Dependency Injection” and got confused.

Some people just skip over these buzz words not giving themselves any chances to figure out what is all about.

In this post I will try to make a clear picture about Dependency Injection and how you can make use of it in your web development.

Example

As usually, I will start with an example, let’s see this code sample:

class Users {
  /**
  * @var PDO Database connection
  */
  protected $dbConnection;

  /**
   * Construct.
   */
  public function __construct()
  {
    $this->dbConnection = Database::getInstance();
  }
}

At first sight, this code may seem not harmful.

If you look deeper, you should consider that we have already hard coded directly the PDO database connection into the class.

What if we want to use another database layer?

Why should our Users class be responsible for connecting to the database?

The basic idea is that classes should be concerned only about what they do.

This is called Separation of concerns.

So let’s rewrite our code to make it accept a database connection from outside.

Dependency Injection

By making it accept a database connection from outside, we are injecting a dependency.

This can be accomplished by using either a constructor method:

class Users {
  /**
  * @var PDO $dbConnection Database connection
  */
  protected $dbConnection;

  /**
   * Construct.
   * @param PDO $dbConnection Database connection
   */
  public function __construct($dbConnection)
  {
    $this->dbConnection = $dbConnection;
  }
}

$users = new Users($dbConnection);

or a setter method:

class Users {
  /**
  * @var PDO $dbConnection Database connection
  */
  protected $dbConnection;

  public function __construct() {}

  /**
   * @param PDO $dbConnection Database connection.
   */
  public function setDatabase($dbConnection)
  {
    $this->dbConnection = $dbConnection;
  }
}

$users = new Users();
$users->set->setDatabase($dbConnection);

This way our Users class is not dependent anymore on any database connection. Furthermore, the system retain control as it should be.

One more positive side effect of using such pattern, is that now our Users class is more testable. We are now able to mock the database connection when writing unit-tests.

Still we have one problem. We are now required to remember the Users class dependencies each time we want to initialize it.

Let’s suppose that our class, over the time, has become more complicated and now it requires more dependencies, for example:

$users = new Users();
$users->setDatabase($dbConnection);
$users->setConfiguration($config);
$users->setErrorHandler($response);
$users->setWhatEver($whatever)

DI added more complexity and confusion.

The first class without DI was simple, we just used new Users(), but now each time we create an instance we have to remember all of its dependencies.

Containers

Containers come to the rescue!

A container is a class that holds all the dependencies of not only the Users class but of all the registered classes.

If you ever heard “Inversion of Control” or simply “IoC”, that is what it means!

In software engineering, inversion of control (IoC) is an object-oriented programming practice where the object coupling is bound at run time by an assembler object and is typically not known at compile time using static analysis. In traditional programming, the flow of the business logic is determined by objects that are statically assigned to one another. With inversion of control, the flow depends on the object graph that is instantiated by the assembler and is made possible by object interactions being defined through abstractions. The binding process is achieved through dependency injection, although some argue that the use of a service locator also provides inversion of control.

Let’s see how we can write a simple container:

class Container
{
  /**
  * @var The database connection
  */
  protected $dbConnection;

  /**
   * Create a new instance of Users and inject its dependencies
   * @return Users
   */
  public static function getUsers()
  {
    $users = new Users();
    $users->setDatabase(Database::getInstance());
    //$users->setConfiguration($config);
    //$users->setErrorHandler($response);
    //$users->setWhatEver($whatever)

    return $users;
  }
}

$users = Container::getUsers();

This way each time we want a new instance of Users we call Container::getUsers(). The container will handle all the dependencies.

We can write a more generic container to be used with any class:

class Container {
  /**
  * @var array
  */
  protected static $registry = array();

  /**
   * @param string $name
   * @param \Closure $resolve
   */
  public static function register($name, \Closure $resolve)
  {
    static::$registry[$name] = $resolve;
  }

  /**
   * @param $name
   * @return mixed
   * @throws \Exception
   */
  public static function resolve($name)
  {
    if ( static::registered($name) )
    {
      $name = static::$registry[$name];
      return $name();
    }

    throw new \Exception('Nothing is registered with the key ' . $name);
  }

  /**
   * @param string $name
   * @return bool
   */
  public static function registered($name)
  {
    return array_key_exists($name, static::$registry);
  }
}

Our container is ready, let’s see how to register and resolve our classes.

// Register Users with key ‘users’
Container::register(‘users’, function() {
  $users = new Users();
  $users->setDatabase(...);
  $users->setConfiguration(...);
  // ...

  return $users;
});

// Get a new instance of Users
$users = Container::resolve(‘users’);

By using containers, the complexity that DI introduced is gone.

We can even rewrite our container this way, taking advantage of magic methods __set() and __get():

class Container {
  protected $registry = array();

  public function __set($name, $resolver)
  {
    $this->registry[$name] = $resolver;
  }

  public function __get($name)
  {
    return $this->registry[$name]();
  }
}

and it would be used like:

$c = new Container();
$c->users = function() {
  $users = new Users();
  $users->setDatabase(...);
  $users->setConfiguration(...);
  // ...

  return $users;
};

$users = $c->users;

Conclusion

Now you should have no more confusion about those “scary” words like DI or IoC.

There exist many frameworks that are already making extensive use of containers and DI, for example Laravel, which is, by the way, one of my favorite frameworks.