Я использовал этот узор один раз. Вот пример использования.
Представьте, что у вас есть несколько классов виджетов, т. Е. 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')
фабричный метод с именем класса как параметр вызывается. Фабрика уже имеет все зависимости и знает логику создания экземпляра класса. Таким образом, он выполняет всю работу и возвращает требуемый экземпляр виджета.
Хорошо, что дело в том, что служба возвращает новый объект. Вместо самой службы? –
Точно. Служба (заводская) возвращает новую услугу или организацию или то, что когда-либо создавал ваш завод. – Rawburner
@JakeN, нет, возвращаемое значение заводского метода - это сервис. Сам заводский класс - это просто посредник, который не является прямым предметом. Однако вы можете написать отдельное определение услуги для завода, если это желательно. – Gerry