这是我的第一篇文章,所以如果我做错了,请纠正我:)
我正在阅读很多帖子(不仅仅是关于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经验的人能帮助我做出这个决定:)