I've written an open-source PHP library that uses DI.
The library has a single point of entry for people wanting to use it, the Client class, which has a constructor that looks like this (simplified):
class Client {
protected $depOne;
protected $depTwo;
public function __construct(DependencyOne $depOne, DependencyTwo, $depTwo)
{
$this->depOne = $depOne;
$this->depTwo = $depTwo;
}
}
DependencyOne and DependencyTwo also have their own dependencies. This means that anyone wanting to use my library that isn't using DI would need to do something like this:
$dependencyThree = new DependencyThree();
$dependencyFour = new DependencyFour();
$dependencyFive = new DependencyFive();
$dependencyOne = new DependencyOne($dependencyThree, $dependencyFour);
$dependencyTwo = new DependencyTwo($dependencyFive);
$client = new Client($dependencyOne, $dependencyTwo);
$someStuff = $client->doTheThing();
Would it be bad to make the dependency injection for just this class optional? e.g.
class Client {
protected $depOne;
protected $depTwo;
public function __construct(DependencyOne $depOne = null, DependencyTwo, $depTwo = null)
{
if (!isset($depOne)) {
$depThree = new DependencyThree();
$depFour = new DependencyFour();
$depOne = new DependencyOne($depThree, $depFour);
}
if (!isset($depTwo)) {
$depFive = new DependencyFive();
$depTwo = new DependencyTwo($depFive);
}
$this->depOne = $depOne;
$this->depTwo = $depTwo;
}
}
This would mean that someone not using DI could just do this:
$client = new Client();
$someStuff = $client->doTheThing();
Those wanting to use DI, or for unit testing purposes, the dependencies can still be passed in.
Is this a bad idea, and if so why?
This is exactly what I do in my open source projects and I think it's a very good solution.
It offers a way for users to start using your library with 0 effort, and at the same time it leaves them the possibility to override anything.
Another solution I sometimes opt for is to have a builder class or factory, see for example this constructor has mandatory parameter, but the ContainerBuilder
hides this complexity.
The advantage of the builder is that it takes the configuration part out of the class. This is useful if the configuration part starts to grow (separation of concerns).
I agree with @Unex. It is not bad make the DI optional but instead of hardconding in the constructor I would provide a factory in my library. This way you keep the same loose coupling you get with DI and keep Client class with single responsibility. Why has Client class to know how to construct other classes? Class constructor is about self knowledge, nothing more. Keep in mind that build a entire dependency chain could be big and complex, maybe you have to even read config files at bootstrap, access constants defined in other php module, call utility functions, etc; you don't want to have all this code in a constructor.
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