【问题标题】:Why the Service Locator is a Anti-Pattern in the following example?为什么服务定位器在以下示例中是反模式?
【发布时间】:2012-02-19 04:08:42
【问题描述】:

我有一个 MVC 应用程序,其领域模型定义明确,包含实体、存储库和服务层。

为了避免必须在我的控制器中实例化我的服务类,从而使我的控制器与不适合它们的逻辑混淆,我创建了一个助手,充当一种Service Locator,但在阅读了一下之后,我意识到许多开发人员:

说服务定位器实际上是一个反模式。但我认为我的实现不是反模式。

他们认为服务定位器是反模式的原因是因为它隐藏了依赖项,但是,我注入了唯一的依赖项(实体管理器,并且这个依赖项可能不会改变,因为它在服务的签名中服务类所需的接口),在我实例化服务定位器时。

这是我的代码:

<?php

namespace App\Controller\Action\Helper;
use Zend_Controller_Action_Helper_Abstract as Helper,
    Doctrine\ORM\EntityManager;

/**
 * Service Locator Helper
 * @author JCM
 */
class Service extends Helper {

    /**
     * The actual EntityManager
     * @var \Doctrine\ORM\EntityManager
     */
    private $entityManager;

    /**
     * Services Namespace
     * @var string
     */
    private $ns;

    /**
     * @param EntityManager $entityManager
     * @param string $ns The namespace where to find the services
     */
    public function __construct( EntityManager $entityManager, $ns )
    {
        $this->entityManager = $entityManager;
        $this->ns = $ns;
    }

    /**
     * @param string $serviceName
     * @param array $options
     * @param string $ns
     */
    public function direct( $serviceName, array $options = array(), $ns = null )
    {
        $ns = ( (!$ns) ? $this->ns : $ns ) . '\\';
        $class = $ns . $serviceName;

        return new $class( $this->entityManager, $options );
    }

    /**
     * @param EntityManager $entityManager
     */
    public function setEntityManager( EntityManager $entityManager )
    {
        $this->entityManager = $entityManager;
    }

    /**
     * @return \Doctrine\ORM\EntityManager
     */
    public function getEntityManager()
    {
        return $this->entityManager;
    }

    /**
     * @param string $name
     */
    public function __get( $name )
    {
        return $this->direct( $name );
    }
}

向前端控制器注册动作助手:

//inside some method in the bootstrap
HelperBroker::addHelper( new App\Controller\Action\Helper\Service( $entityManager, '\App\Domain\Service' ) );

以及我如何在控制器中使用此 Helper:

//Some Controller
$myService = $this->_helper->service( 'MyService' ); //returns an instance of the class App\Domain\Service\MyService
$result = $myService->doSomethingWithSomeData( $this->getRequest()->getPost() );
//etc...
  • 我的实现是否正确?
  • 真的是反模式吗?
  • 我可能面临哪些问题?
  • 如何重构我的代码以消除这种反模式,但继续使用功能?

【问题讨论】:

    标签: php zend-framework dependency-injection anti-patterns service-locator


    【解决方案1】:

    我不认为您构建的内容确实实现了服务定位器模式。如果你有,那么某处就会有一个全局“注册表”。

    我看到的基本上是一个带有依赖项的工厂类 (App\Controller\Action\Helper\Service),它们是通过 ctor 注入的。所以你的类不知道它的依赖来自哪里,它也不负责创建它们(这是一件好事!)。

    如果我错了,请纠正我。 :)

    顺便说一句:这也是您不应该绕过依赖注入容器的原因。它变成了一个服务定位器。

    【讨论】:

    • 嗯,真的没有全局注册表(不考虑HelperBroker,这是一个像注册表一样工作的静态类),所以我有一个工厂类? :S 你能解释一下两者之间的主要区别(工厂类与服务定位器)吗?
    • 工厂类根据您传递的参数创建一些东西。你告诉工厂要创建什么,它总是会为相同的输入返回相同的输出。在您的示例中,您注入了 EM(通过 ctor)和服务名称/命名空间(通过方法)。也许这有帮助:stackoverflow.com/questions/1557781/…
    • @PhilippeGerber:我认为区分工厂和容器是值得的。通常,$factory-&gt;getObject($params) 形式的重复调用将产生所请求对象的不同实例。相反,容器通常有一个内部注册表,因此所有$container-&gt;getObject($params) 形式的调用都将返回 same 实例。不过,您是对的,(1)工厂和容器都是处理依赖注入的接线代码的好地方,(2)原始问题中的示例充当工厂。
    • @DavidWeinraub 谢谢两位的解释,我有点困惑,以为我在做的是服务定位器,但是更好地查看我的代码,根据解释,我可以看到我实际上正在使用工厂类。我以为我在使用一种模式,但实际上是在使用另一种模式,至少最终结果证明这是一件好事。 (我没有使用反模式)。 =)
    • @David Weinraub:是的。我在那里用了same这个词有点不清楚。工厂create,它们不是store的仓库。 :)
    猜你喜欢
    • 2012-09-07
    • 2014-05-26
    • 2018-12-13
    • 2020-07-08
    • 2017-06-17
    • 2019-11-03
    • 1970-01-01
    • 2010-10-28
    • 1970-01-01
    相关资源
    最近更新 更多