Symfony3工厂即服务

Jak*_*e N 3 service factory-pattern symfony

我一直试图解决这个问题

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

但是似乎有一块缺失让所有人都厌倦了或者我完全忽略了这一点.

该示例具有工厂类

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

        // ...

        return $newsletterManager;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

    app.newsletter_manager:
        class:   AppBundle\Email\NewsletterManager
        factory: 'app.newsletter_manager_factory:createNewsletterManager'
Run Code Online (Sandbox Code Playgroud)

所以现在我们有一个通过param NewsletterManager知道NewsletterManagerFactoryClass的Classfactory:services.yml

你如何利用这种配置?现在暴露在内的NewsletterManager是什么让我可以createNewsletterManager在工厂上课?

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

Ste*_*hka 7

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

试想一下,你有多个窗口小部件类,即Acme\Widget1,Acme\Widget2,Acme\WidgetN.

每个小部件都有高级实例化过程,因此您决定使用工厂.它还具有复杂的依赖关系链,每个窗口小部件都需要实例化.即Acme\Dependency1,Acme\Dependency2,Acme\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'] 
Run Code Online (Sandbox Code Playgroud)

在这里你有强烈的代码重复气味.如果你想改变一些你需要做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]
Run Code Online (Sandbox Code Playgroud)

代码重复已经消失.

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

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

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 }
Run Code Online (Sandbox Code Playgroud)

然后在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()]);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

最后在工厂......

class AcmeWidgetFactory
{
    //.....
    public static function createWidget($class) 
    {
        //.....
        return new $class(/*  dependencies */);
        //.....
    }
    //.....
}
Run Code Online (Sandbox Code Playgroud)

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


Raw*_*ner 1

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

$nm = $container->get('app.newsletter_manager')
Run Code Online (Sandbox Code Playgroud)

那么时事通讯管理器将由工厂自动创建。