Php 为什么在Zend Framework 2中使用服务管理器?

Php 为什么在Zend Framework 2中使用服务管理器?,php,service,zend-framework2,factory-pattern,service-management,Php,Service,Zend Framework2,Factory Pattern,Service Management,假设我有一项服务: namespace Helloworld\Service; class GreetingService { public function getGreeting() { if(date("H") <= 11) return "Good morning, world!"; else if (date("H") > 11 && date("H") < 17)

假设我有一项服务:

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 indexAction()
{
    $greetingSrv = $this->getServiceLocator()
        ->get('greetingService');

    return new ViewModel(
        array('greeting' => $greetingSrv->getGreeting())
    );
}
假设这使控制器依赖于服务(和ServiceManager)

更好的解决方案是为该服务创建工厂,或者在ServiceManager中返回闭包,并在控制器中创建setter:

class IndexController extends AbstractActionController
{
    private $greetingService;

    public function indexAction()
    {

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

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

我的问题是为什么?为什么第二种方法比第一种好?而
控制器依赖于服务的含义是什么


感谢默认情况下,
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的机制的一部分(实际上它是一个


通过这样做,我们不会将对象图构造带到MVC层(否则这将是一种冲突和不必要的跨层耦合)。服务管理器+我们的“适应”工厂类在某种意义上是我们的依赖项解析机制。事实上,我们的系统仍然是可移植的,例如,我们可以选择另一个系统(另一个框架),并且对MVC平台的依赖性较低

要在@yechabbis中添加内容,请回答:

ServiceConfiguration
的工厂模式确实适用于复杂的基础结构,或者干脆不在配置中使用
闭包
/
可调用函数
。这有两个原因:

  • 可读性/更清晰的代码
  • 演出
getServiceConfig
中设置工厂模式非常好、干净和快速。并没有实例化任何类,但会在调用服务键时进行实例化。但是,如果您使用闭包模式或可调用函数设置服务,那么在每次请求时,这些类将始终被实例化


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

我认为第二种方法更好,即使控制器类独立于GreetingService。当您希望使用另一个由控制器使用的问候服务时,这种方法将非常有用。您根本不需要更改控制器类中的代码,而是通过使用工厂闭包更改服务管理器来实现

我相信这是控制反转或依赖注入的主要思想,所有的连接和配置都在类之外完成(在本例中为控制器类)


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

非常有用的问题。他被反复抚养长大。我想你能得到准确的答案

这很奇怪。ZF2文档说,最好利用ServiceManager和工厂Closure。我认为,如果域服务具有复杂的依赖关系(您可能也会从SM检索到),并且您不想在控制器中使用工厂,则使用工厂是合适的。你能提供文档部分的链接吗?我恐怕不能,这是一本书店的例子,你是说如果我想让我的服务可用于其他控制器或模块,那么我应该使用第二种方法?不,使用
getServiceConfig
方法。您可以声明工厂(用于需要其他依赖项的复杂服务)或可调用(简单服务)。您可以在ZfcUser模块中找到示例:getter使用注入服务(如果可用),或者从serviceLocator获取一个。@Xerkus ZfcUser是一个非常糟糕的示例,因为它涉及到干净且性能良好的服务配置
'controllers' => array(
    'factories' => array(
        'Helloworld\Controller\Index' => function($serviceLocator) {
            $ctr = new Helloworld\Controller\IndexController();

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

            return $ctr;
        }
    )
)
//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;
    }

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

}