ServiceLocator,让我们在ZF2上下文中对它进行思考

Gre*_*han 5 php architecture zend-framework2

根据马可的Pivetta看法这个,这个老问题 和我的回答的另一个问题

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

实际上我们可以ServiceLocatorAwareInterface结合使用ServiceLocatorAwareTrait.事实上在ZF3中,服务定位器将被移除在控制器中它们可能也会删除此接口,或建议不使用它的人,这是有道理的.

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

不要在服务中使用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();
}
Run Code Online (Sandbox Code Playgroud)

如您所见,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);
            }
Run Code Online (Sandbox Code Playgroud)

Wil*_*ilt 3

假设您要注入ServiceLocator实例。无法保证ServiceLocator实际保存您的硬依赖项,从而破坏了 DI 模式。使用构造函数依赖注入时,您可以确保所需的所有服务确实可用。否则,服务的构建就会失败。

当使用 a 时,ServiceLocator您最终将进入一个实例化的服务类,其中硬依赖项可能通过ServiceLocator. 这意味着您必须编写所有类型的附加逻辑(检查依赖关系、抛出异常),以防在ServiceLocator您请求时无法从实例解析依赖关系。编写所有这些代码可能比注入 15 个依赖项要工作得多,而且最重要的是,整个服务中的逻辑将变得混乱。

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

恕我直言,注入 15 个依赖项比注入实例代码更少,更容易维护ServiceLocator