2016-12-16 2 views
2

Я пытаюсь получить мою голову вокруг этогоSymfony3 Factory, как службы

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

Но там, кажется, часть отсутствует, что шины все это вместе ИЛИ Я полностью отсутствует точка.

В примере имеет класс фабрики

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 в заводском классе?

Эти две службы по-прежнему полностью разделены, насколько я могу судить?

ответ

0

Это просто. Если вы позвоните по телефону

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

тогда менеджер рассылки будет автоматически создан на заводе-изготовителе.

+0

Хорошо, что дело в том, что служба возвращает новый объект. Вместо самой службы? –

+0

Точно. Служба (заводская) возвращает новую услугу или организацию или то, что когда-либо создавал ваш завод. – Rawburner

+1

@JakeN, нет, возвращаемое значение заводского метода - это сервис. Сам заводский класс - это просто посредник, который не является прямым предметом. Однако вы можете написать отдельное определение услуги для завода, если это желательно. – Gerry

2

Я использовал этот узор один раз. Вот пример использования.

Представьте, что у вас есть несколько классов виджетов, т. Е. 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'] 

Здесь у вас есть сильный запах дублирования кода. Если вы хотите что-то изменить, вам нужно сделать это 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') фабричный метод с именем класса как параметр вызывается. Фабрика уже имеет все зависимости и знает логику создания экземпляра класса. Таким образом, он выполняет всю работу и возвращает требуемый экземпляр виджета.

+0

Спасибо, что это полезно, и я понимаю, почему это удобно. Но я все еще не понимаю, почему вы добавляете параметр «factory» в службу. Например, служба 'acme.widget2' теперь имеет заводский параметр' ['@ acme.widget_factory', createWidget] ', но как это используется внутри класса' Acme \ Widget2'. Можете ли вы показать мне, что может выглядеть класс 'Acme \ Widget2'? –

+0

Ничего особенного. Просто класс с конструктором. Вся логика создания экземпляра перемещается на завод. Когда Symfony хочет создать экземпляр 'acme.widget2', он принимает экземпляр' acme.widget_factory' и вызывает его метод 'createWidget()' с именем класса виджетов в качестве параметра. Вся сложная логика создания и настройки происходит там. В конце 'createWidget()' возвращает готов к использованию экземпляр 'Acme \ Widget2'. – Stepashka

Смежные вопросы