4

У меня есть приложение MVC с хорошо известной моделью домена, с объектами, репозиториями и уровнем обслуживания.Почему локатор сервисов является Anti-Pattern в следующем примере?

Чтобы избежать необходимости создавать классы обслуживания внутри моих контроллеров и, таким образом, беспорядок мои контроллеры с логикой, которая им не подходит, я создал помощник, который действует как своего рода Service Locator, но после прочтения я понял что многие разработчики:

Скажите, что Service Locator на самом деле является анти-шаблоном. Но я считаю, что моя реализация не является анти-шаблоном.

Причина, по которой они считают, что локатор сервисов является анти-шаблоном, потому что он скрывает зависимости, однако я ввожу только зависимость (диспетчер сущностей, и эта зависимость, вероятно, не изменится, поскольку она находится в сигнатуре Service), требуемый классом службы, в то время, когда я создаю экземпляр 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); 
    } 
} 

Регистрация Действие Helper с Front Controller:

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

И как я использую этот помощник в моих контроллеров:

//Some Controller 
$myService = $this->_helper->service('MyService'); //returns an instance of the class App\Domain\Service\MyService 
$result = $myService->doSomethingWithSomeData($this->getRequest()->getPost()); 
//etc... 
  • Моя реализация верна?
  • Это действительно анти-шаблон?
  • Каковы возможные проблемы, с которыми я могу столкнуться?
  • Как я могу реорганизовать свой код, чтобы устранить этот анти-шаблон, но продолжите функционировать?

ответ

4

Я не думаю, что то, что вы создали, реализует шаблон локатора сервисов. Если бы это было так, там был бы глобальный «реестр».

Что я вижу в основном заводского класса (App\Controller\Action\Helper\Service) с зависимостями, которые вводятся через ctor. Поэтому ваш класс не знает, откуда взялись его зависимости, и он также не несет ответственности за их создание (что хорошо!).

Исправьте меня, если я ошибаюсь. :)

BTW: Это также является причиной того, почему вы не должны проходить вокруг контейнера для инъекций. Он превращается в локатор сервисов.

+0

Hum, Действительно, нет глобального реестра (без учета HelperBroker, который является статическим классом, который работает как реестр), так что у меня есть фабричный класс? : S Можете ли вы объяснить основные отличия между ними (заводская класса против локатора сервисов)? – JCM

+3

Класс фабрики создает что-то, основанное на параметрах, которые вы передаете. Вы сообщаете фабрике, что создавать, и она всегда будет возвращать один и тот же результат для одного и того же ввода.В примере вы вводите EM (через ctor) и имя/пространство имен службы (посредством метода). Возможно, это помогает: http://stackoverflow.com/questions/1557781/whats-the-difference-between-the-dependency-injection-and-service-locator-patte –

+3

@PhilippeGerber: Я думаю, что стоит отличать завод от контейнер. Как правило, повторные вызовы формы '$ factory-> getObject ($ params)' будут создавать отдельные экземпляры запрашиваемого объекта. Напротив, контейнер обычно имеет внутренний реестр, так что все вызовы формы '$ container-> getObject ($ params)' возвращают экземпляр _same_. Тем не менее, вы правы в том, что (1) обе фабрики и контейнеры являются хорошими домами для кода подключения, который обрабатывает инъекцию зависимостей, и (2) пример в исходном вопросе функционирует как фабрика. –

Смежные вопросы