为什么在ZF2上使用服务?

时间:2016-04-05 12:43:20

标签: php oop zend-framework2

我试图了解ZendFramework 2的一些原则,我想知道为什么要在ZF2 module.config.php 上注入服务,如果我可以使用加载实例的对象作曲家自动加载器。对于zend工厂来说,如果我可以轻松实现Factory类并在控制器中调用它,为什么要使用它?

2 个答案:

答案 0 :(得分:2)

要事先清楚 - ServiceLocator不是创建对象的must use方式。您仍然可以直接创建对象,在很多情况下,这是一种非常有效的方法。

话虽如此,有几个原因可以解释为什么要将ServiceLocator用于某些对象。不幸的是,Wikipedia Page对此并不太了解使用服务定位器的原因。我发现说明和示例here更有用。

对于余下的内容,我会使用classservice这两个词来互换。我将要求服务的类/对象命名为Consumer,工厂负责创建服务factory

了解服务经理有一个非常重要的事实。服务名称需要是类名。它可以是任何字符串(例如服务'名称)。

您要使用服务定位器的主要原因是:

  • 使用相同的服务实例和许多地方(共享服务)
  • 摘要来自消费者的服务方式constructions(由工厂创建或直接实例化)
  • 将模块相互耦合
  • 创建抽象服务(由任何字符串标识),让其他人决定此服务的具体实现
  • 使用委托人扩展服务创建

重复使用服务

这可能是最容易看到的。通常,您有一个服务,并希望在您的应用程序中使用相同的实例。用例可以是从Configuration服务到Caching Service的任何内容。

我们肯定可以使用Singleton来确保全局只创建一个实例 - 这也可以。但这并没有提供其他论据的好处。

在Zf2中,默认情况下共享所有服务。因此,每次按名称请求服务时,您都将获得相同的实例。这可以在每个服务级别上启用(shared配置中的service_manager配置密钥。

抽象创作方式 理想情况下,消费者不应该知道如何创建所需的服务。这使它与所需服务更加分离。所有它需要知道的,就是说I require an instance of this service

使用服务管理器,您可以实现此目的:您要求服务经理获取特定服务,但您并不关心服务管理器实际创建此服务的方式。可能是服务管理器直接实例化它。它可能是一个工厂。消费者并不关心 - 它只关心结果,即服务的一个实例。

这样做的好处是,您可以透明地替换创建该服务的方式,而不必将消费者代码更新为创建该服务的新方式。

解耦模块

隐藏服务创建方式会导致解耦模块。我们假设您有两个模块CacheUseruser模块需要Cache模块中的Cache服务。您不必直接依赖Cache模块和实现,而是通过名为cache的服务隐藏该事实。 user模块并不关心Cache模块如何创建该服务。事实上,我们甚至可以用提供相同服务的另一个模块Cache替换AdvancedCache模块,而无需更新User模块。 User模块与特定模块无关 - 只存在名为cache的服务。

抽象服务名称

在前面的例子中,我已经介绍了这个概念。每个服务都有一个全局名称 - 一个简单的字符串。在许多情况下,这是类名,但我们不限于此。

坚持上一个cache服务示例:在这里,我们没有指定User模块实际需要哪种类型的缓存 - 它并不关心。它可以是array(内存中)缓存,file缓存或memcache缓存。

在其他地方,我们决定要全局使用哪个缓存,并将该具体实现分配给cache的名称。所有需要cache服务的模块现在都可以自动接收该实现而无需更新。

在Zf2中,这被广泛使用。以Request服务为例。在许多情况下,这是Http\Request的一个实例。但是在使用CLI时,这将是Console\Request。但是,在请求Request服务时,我们实际上并不关心哪一个服务,只要它们的行为相同。

另一个例子是Renderer。根据我们想要为其呈现输出的环境,我们会收到不同的实现(HTML / Json / Console用法)。但我们的消费者并不需要知道我们为/.// p>呈现的具体实现

<强>委托者

Delegators可用于extend工厂。我现在暂时将其排除在范围之外,因为这是一个更高级的用例。可以这么说,您可以在创建时扩展/代理服务创建。

答案 1 :(得分:0)

完成@Fge回答iI将向您展示一个示例,我自己试验了服务管理器如何有用,何时不是。

我在一些元素上创建了一个动态表单,我必须添加一些过滤器和一些验证器。

我向您展示了我的自定义服务方法,为我完成这项工作:

public function setValidators($form, $name, $validator, $nameFieldset)
{
    $optionsSpace = [
        'translator' => $this->translator,
        'type' => NotEmpty::SPACE
    ];
    $optionsString = [
        'translator' => $this->translator,
        'type' => NotEmpty::STRING
    ];
    $optionsDigits = [
        'translator' => $this->translator,
    ];
    $form->getInputFilter()->get('items')
        ->get($nameFieldset)
        ->get($name)
        ->setRequired(true)
        ->getValidatorChain()
        ->attach($validator, true, 1)
        ->attach(new NotEmpty($optionsSpace), true, 2)
        ->attach(new NotEmpty($optionsString), true, 2)
        ->attach(new Digits($optionsDigits), true, 2);
    return $form;
}

你可以看到我使用了一些数组,还有一些Zend \ Validators。 您还看到我在传递给那些验证器的每个选项数组上使用$this->translator。为什么我通过翻译?我们来看一下验证器:

class NotEmpty extends AbstractValidator

抽象的验证器

abstract class AbstractValidator implements
    Translator\TranslatorAwareInterface,
    ValidatorInterface

如果您使用服务管理器,TranslatorAwareInterface将由初始化程序实例化。如果你直接在你的服务中声明了新的验证器,那么情况并非如此,所以你必须在你的选项中设置它。