I use Laravel, but hopefully this doesn't matter. I have a user and user's state. Say, simple integer or a string. I want to run some logic depending on user state in my service class.
So, the first approach is pretty simple:
<?php
class UserStateManager
{
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function handle(UserRepository $userRepository)
{
$user = $this->userRepository->find(1);
switch ($user->state) {
case 'new':
$this->handleUserNewState();
break;
case 'banned':
$this->handleUserBannedState();
break;
// ...
}
}
private function handleUserNewState()
{
// Do some logic
// Here we have access to userRepository which was injected in UserStateManager constructor
}
}
Pay attention that I injected UserRepository
in the constructor.
So, using this approach I keep UserStateManager
dependencies injected, without creating them manually.
But now I want to put every state handler to a separate class. Like, to have UserNewState
class with handle
method.
The problem is that in this case:
public function handle(UserRepository $userRepository)
{
$user = $this->userRepository->find(1);
switch ($user->state) {
case 'new':
(new UserNewState)->handle();
break;
case 'banned':
(new UserBannedState)->handle();
break;
// ...
}
}
if I create UserNewState
object I cannot inject dependencies into this class since I manually create it using new
operator. Passing lots of dependencies in the constructor (like new UserNewState($this->userRepository, ...)
) is not an option, ugly solution.
So how to deal with this case? Because it's very common: on the one hand I can separate logic into several methods inside one class, on the other hand I cannot separate logic into many classes.
The problem is dependency injection and creating these class manually. And I reckon that DI is a good thing.
Of course I can create dependencies inside UserNewState
class, directly in the constructor:
public function __constructor() {
$this->userRepository = new UserRepository;
}
but this is not testable. Even though I don't test my apps, I just want to follow best practices.