ServiceLocator,让我们在ZF2上下文中考虑它

时间:2015-11-03 10:06:00

标签: php architecture zend-framework2

根据Marco的Pivetta this的想法,这old question 以及我对an other question

的回答

我正在询问自己在Zend Framework 2应用程序中使用我们服务的更好方法。

实际上,我们可以将ServiceLocatorAwareInterfaceServiceLocatorAwareTrait结合使用。 事实In ZF3 service locator will be removed in controller他们可能也会删除这个界面,或建议不使用它的人,这是有道理的。

我看到我们的服务如何构建的唯一方法是:

  

请勿在服务中使用ServiceLocator,请使用DependancyInjection。

问题是:

有些项目非常大,你要么:

  • 一个工作流程的15个服务类。
  • 服务类有15个Dependancies。
  • 选择你的噩梦......

服务中可能需要的一些示例:

  • 取回formManager(您无法在控制器中调用它)
  • 在通过AJAX和JSON响应将HTML字符串返回到视图之前,您可能需要让ViewRenderer呈现模板;
  • 您可能需要取回翻译人员或ZF2提供的所有服务
  • 获取您的实体经理,如果您有多个数据库,请在此处添加计数
  • 获取其他服务,如MailService,ExportService,ImportService等......
  • 如果你必须加载特定服务取决于客户端(BtoB中的多客户端网站...添加一些服务,因为你无法加载|调用AbstractFactory)

也许对于其中的一些观点,他们可以通过我不知道的技巧来解决。

  

我的问题是:

     

对于一项服务,拥有15个或更多Dependancies是一个好习惯   并放弃控制器中的ServiceLocator,还放弃服务?

根据评论进行修改

为了说明我的观点,我粘贴了一个构造函数:

public function __construct(
    ToolboxService $toolboxService,
    EntityService $entityService,
    UserService $userService,
    ItemService $itemService,
    CriteriaService $criteriaService,
    Import $import,
    Export $export,
    PhpRenderer $renderer
) {
    $this->toolboxService = $toolboxService;
    $this->entityService = $entityService;
    $this->userService = $userService;
    $this->emOld = $this->toolboxService->getEmOld();
    $this->emNew = $this->toolboxService->getEmNew();
    $this->serviceLocator = $this->toolboxService->getServiceLocator();
    $this->itemService = $itemService;
    $this->criteriaService = $criteriaService;
    $this->import = $import;
    $this->export = $export;
    $this->renderer = $renderer;
    $this->formManager = $this->toolboxService->getFormManager();
}

如您所见,ToolboxService本身就是一个具有多个依赖项的对象。此服务位于我的Application文件夹中,几乎无处不在。 我有2个实体经理(连接到2个数据库,但可能很快,我将需要第三个......)

您可以看到我通过依赖使用serviceLocator,因此该服务不实现ServiceLocatorAwareInterface。如果我没有使用它,我可以用

搞砸我的AbstractFactory调用
// Distribute somes orders depends on Clients
$distributionClass = $this->serviceLocator->get(ucfirst($param->type));
            if ($distributionClass instanceof DistributeInterface) {
                $distributionClass->distribute($orders, $key);
            } else {
                throw new \RuntimeException("invalid_type_provided", 1);
            }

1 个答案:

答案 0 :(得分:3)

假设您要注入ServiceLocator实例。无法保证ServiceLocator实际上保留了您的硬依赖关系,从而打破了DI模式。使用构造函数依赖项注入时,您确信所需的所有服务都是真正可用的。如果没有,那么服务的构建就会失败。

使用ServiceLocator时,您将最终进入实例化的服务类,其中ServiceLocator可能会或可能不会提供硬依赖项。这意味着您必须编写所有类型的附加逻辑(检查依赖项,抛出异常),以防在您要求时无法从ServiceLocator实例解析依赖项。编写所有这些代码可能会产生更多的工作,然后注入15个依赖项,最重要的是逻辑将在整个服务中混乱。

此外,您仍需要添加所有setter和getter方法,才能从ServiceLocator获取服务并使您的服务可以测试。

IMHO注入15个依赖项代码更少,更容易维护,然后注入ServiceLocator实例。