Symfony3工厂即服务

时间:2016-12-16 13:32:57

标签: symfony service factory-pattern

我一直试图理解这个

http://symfony.com/doc/current/service_container/factories.html

但是似乎有一件事遗失了所有这一切 OR 我完全忽略了这一点。

该示例具有工厂类

class NewsletterManagerFactory
{
    public static function createNewsletterManager()
    {
        $newsletterManager = new NewsletterManager();

        // ...

        return $newsletterManager;
    }
}

然后通过服务将该工厂配置为NewsletterManager服务的工厂类和方法。

services:
    app.newsletter_manager_factory:
        class: AppBundle\Email\NewsletterManagerFactory

    app.newsletter_manager:
        class:   AppBundle\Email\NewsletterManager
        factory: 'app.newsletter_manager_factory:createNewsletterManager'

现在我们有一个NewsletterManager类通过NewsletterManagerFactory

中的factory:参数了解services.yml

问题

如何使用此配置?现在NewsletterManager内部暴露的内容允许我在工厂类中调用createNewsletterManager

据我所知,这两项服务仍完全分开?

2 个答案:

答案 0 :(得分:5)

我曾经使用过这种模式。这是它的用例。

想象一下,您有多个窗口小部件类,即Acme\Widget1Acme\Widget2Acme\WidgetN

每个小部件都有高级实例化过程,因此您决定使用工厂。它还具有复杂的依赖关系链,每个窗口小部件都需要实例化。即Acme\Dependency1Acme\Dependency2Acme\Dependency3

所以你要做的就是创建一次带有依赖关系的Acme\WidgetFactory服务。然后,您需要为每个窗口小部件指定Acme\WidgetFactory作为工厂。如果窗口小部件实例化方式发生了某些变化,您只需要更改一个类和一个服务定义。所有1到N小部件服务保持不变。

以下是示例......

典型的实施方式:

acme.widget1:
    class: Acme\Widget1
    factory: ['Acme\Widget1', 'create']
    arguments: ['@acme.dependency1', '@acme.dependency2', '@acme.dependencyN'] 

acme.widget2:
    class: Acme\Widget2
    factory: ['Acme\Widget2, 'create']
    arguments: ['@acme.dependency1', '@acme.dependency2', '@acme.dependencyN'] 

acme.widgetN:
    class: Acme\WidgetN
    factory: ['Acme\WidgetN', 'create']
    arguments: ['@acme.dependency1', '@acme.dependency2', '@acme.dependencyN'] 

在这里你有强烈的代码重复气味。如果你想改变一些东西你需要做N次。

相反,这是你可以做的。

acme.widget_factory:
    class: Acme\WidgetFactory
    arguments: ['@acme.dependency1', '@acme.dependency2', '@acme.dependencyN']

acme.widget1:
    class: Acme\Widget1
    factory: ['@acme.widget_factory', createWidget]

acme.widget2:
    class: Acme\Widget2
    factory: ['@acme.widget_factory', createWidget]

acme.widgetN:
    class: Acme\WidgetN
    factory: ['@acme.widget_factory', createWidget]

代码重复已经消失。

出现小不便......工厂不知道要实例化的具体类。我使用了以下技术。

我标记了每个小部件,然后在编译器传递期间向工厂添加了额外的参数。

acme.widget_factory:
    class: Acme\WidgetFactory
    arguments: ['@acme.dependency1', '@acme.dependency2', '@acme.dependencyN']

acme.widget1:
    class: Acme\Widget1
    factory: ['@acme.widget_factory', createWidget]
    tags:
        - { name: acme.widget }

acme.widget2:
    class: Acme\Widget2
    factory: ['@acme.widget_factory', createWidget]
    tags:
        - { name: acme.widget }

acme.widgetN:
    class: Acme\WidgetN
    factory: ['@acme.widget_factory', createWidget]
    tags:
        - { name: acme.widget }

然后在DepencencyInjection \ AcmeDemoExtension.php

class AcmeDemoExtension implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        $widgets = $container->findTaggedServiceIds('acme.widget');
        foreach ($widgets as $id => $tags) {
            $definition = $container->getDefinition($id);
            $definition->setArguments([$definition->getClass()]);
        }
    }
}

最后在工厂......

class AcmeWidgetFactory
{
    //.....
    public static function createWidget($class) 
    {
        //.....
        return new $class(/*  dependencies */);
        //.....
    }
    //.....
}

因此,当您执行$this->get('acme.widget1')时,将调用以类名作为参数的工厂方法。 Factory已经拥有所有依赖项并且知道类实例化的逻辑。所以它完成所有工作并返回所需的小部件实例。

答案 1 :(得分:1)

这很简单。如果你要打电话

$nm = $container->get('app.newsletter_manager')

然后,工厂将自动创建新闻通讯管理器。