11

假设我有一项服务:

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!";
    }
}

我为它创建了一个可调用的

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)

更好的解决方案是为该服务创建工厂或在 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

谢谢

4

4 回答 4

13

ServiceManager默认情况下,它会注入到任何 ZF2 控制器中,因为它扩展了实现AbstractController接口ServiceLocatorAwareInterface

第二种方法具有“冗余”的形式,因为除了已经可以访问ServiceManager实例之外,每当您需要在控制器之间共享服务时,您都需要为每个控制器配置注入机制。由于您的控制器已经对 ServiceManager 具有依赖关系,因此使用第一种方法并将您的域相关服务注册到ServiceManager,从而集中对服务层的访问会更有意义。

注意:答案的以下部分可能超出了问题的范围,但它旨在提供原始答案的“隐藏”背景。

假设我们正在构建一个复杂的系统,在该系统中低耦合、可重用性和可测试性得到提升。我们的系统是多层的,我们构建了一切,直到服务层。请注意,到目前为止,我们还没有考虑“MVC”Web 层,甚至没有选择给定的框架。

在我们的服务层(我将考虑这一层,因为它是问题中提到的那个),我们假设我们采用了业务逻辑和对象图构造(或依赖解析)之间的分离原则。所以我们可能有几个通过工厂构建的复杂服务。

现在我们的服务层已经构建好了,我们决定将它“插入”到 ZF2 之上。自然,我们的服务应该可以从控制器访问,正如您的问题所说明的那样,我们有不止一种方法可以做到这一点。但是,我们希望避免冗余并利用我们已经构建的内容。让我们假设以下工厂:

//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;
    }

}

我们现在要做的只是调整(实际上扩展)我们的工厂,以便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 平台具有较低的依赖性。

于 2013-01-27T19:26:02.877 回答
4

非常有用的问题。他一再被抚养长大。我想你可以在这里得到一个准确的答案

于 2013-09-26T16:24:49.613 回答
3

向@yechabbis 答案添加一些内容:

的工厂模式ServiceConfiguration确实适用于复杂的基础设施,或者只是不使用closures/callable functions在配置中。这有两个原因:

  • 可读性/更简洁的代码
  • 表现

在里面设置工厂模式getServiceConfig很好,干净,快速。没有类被实例化,但会在调用服务密钥时实例化。但是,如果您使用闭包模式或可调用函数设置服务,那么这些类将始终在每次请求时实例化!

这可能只是一个示例,但您显示的代码也最好用作ViewHelper

于 2013-01-28T06:29:43.937 回答
0

我认为第二种方法更好,它使控制器类独立于 GreetingService。当您想使用另一个由控制器使用的问候服务时,此方法将很有用。您根本不需要更改控制器类中的代码,而是通过使用该工厂关闭更改服务管理器来做到这一点。

我相信这是控制反转或依赖注入的主要思想,所有的布线和配置都是在类之外完成的(在这种情况下:控制器类)。

所以,在我看来,这就是为什么第二种方法更好的原因。

于 2013-07-14T15:45:33.177 回答