У меня проблемы с зависимостями в моем приложении на уровне сервиса.
У меня есть следующий класс:
<?php
class UserService{
private $userRepository;
private $vocationService;
private $roleService;
public function __construct(UserRepository $userRepository, VocationService $vocationService, RoleService $roleService)
{
$this->userRepository = $userRepository;
$this->vocationService = $vocationService;
$this->roleService = $roleService;
}}
Есть только три зависимости, которые я внедряю.
Предположим, я хочу добавить следующую зависимость, например: NextService.
Мой конструктор снова вырастет.
Что если бы я хотел передать больше зависимостей внутри конструктора?
Может быть, я должен решить эту проблему, передав контейнер IoC, а затем получить желаемый класс?
Вот пример:
<?php
class UserService{
private $userRepository;
private $vocationService;
private $roleService;
public function __construct(ContainerInterface $container)
{
$this->userRepository = $container->get('userRepo');
$this->vocationService = $container->get('vocService');
$this->roleService = $container->get('roleService');
}
}
Но теперь мой UserService класс зависит от контейнера IoC, который я добавляю.
Как решить проблему, следуя передовой практике?
С уважением, Адам
Внедрение контейнера в качестве зависимости от вашего сервиса считается плохой практикой по нескольким причинам. Я думаю, что главное здесь — выяснить, почему, а затем попытаться понять проблему, которая заставляет вас думать о «впрыскивании контейнера» как возможном решении, и о том, как решить эту проблему.
В объектно-ориентированное программирование, важно четко определить отношения между объектами. Когда вы смотрите на зависимости данного объекта, вам должно быть интуитивно понятно, как объект ведет себя и каковы другие объекты, на которые он опирается, глядя на его общедоступный API.
Это также плохая идея, чтобы позволить вашему объекту полагаться на средство разрешения зависимостей, В примере, которым вы поделились, ваш объект не может жить без container
который предоставляется DI компонент.
Если вы хотите использовать этот объект в другом месте, например, в приложении, которое использует другую платформу, вам придется переосмыслить способ, которым ваш объект получает свои зависимости, и реорганизовать его.
Основная проблема здесь состоит в том, чтобы понять, почему вашему сервису нужны все эти зависимости,
В объектно-ориентированное программирование, принцип единой ответственности
утверждает, что каждый контекст (класс, функция, переменная и т. д.) должен
определить единую ответственность, и эта ответственность должна быть
полностью инкапсулирован контекстом. Все его услуги должны быть
тесно связаны с этой ответственностью.
Источник: Википедия
Исходя из этого определения, я думаю, что вы должны разделить UserService
в услуги, которые обрабатывают только один Ответственность каждого.
я согласна с тем что __construct
может расти довольно легко.
Тем не менее, у вас есть Setter DI
к вашим услугам: http://symfony.com/doc/current/components/dependency_injection/types.html#setter-injection
Более того, есть Property DI
, но я бы не рекомендовал его, так как ваш сервис остается открытым для манипуляций: http://symfony.com/doc/current/components/dependency_injection/types.html#property-injection
Вы можете абстрагировать некоторые часто используемые службы в одном вспомогательном сервисе, а затем просто добавить этот помощник в другие сервисы. Также вы можете определить некоторые полезные функции в этом вспомогательном сервисе. Что-то вроде этого:
<?php
namespace Saman\Library\Service;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Symfony\Component\Form\FormFactory;
use Symfony\Bundle\FrameworkBundle\Translation\Translator;
use Symfony\Bundle\TwigBundle\Debug\TimedTwigEngine;
use Symfony\Component\Security\Core\SecurityContext;
use Doctrine\ORM\EntityManager;
class HelperService
{
protected $translator;
protected $securityContext;
protected $router;
protected $templating;
protected $em;
public function __construct(
Translator $translator,
SecurityContext $securityContext,
Router $router,
TimedTwigEngine $templating,
EntityManager $em
)
{
$this->translator = $translator;
$this->securityContext = $securityContext;
$this->router = $router;
$this->templating = $templating;
$this->em = $em;
}
Getters ...
public function setParametrs($parameters)
{
if (null !== $parameters) {
$this->parameters = array_merge($this->parameters, $parameters);
}
return $this;
}
/**
* Get a parameter from $parameters array
*/
public function getParameter($parameterKey, $defaultValue = null)
{
if (array_key_exists($parameterKey, $this->parameters)) {
return $this->parameters[$parameterKey];
}
return $defaultValue;
}
}
Теперь представьте, что у вас есть UserService, и вы определяете его так:
<?php
namespace Saman\UserBundle\Service;
use Saman\Library\Service\HelperService;
class UserService
{
protected $helper;
public function __construct(
Helper $helper,
$parameters
)
{
$this->helper = $helper;
$this->helper->setParametrs($parameters);
}
public function getUser($userId)
{
$em = $this->helper->getEntityManager();
$param1 = $this->helper->getParameter('param1');
...
}