Objects will often depend on other objects. Instead of creating the dependency in the constructor, the dependency should be passed into the constructor as a parameter. This ensures there is not tight coupling between the objects, and enables changing the dependency upon class instantiation. This has a number of benefits, including making code easier to read by making the dependencies explicit, as well as making testing simpler since the dependencies can be switched out and mocked more easily.
In the following example, Component
will depend on an instance of Logger
, but it doesn't create one. It requires one to be passed as argument to the constructor instead.
interface Logger {
public function log(string $message);
}
class Component {
private $logger;
public function __construct(Logger $logger) {
$this->logger = $logger;
}
}
Without dependency injection, the code would probably look similar to:
class Component {
private $logger;
public function __construct() {
$this->logger = new FooLogger();
}
}
Using new
to create new objects in the constructor indicates that dependency injection was not used (or was used incompletely), and that the code is tightly coupled. It is also a sign that the code is incompletely tested or may have brittle tests that make incorrect assumptions about program state.
In the above example, where we are using dependency injection instead, we could easily change to a different Logger if doing so became necessary. For example, we might use a Logger implementation that logs to a different location, or that uses a different logging format, or that logs to the database instead of to a file.