使用服务配置器来配置服务
服务配置器(Service Configurator)是依赖注入容器(DI Container)的一个功能,它提供一个“回调”的机会,使你能够在服务被实例化之后改变其配置。
你可以在另一个服务中指定一个方法、一个PHP函数,或是一个静态方法。原服务的实例被传递到这个回调中,再由配置器做一些它该做的事——即,在服务被创建之后修改配置。
服务配置器可以被用于,举例来说,当你有一个需要进行复杂配置的服务,其配置信息来自于不同的资源/服务时。使用一个外部配置器,你可以令该服务保持不受干扰的可执行性,同时令它与其他那些“用来提供配置信息”的对象保持松藕合。
另一个有趣的使用场景是,当你有多个对象需要共享同一个配置信息,或是在运行时需要以某种相似方式进行配置的时候。
例如,假设你有一个程序是用来发送不同类型的邮件到用户处。邮件被发送时是有不同格式的,控制格式的开启和关闭是依靠一些动态的程序设定。你可以先定义NewsletterManager
类如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class NewsletterManager implements EmailFormatterAwareInterface
{
protected $mailer;
protected $enabledFormatters;
public function setMailer(Mailer $mailer)
{
$this->mailer = $mailer;
}
public function setEnabledFormatters(array $enabledFormatters)
{
$this->enabledFormatters = $enabledFormatters;
}
// ...
} |
然后是另一个GreetingCardManager
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class GreetingCardManager implements EmailFormatterAwareInterface
{
protected $mailer;
protected $enabledFormatters;
public function setMailer(Mailer $mailer)
{
$this->mailer = $mailer;
}
public function setEnabledFormatters(array $enabledFormatters)
{
$this->enabledFormatters = $enabledFormatters;
}
// ...
} |
如前述,我们的目标是要在运行时根据程序的设置来设定好格式。为了实现这个,你要有一个EmailFormatterManager
类,用于负责加载并验证已经在程序中开启的格式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class EmailFormatterManager
{
protected $enabledFormatters;
public function loadFormatters()
{
// code to configure which formatters to use
// 配置哪些formatter可用的代码
$enabledFormatters = array(...);
// ...
$this->enabledFormatters = $enabledFormatters;
}
public function getEnabledFormatters()
{
return $this->enabledFormatters;
}
// ...
} |
如果你的目标是避免NewsletterManager
和GreetingCardManager
,同EmailFormatterManager
发生藕合,你应该创建一个配置器类,来配置这些类的实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class EmailConfigurator
{
private $formatterManager;
public function __construct(EmailFormatterManager $formatterManager)
{
$this->formatterManager = $formatterManager;
}
public function configure(EmailFormatterAwareInterface $emailManager)
{
$emailManager->setEnabledFormatters(
$this->formatterManager->getEnabledFormatters()
);
}
// ...
} |
EmailConfigurator
类的工作就是注入已经开启的格式过滤器到NewsletterManager
和GreetingCardManager
中,因为后二者并不关心“被开启的格式过滤器从何而来”。换句话说,本着“单一职责”的原则,EmailFormatterManager
才是拥有“获知已开启的格式”和“如何加载这些格式”等相关信息的类。
配置器的服务配置 ¶
前面提到的类,它们的服务配置可能是下面这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
services:
my_mailer:
# ...
email_formatter_manager:
class: EmailFormatterManager
# ...
email_configurator:
class: EmailConfigurator
arguments: ['@email_formatter_manager']
# ...
newsletter_manager:
class: NewsletterManager
calls:
- [setMailer, ['@my_mailer']]
configurator: ['@email_configurator', configure]
greeting_card_manager:
class: GreetingCardManager
calls:
- [setMailer, ['@my_mailer']]
configurator: ['@email_configurator', configure] |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?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="my_mailer">
<!-- ... -->
</service>
<service id="email_formatter_manager" class="EmailFormatterManager">
<!-- ... -->
</service>
<service id="email_configurator" class="EmailConfigurator">
<argument type="service" id="email_formatter_manager" />
<!-- ... -->
</service>
<service id="newsletter_manager" class="NewsletterManager">
<call method="setMailer">
<argument type="service" id="my_mailer" />
</call>
<configurator service="email_configurator" method="configure" />
</service>
<service id="greeting_card_manager" class="GreetingCardManager">
<call method="setMailer">
<argument type="service" id="my_mailer" />
</call>
<configurator service="email_configurator" method="configure" />
</service>
</services>
</container> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
// ...
$container->setDefinition('my_mailer', ...);
$container->setDefinition('email_formatter_manager', new Definition(
'EmailFormatterManager'
));
$container->setDefinition('email_configurator', new Definition(
'EmailConfigurator'
));
$container->setDefinition('newsletter_manager', new Definition(
'NewsletterManager'
))->addMethodCall('setMailer', array(
new Reference('my_mailer'),
))->setConfigurator(array(
new Reference('email_configurator'),
'configure',
)));
$container->setDefinition('greeting_card_manager', new Definition(
'GreetingCardManager'
))->addMethodCall('setMailer', array(
new Reference('my_mailer'),
))->setConfigurator(array(
new Reference('email_configurator'),
'configure',
))); |