Dependency injection

Knowing that objects should be decoupled from one another is one thing. Actually doing it in practice is quite another, but I found some great blog posts that I found really helpful – for example:

>The problem with most dependencies is the way that code handles and interacts with them. What I really mean is the problem is in the code and not the dependency. 
>
> — Ryan at Potstuck.com: [PHP Dependency Injection](http://www.potstuck.com/2009/01/08/php-dependency-injection/)

[Fabien Potencier](http://fabien.potencier.org/), the creator of the Symfony framework for PHP, has become something of an evangelist for dependency injection in the PHP world and made available some awesome [presentations](http://www.slideshare.net/fabpot/dependency-injection-with-php-and-php-53) and [articles](http://fabien.potencier.org/article/11/what-is-dependency-injection)

The issue is not that dependencies are bad; a web application cannot function without some sort of session persistence or access to a database of some sort, for example. These are necessary dependencies and will always be there. But the problem is how your classes get at their dependencies such as session storage, cache and database.

A common approach might be to inherit the database handle from a base class or set it in the __construct() method then reference it where needed. This works well enough, but leads to precisely the sort of coupling we don’t want:

[php]
class User
{
private $conn;
public function __construct()
{
$this->conn = DB::getInstance();
}
public function getUsers()
{
$query = $this->conn->prepare("SELECT * FROM users");

}
}[/php]

But here we still have the problem that the User class goes and gets the database handle from another class in the application. Scale this up in a significant web application and you can end up with a chaotic spider’s web of dependencies between classes that is hard to unravel. This directly breaks the “separation of concerns” paradigm, and also creates significant problems when trying to write unit tests for these classes.

Dependency injection is based on Inversion of Control, colloquially known as the Hollywood Principle; don’t call us, we’ll call you. In this case, we move the dependency outside of the class definition and ‘inject’ it into the class by one of various means

The three standard methods are

Constructor injection:
[php]$user = new User(DB::getInstance());[/php]

Setter injection:
[php]$user = new User();
$user->setDB(DB::getInstance());[/php]

Property injection:
[php]$user = new User();
$user->conn = DB::getInstance();[/php]

Each of these breaks the internal dependency of the User class on the DB class and makes it much easier to test the User class as a single unit of work, at the cost of one extra line of code wherever the User class is instantiated.

This might not be ideal in complex situations. For example, there may be several dependencies to inject, such as database cache or session storage container; this makes the constructor method signature more cluttered if you’re using constructor injection, or adds additional lines of code in the other two cases. It also makes it necessary to understand all the required dependencies of a class and explicitly set them everywhere you use it. In practice this could make the application considerably harder to work on.

There is another pattern that can be used to alleviate this sort of concern; using a factory or container to manage the dependencies in one place. So for example

[php]
class Container
{
public static function makeUser()
{
$user = new User();
$user->conn = DB::getInstance();
$user->storage = new SessionStorage(‘memcache’);
$user->cache = new Cache(‘memcache’);
return $user;
}
}
[/php]

And typical use would look something like this:

[php]
$user = Container::makeUser();
$users = $user->getUsers();
…[/php]

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s