服务容器和工厂同时

时间:2017-05-16 08:21:27

标签: php symfony dependency-injection factory-pattern

这是我的第一篇文章,所以如果我做错了,请纠正我:)

我正在阅读很多帖子(不仅仅是关于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经验的人能帮助我做出这个决定:)

0 个答案:

没有答案