【问题标题】:ServiceLocator, let's thoughts about it in ZF2 contextServiceLocator,让我们在 ZF2 的背景下思考它
【发布时间】:2016-02-03 10:30:31
【问题描述】:

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

我一直在问自己如何在 Zend Framework 2 应用程序中使用我们的服务。

实际上我们可以将ServiceLocatorAwareInterfaceServiceLocatorAwareTrait 结合使用。 鉴于In ZF3 service locator will be removed in controller 的事实,他们可能还会删除此接口,或者建议不使用它的人,这是有道理的。

我了解如何构建我们的服务的唯一方法是:

不要在你的服务中使用 ServiceLocator,使用 DependancyInjection。

问题是:

有些项目太大了,你有:

  • 一个工作流的 15 个服务类。
  • 具有 15 个依赖项的服务类。
  • 选择你的噩梦...

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

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

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

我的问题是:

为一项服务拥有 15 个或更多依赖项是否是一种好习惯 并放弃 ServiceLocator,在控制器中,也在服务中?

从 cmets 编辑

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

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 本身就是一个具有多个依赖项的对象。该服务在我的应用程序文件夹中,几乎无处不在。 我有 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);
            }

【问题讨论】:

  • 你能给出一个有 15 个硬依赖的服务的具体例子吗?我会说,如果是这样的话,应该更多地以不同的方式组织事情......
  • 组织的问题,我已经有想法了。当您拥有庞大的应用程序,经过多年的开发,再加上多客户端应用程序,您必须使用通用工作流程处理所有这些应用程序,然后处理特定性。ServiceLocator 可以很好地实现这一目标
  • 我认为您可以将这些 UserService|ItemService|EntityService 分组为 ResourceServiceImport|Export 中的 ExchangeServiceTransportService。然后你有5个依赖项。但也许这不是您正在等待的评论:D
  • 将依赖分组并不是解决方案,因为每个服务可以相互独立,甚至属于不同的模块。用户和项目分为 2 个不同的模块。我的模块订单有 3 个依赖项:应用程序/用户/项目。另外,分组依赖关系在架构方面没有更多作用,它只是隐藏了混乱。
  • 我在这里同意@Wilt;您可以进一步将多个服务封装为一个,从而减少依赖项的数量。这些新服务将只是相关服务的容器(也是注入的),因此它们仍然可以单独使用。

标签: php architecture zend-framework2


【解决方案1】:

假设您将注入 ServiceLocator 实例。不能保证 ServiceLocator 实际上持有你的硬依赖,从而打破了 DI 模式。使用构造函数依赖注入时,您可以确定所需的所有服务都是真正可用的。否则,服务的构建将失败。

当使用ServiceLocator 时,您最终会进入一个实例化的服务类,其中硬依赖可能会或可能不会通过ServiceLocator 获得。这意味着您必须编写所有类型的附加逻辑(检查依赖关系,抛出异常),以防在您请求时无法从 ServiceLocator 实例解决依赖关系。编写所有这些代码可能会比注入 15 个依赖项要多得多,除此之外,您的服务中的逻辑将变得杂乱无章。

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

恕我直言,注入 15 个依赖项与注入 ServiceLocator 实例相比,代码更少且更易于维护。

【讨论】:

  • 感谢您的回答,但您错过了一些事情:调用抽象工厂来处理所有客户端特定性怎么样?
  • @Hooli,当然,您可以使用普通工厂和抽象工厂来创建服务实例,但是我猜您的构造函数中仍然有 DI 和 15 个依赖项?还是你的意思不同?
  • 哼,也许我的观点没有说清楚。通过 AbstractFactory 调用,我的意思是在我的服务中,我为特定客户端调用另一个脚本,并且该调用是通过使用具有动态给定名称的 AbstractFactory 进行的。看看我粘贴的最后一段代码。
  • @Hooli。如果你说抽象工厂,我会想到the ZF2 abstract_factories。您提出的这个自定义代码/解决方案不是您项目中新的 ZF2 程序员可以理解/期望的。
  • @Hooli 你没有接受答案。这不是您想要的吗?
猜你喜欢
  • 2013-09-12
  • 2015-09-11
  • 1970-01-01
  • 1970-01-01
  • 2016-08-01
  • 2015-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多