Even though we have removed all global
calls in our classes, they are likely to retain other hidden dependencies. In particular, we are probably creating new object instances in inappropriate locations, tightly coupling the classes together. These things make it much harder to write tests and to see what the internal dependencies are.
After converting the global
calls in a hypothetical ItemsGateway class, we might have something like this:
classes/ItemsGateway.php
1 <?php
2 class ItemsGateway
3 {
4 protected $db_host;
5 protected $db_user;
6 protected $db_pass;
7 protected $db;
8
9 public function __construct($db_host, $db_user, $db_pass)
10 {
11 $this->db_host = $db_host;
12 $this->db_user = $db_user;
13 $this->db_pass = $db_pass;
14 $this->db = new Db($this->db_host, $this->db_user, $this->db_pass);
15 }
16
17 public function selectAll()
18 {
19 $rows = $this->db->query("SELECT * FROM items ORDER BY id");
20 $item_collection = array();
21 foreach ($rows as $row) {
22 $item_collection[] = new Item($row);
23 }
24 return $item_collection;
25 }
26 }
27 ?>
There are two dependency injection issues here:
global $db_host
, $db_user
, $db_pass
and then constructed a Db
object internally. Our initial pass at removing global
calls got rid of the globals, but it left this Db
dependency in place. This is what we will call a one-time creation dependency.selectAll()
method creates new Item
objects, and as such is dependent on the Item
class. We cannot see this dependency from the outside of the class. This is what we will call a repeated creation dependency.The point of dependency injection is to push the dependencies in from the outside, thereby revealing the dependencies in our classes. Using a new
keyword inside a class is in opposition to that idea, so we need to work through the codebase to remove that keyword from our non-Factory
classes.
What is a Factory Object?
One of the keys to dependency injection is that an object may either create other objects, or it may operate on other objects, but not both. Any time we need to create an object inside another object, we let that work be done by something called a Factory with a newInstance()
method and inject that Factory into the object that needs to do creation. The new
keyword is restricted to being used inside Factory objects. This allows us to switch out Factory objects any time we need to create different kinds of objects.