Apr 01, 2013
Table of contents:
When you start to build complex web applications, it’s important to structure your components so that they remain independent and your architecture remains flexible and maintainable going forward. However, once you add even the slightest bit of complexity, it can become difficult to maintain this form.
In Object Oriented Programming, it is inevitable that one object will need to use an instance of another. This is likely going to be happening in places all over your project. Say for example you are using a MySQL database. When you are making calls to the database, you will be using an instance of the database object. But what happens if you need to switch databases to use PostgreSQL instead? Dependency Injection is a design pattern that allows you to “decouple” these two components so they remain independent.
A second big advantage to Dependency Injection is when it comes to testing. Recently I introduced Test Driven Development and then we explored PHPUnit. Dependency Injection is another important part of writing good, well tested software.
Back to our database example. When you are testing your code, you probably don’t want to have to actually connect to a Database. Connecting to a database during testing can slow the process down and it requires you to maintain test data when really all you need to test are the methods that interact with the database. By using Dependency Injection, you can replace the database object with a static resource that acts the way you would expect the database to act.
So as you can see, Dependency Injection allows to keep your components independent. Dependency Injection sounds like such a complicated idea, but really it’s very simple. Essentially it just breaks down to passing an object it’s dependencies, rather than letting it create them itself.
To show you how Dependency Injection works in real life, here is an example in code. As usual, these examples are in PHP, but hopefully it should be easy enough to translate into whatever language you are using. Don’t take this code too literally, it’s just an example of passing a dependency, rather than something you would actually want to use.
Imagine we have the following database class:
class Database
{
public function __construct($dsn, $user, $password)
{
// Make a connection to the database
}
}
And we have a User class that creates a new user:
class User
{
private $name;
private $age;
private $website;
private $db;
public function __construct($name, $age, $website)
{
$this->name = $name;
$this->age = $age;
$this->website = $website;
// Instantiate database
$this->db = new Database(
"
mysql:host=localhost;dbname=database",
"username",
"password"
);
}
}
This code is bad for 3 main reasons.
Hopefully that example above shows the pain of not using Dependency Injection.
To refactor this example to use Dependency Injection, we simply have to pass the database object to the User class constructor:
class User {
private $name;
private $age;
private $website;
private $db
public function __construct(
$name,
$age,
$website,
Database $database
) {
$this->name = $name;
$this->age = $age;
$this->website = $website;
$this->db = $database;
}
}
Now that we are passing an instance of the database to the User class on construction we have solved the three problems that we had before.
Whilst Dependency Injection can seem like a difficult term to get your head around, it is really very simple. Dependency Injection is basically just providing an object with the things it needs, rather than allowing it to create them itself. So if an object requires access to the database, instead of allowing the object to create an instance of the database class, we can provide an instance instead.
Dependency Injection is really very important when it comes to testing. When you provide an object with it’s dependencies, it makes it very easy to mock them. If you allow an object to create it’s own dependencies, you now need to deal with them during your tests as the object now has multiple concerns.
To find out more about Dependency Injection, have a read of this article by Fabien Pontencier and this article by Martin Fowler.