Это мой первый пост, так что если я что-то делаю не так, поправьте меня 🙂
Я читаю много постов (не только о stackoverflow) и знаю, что внедрение зависимостей — верный способ написания кода. Но я нигде не нахожу информации, исключает ли использование контейнера зависимости зависимостей использование фабрики?
Я начинаю писать код, используя внедрение зависимостей. Теперь у меня есть несколько функций, написанных на DI, и мне нужен контейнер, чтобы сделать код более понятным (приложение находится в Symfony 1.4, поэтому я использую внешний DI-контейнер http://container.thephpleague.com/).
Позвольте мне показать пример. у меня урок TvMonitor
который я использую, чтобы проверить некоторые условия и уведомить меня, если какое-то условие не было выполнено (отобразить информацию на специальном мониторе). Теперь мой код выглядит примерно так:
class TvMonitor
{
public function check(){ /* run check on every injected checker */ }
public function addChecker(CheckerInterface $checker){ ... }
public function addCheckers(CheckerInterface[] $chekers) { /* make foreach and use `addChecker` */ }
}
interface CheckerInterface
{
public function check();
public function getDetails(): array;
}
class CheckerFactory
{
public function getForFirstMonitor(): array
{
return [
new FileError('/some/file/path'),
new ApiBugs(new SomeApiClient('http://base_url/', 'api_key', new HttpClient()))
];
}
public function getForSecondMonitor(): array
{
return [
new FileError('/some/other/file/path'),
new OtherApiBugs(new SomeOtherApiClient())
];
}
}
В контроллерах код выглядит так:
/* first monitor controller */
$checker_factory = new CheckerFactory();
$monitor = new TvMonitor();
$monitor->addCheckers($checker_factory->getForFirstMonitor());
$monitor->check();/* second monitor controller */
$checker_factory = new CheckerFactory();
$monitor = new TvMonitor();
$monitor->addCheckers($checker_factory->getForSecondMonitor());
$monitor->check();
Теперь у меня есть 2 идеи. Одним из них является удаление фабрики и сохранение в контейнере 2 экземпляров TvMonitor с другим списком проверок:
$container
->add('fisrt_checker', FileError::class)
->withArgument('/some/file/path');
$container
->add('second_checker', ApiBugs::class)
->withArgument(...);
$container
->add('third_checker', FileError::class)
->withArgument('/some/other/file/path')
$container
->add('fourth_checker', OtherApiBugs::class)
->withArgument(...)
$container
->add('first_tv_monitor', TvMonitor::class)
->withMethodCall('addCheckers', ['first_checker', 'second_checker']);
$container
->add('second_tv_monitor', TvMonitor::class)
->withMethodCall('addCheckers', ['first_checker', 'fourth_checker']);
Контроллеры будут выглядеть так:
/* first monitor controller */
$monitor = $container->get('first_tv_monitor');
$monitor->check();/* second monitor controller */
$monitor = $container->get('second_tv_monitor');
$monitor->check();
Вторая идея состоит в том, чтобы внести некоторые изменения в фабрику (внедрить внутрь всего объекта, необходимого для создания контроллеров) и зарегистрировать его в контейнере:
class CheckerFactory
{
public function __contstruct(SomeApiClient $some_api_client, SomeOtherApiClient $some_other_api_client){ ... }
/* rest method are the same but use injected objects */
}
$container
->add('some_api_client', SomeApiClient::class);
$container
->add('some_other_api_client', SomeOtherApiClient::class);
$container
->add('checker_factory', CheckerFactory::class)
->withArgument('some_api_client')
->withArgument('some_other_api_client');
$container
->add('tv_monitor', TvMonitor::class);
Контроллеры будут выглядеть так:
/* first monitor controller */
$checker_factory = $container->get('checker_factory');
$monitor = $container->get('tv_monitor');
$monitor->addCheckers($checker_factory->getForFirstMonitor());
$monitor->check();/* second monitor controller */
$checker_factory = $container->get('checker_factory');
$monitor = $container->get('tv_monitor');
$monitor->addCheckers($checker_factory->getForSecondMonitor());
$monitor->check();
Я думаю, что первая идея не хороша, потому что контейнер (или, скорее, его конфигурация) делает слишком много.
Я предпочту вторую идею, потому что каждый элемент делает одно:
Пожалуйста, дайте мне знать, что вы думаете об этих идеях (или, может быть, у вас есть другие). Я надеюсь, что кто-то с большим опытом в DI поможет мне с этим решением 🙂
Задача ещё не решена.
Других решений пока нет …