Original Class
class HomeController
{
function __construct()
{
$this->setPhpRenderer('Module');
$this->repository = new HomeRepository($id);
$this->proposalService = new ProposalService($this->repository);
$this->quickAdd = new QuickAdd($this->repository);
}
}
/*
* Instantiation is nice and clean:
*/
$home = new HomeController();
Using Dependency Injection
class HomeController
{
function __construct(
PhpRenderer $renderer,
HomeRepository $repository,
ProposalService $proposalService,
QuickAdd $quickAdd)
)
{
$this->renderer = $renderer;
$this->repository = $repository;
$this->proposalService = $proposalService;
$this->quickAdd = quickAdd;
}
}
/*
* Instantiate Class components
*/
$renderer = PhpRenderer('Module');
$repository = new HomeRepository($id);
$proposalService = new ProposalService($this->repository);
$quickAdd = new QuickAdd($this->repository);
/*
* Instantiate class -
*/
$home = new HomeController(
$renderer,
$repository,
$proposalService,
$quickAdd
);
Question
I am noting that DI adds a boatloads of code and I want to keep my instantiation nice and neat while still using dependency injection principles.
One thought I’ve had is instead of passing individual class components, put those components into an array
. This will add extra code of course to manage the array
, but all class components will be tucked into it.
5
You can use IoC containers to do that. You just dump all of the dependencies in them (or let the IoC container discover them).
I do not recommend this route as it complicates debugging, complicates unit testing, and otherwise obfuscates what’s going on when you’re just looking to make things cleaner (and thus more readable).
You could use basic factories, which takes the injection and hides it behind a factory call.
I generally do not recommend this route as it’s added boilerplate to what you already have and more things to screw up. And it also tends to make your code less discoverable.
But in general – you should not do either. Those few added lines aren’t that much noise. They make it explicit what is going on. They add flexibility for how people can use your code. They make it easier to debug the code. They make the code better.
2
The best technique to resolve this is to choose convention over configuration.
Containers can end up hiding dependencies in a mysterious way. Factories can end up doing the same but don’t have to.
The heart and soul of convention is default values. The trick is you need to allow every default to be overridden independently. If your language allows named parameters you get this for free. If not you need construction code that simulates it.
In java the go to pattern for this is the josh builder. There is a PHP variation.
Honestly, the josh builder style only leads to less code than what you started with here when you have more code that builds objects than you have code that defines objects.
Your style makes defining a class easy but building it a pain. The Bloch builder makes defining a class a pain but building easy.
They both offer flexibility so it’s what you do most that determines what makes most sense.