I am trying to understand dependency injection in PHP and I see there are two ways to do this in Laravel.
So let us say I have a class Foo
like so:
class Foo{
}
Now I have a class called Bar
which is dependent on Foo
so I could do something like:
class Bar{
protected $foo;
public function __construct()
{
$this->foo = new Foo();
}
}
But in Laravel, I have come across terms like typehinting and reflection which allow me to do this:
class Bar{
protected $foo;
public function __construct(Foo $foo)
{
$this->foo = $foo;
}
}
What I am trying to understand is the difference between these two. Are they totally identical? And is there a specific reason I should prefer on over the other?
PS: I am newbie and I am not sure if I am using the jargon in the question correctly.
A class is no longer responsible for creating the objects it requires, and it does not have to delegate instantiation to a factory object as in the Abstract Factory design pattern. There are three types of dependency injection — constructor injection, method injection, and property injection.
Dependency injection is way of designing your class so that its dependencies can be provided from outside the class. An aggregate relationship can be modeled to support dependency injection but that does not mean Aggregation and Dependency Injection are the same thing. Save this answer.
Constructor Injection is the act of statically defining the list of required Dependencies by specifying them as parameters to the class's constructor. The constructor signature is compiled with the type and it's available for all to see.
It mostly comes down to coupling of code.
class Foo {
public function __construct() {
new Bar;
}
}
This couples a very specific Bar
to this specific Foo
. There's no way to alter which Bar
gets instantiated without rewriting this code. This also means Foo
needs to know about Bar
's dependencies. Maybe today Bar
can be instantiated with just new Bar
. But maybe tomorrow you're refactoring Bar
and must now instantiate it with new Bar($database)
. Now you also need to rewrite Foo
to accommodate that.
That's where dependency injection comes in (the above is not dependency injection, you're not injecting anything):
class Foo {
public function __construct(Bar $bar) { }
}
This Foo
merely declares that it needs an object with the characteristics of Bar
upon instantiation. But Foo
does not need to know anything about how Bar
came about to be, what its dependencies are or what exactly it does. The only thing it expects of Bar
is a defined public
interface, anything else about it is irrelevant. In fact, to gain even more flexibility, you may want to use an interface
instead of a concrete class dependency here.
Dependency injection allows you to divorce concrete details of classes from other code. It allows you to have one central place where classes are instantiated, which is a place where you need to know and consider concrete details about the classes you're instantiating. This can be a dependency injection container for example. You don't want to spread class instantiation logic all over the place, because as mentioned above, that logic may change, and then you need to rewrite code all over the place.
require_once 'Foo.php';
require_once 'Bar.php';
$foo = new Foo(new Bar);
The above code is where it's being decided which Bar
gets injected into Foo
. It's also the place which needs to worry about Bar
's dependencies. Note that dependency loading and instantiation is the only thing this code does. It's trivial to change only this piece of code as necessary, without needing to touch Foo
or Bar
, which may be full of complex business logic.
Dependency injected code also allows you to take your app apart and put it together flexibly. For example for testing purposes. Or simply to reuse different components flexibly in different contexts.
Also see How Not To Kill Your Testability Using Statics.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With