使用“工厂”来创建服务

3.4 版本
维护中的版本

全新的setFactory()方法从Symfony2.6起被引入。“工厂”相关语法也适用于2.6之前的老版本框架。

Symfony的服务容器提供了一个强有力的方式来控制对象建立,它允许你指定传给构造器的参数,以及调用setter方法并设置parameter参数。然而有些时候,以上方式仍然不能提供给你构造对象所需的每样东西。基于此种情形,你需要使用工厂方法来创建对象,并告诉服务容器去调用一个工厂方法,而不是直接实例化一个类。

假设你有一个工厂,用于配置并返回一个新的NewsletterManager对象:

1
2
3
4
5
6
7
8
9
10
11
class NewsletterManagerFactory
{
    public static function createNewsletterManager()
    {
        $newsletterManager = new NewsletterManager();
 
        // ...
 
        return $newsletterManager;
    }
}

为了让NewsletterManager对象能够成为一个服务,你需要配置服务容器,以便能够使用工厂方法NewsletterManager::createNewsletterManager():

1
2
3
4
services:
    newsletter_manager:
        class:   NewsletterManager
        factory: [NewsletterManagerFactory, createNewsletterManager]
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
 
    <services>
        <service id="newsletter_manager" class="NewsletterManager">
            <factory class="NewsletterManagerFactory" method="createNewsletterManager" />
        </service>
    </services>
</container>
1
2
3
4
5
6
7
use Symfony\Component\DependencyInjection\Definition;
 
// ...
$definition = new Definition('NewsletterManager');
$definition->setFactory(array('NewsletterManagerFactory', 'createNewsletterManager'));
 
$container->setDefinition('newsletter_manager', $definition);

当使用工厂创建服务时,class选项的值并不能影响到生产出来的服务。真实的类名只依赖于工场返回的对象。不过,这里配置的类名可能会被compiler pass所使用,因此还是赋与其一个有意义的名字比较好。

现在,上面的工厂方法将被静态调用。如果工厂类自身要求被实例化,进而调用对象里的相应方法,这时工厂自身应被设置为一个服务。同时,方法要改为“非静态”的(如get这种)。

1
2
3
4
5
6
services:
    newsletter_manager.factory:
        class: NewsletterManagerFactory
    newsletter_manager:
        class:   NewsletterManager
        factory: ["@newsletter_manager.factory", createNewsletterManager]
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
 
    <services>
        <service id="newsletter_manager.factory" class="NewsletterManagerFactory" />
 
        <service id="newsletter_manager" class="NewsletterManager">
            <factory service="newsletter_manager.factory" method="createNewsletterManager" />
        </service>
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Definition;
 
// ...
$container->register('newsletter_manager.factory', 'NewsletterManagerFactory');
 
$newsletterManager = new Definition();
$newsletterManager->setFactory(array(
    new Reference('newsletter_manager.factory'),
    'createNewsletterManager'
));
$container->setDefinition('newsletter_manager', $newsletterManager);

传递参数给“工厂方法” 

如果你需要传递参数给工厂方法,可以在服务容器中使用aruguments选项。例如,假设前面的createNewsletterManager方法需要一个templating服务作为参数:

1
2
3
4
5
6
7
8
9
services:
    newsletter_manager.factory:
        class: NewsletterManagerFactory

    newsletter_manager:
        class:   NewsletterManager
        factory: ["@newsletter_manager.factory", createNewsletterManager]
        arguments:
            - '@templating'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
 
    <services>
        <service id="newsletter_manager.factory" class="NewsletterManagerFactory"/>
 
        <service id="newsletter_manager" class="NewsletterManager">
            <factory service="newsletter_manager.factory" method="createNewsletterManager"/>
            <argument type="service" id="templating"/>
        </service>
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Definition;
 
// ...
$container->register('newsletter_manager.factory', 'NewsletterManagerFactory');
 
$newsletterManager = new Definition(
    'NewsletterManager',
    array(new Reference('templating'))
);
$newsletterManager->setFactory(array(
    new Reference('newsletter_manager.factory'),
    'createNewsletterManager'
));
$container->setDefinition('newsletter_manager', $newsletterManager);

本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。

登录symfonychina 发表评论或留下问题(我们会尽量回复)