Зачем использовать Service Manager в Zend Framework 2?

говорит, что у меня есть сервис:

namespace Helloworld\Service;

class GreetingService
{
    public function getGreeting()
    {
        if(date("H") <= 11)
            return "Good morning, world!";
        else if (date("H") > 11 && date("H") < 17)
            return "Hello, world!";
        else
            return "Good evening, world!";
    }
}

и я создаю для него invokable

public function getServiceConfig()
{
    return array(
        'invokables' => array(
            'greetingService'
                => 'Helloworld\Service\GreetingService'
        )
    );
}

то в моем контроллере я мог бы сделать:

public function indexAction()
{
    $greetingSrv = $this->getServiceLocator()
        ->get('greetingService');

    return new ViewModel(
        array('greeting' => $greetingSrv->getGreeting())
    );
}

предположительно это делает контроллер зависимым от службы (и ServiceManager)

и лучшим решением является создание factory для этой службы или возврат замыкания в ServiceManager и создание установщика в контроллере:

class IndexController extends AbstractActionController
{
    private $greetingService;

    public function indexAction()
    {

        return new ViewModel(
            array(
                'greeting' => $this->greetingService->getGreeting()
            )
        );
    }

    public function setGreetingService($service)
    {
        $this->greetingService = $service;
    }
}

и

'controllers' => array(
    'factories' => array(
        'Helloworld\Controller\Index' => function($serviceLocator) {
            $ctr = new Helloworld\Controller\IndexController();

            $ctr->setGreetingService(
                $serviceLocator->getServiceLocator()
                    ->get('greetingService')
            );

            return $ctr;
        }
    )
)

Мой вопрос - почему? Почему второй подход лучше первого? и что это значит, что controller is dependent of the service?

спасибо

Ответ 1

ServiceManager по умолчанию вводится в любой контроллер ZF2, поскольку он расширяет AbstractController, реализуя интерфейс ServiceLocatorAwareInterface.

Второй подход имеет форму " redundancy", потому что, помимо уже имеющего доступ к экземпляру ServiceManager, всякий раз, когда вам нужно делиться ваш сервис среди ваших контроллеров, вам нужно настроить для каждого из них механизм впрыска. Поскольку у ваших контроллеров уже есть зависимость от ServiceManager, было бы разумнее использовать первый подход и зарегистрировать ваши связанные с доменом службы в ServiceManager, централизуя таким образом доступ к уровню службы.

Примечание. Следующая часть ответа может выйти за рамки вопроса, но она направлена ​​на предоставление "скрытого" фона исходного.Суб >

Предположим, что мы строим сложную систему, в которой продвигаются низкоуровневые, повторно используемые и тестовые возможности. Наша система многослойна, и мы построили все до уровня обслуживания. Обратите внимание, что до сих пор мы еще не рассматривали веб-уровень "MVC" или даже выбирали определенную инфраструктуру.

В пределах нашего уровня обслуживания (я рассмотрю этот уровень, как он упоминается в вопросе), мы предполагаем, что мы приняли принцип разделения между бизнес-логикой и построением диаграммы объектов (или разрешением зависимостей). Таким образом, у нас есть, вероятно, несколько сложных сервисов, которые создаются на заводе.

Теперь, когда наш сервисный уровень построен, мы решили "подключить" его выше ZF2. Естественно, наши сервисы должны быть доступны из контроллеров, так как ваш вопрос иллюстрирует это, у нас есть больше, чем способ сделать это. Однако мы хотим избежать избыточности и использовать то, что мы уже создали. Предположим, что factory:

//The following class is a part of our Service layer
public class ComplexServiceFactory{

    private dependencyA;
    private dependencyB;

    public function buildComplexService()
    {
        $service = new ComplexService();
        $service->setDependencyA($this->dependecyA);
        $service->setDependencyB($this->dependecyB);
        return $service;
    }

}

Теперь нам нужно только отрегулировать (на самом деле расширить) наш factory, чтобы он мог использоваться логикой ServiceManager. Этот класс можно рассматривать как часть механизма, используемого для "подключения" нашей системы к ZF2 (это фактически Adapter)

public class SMComplexServiceFactory extends ComplexServiceFactory implements
    Zend\ServiceManager\FactoryInterface
{

    public function createService(ServiceLocatorInterface $sm)
    {
        $this->setDependencyA($sm->get('dependecyA'));
        $this->setDependencyB($sm->get('dependecyB'));
        return parent::buildComplexService;
    }

}

Таким образом, мы не создаем конструкцию графа объекта для слоя MVC (иначе это было бы Разделение проблем нарушение и ненужная межслойная связь). Сервис-менеджер + наши классы "Адаптированные" фабрики в некотором смысле являются нашим механизмом разрешения зависимостей. Дело в том, что наша Система по-прежнему переносима, мы могли бы, например, выбрать другую систему (другую структуру) и иметь низкую зависимость от платформы MVC.

Ответ 2

Очень полезный вопрос. Он неоднократно поднимался. Я думаю, вы можете получить ТОЧНЫЙ ОТВЕТ ЗДЕСЬ

Ответ 3

Чтобы добавить что-нибудь в ответ @yechabbis:

Шаблон factory для ServiceConfiguration действительно предназначен для сложной инфраструктуры или просто не использовать closures/callable functions внутри конфигурации. Это происходит по двум причинам:

  • Код чтения/очистки
  • Производительность

Настройка шаблона factory внутри getServiceConfig приятная, чистая и быстрая. Никакие классы не создаются, но при вызове ключевого ключа службы. Если, однако, вы настраиваете службы, используя шаблон закрытия или вызываемую функцию, тогда эти классы будут всегда создаваться при каждом отдельном запросе!

Это, вероятно, всего лишь пример, но код, который вы показали, также лучше всего будет служить ViewHelper

Ответ 4

Я думаю, что второй подход лучше, что делает класс контроллера независимым от GreetingService. Этот подход будет полезен, если вы хотите использовать другую службу приветствия, которая будет потребляться контроллером. Вам вообще не нужно менять код в классе контроллера, вместо этого вы делаете это, меняя диспетчер служб с закрытием factory.

Я считаю, что это основная идея инверсии управления или инъекции зависимостей, все проводки и конфигурация выполняются вне класса (в данном случае: класс контроллера).

Итак, по моему мнению, причина, по которой второй подход является лучшим.