假设我有服务:
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中返回一个闭包并在控制器中创建一个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;
}
}
和
'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
?
感谢
答案 0 :(得分:12)
ServiceManager
默认注入任何ZF2控制器,因为它扩展了实现AbstractController
接口的ServiceLocatorAwareInterface
。
第二种方法的形式为“redundancy”,因为除了已经访问ServiceManager
实例之外,每当您需要在控制器之间共享服务时,您需要为每个实例配置一个他们的注射机制。由于您的控制器已经相对于ServiceManager具有依赖性,因此使用第一种方法并将域相关服务注册到ServiceManager
会更有意义,从而集中访问服务层。
注意:答案的以下部分可能超出了问题的范围,但它旨在提供原始答案的“隐藏”背景。 子>
让我们假设我们正在建立一个复杂的系统,其中低耦合,可重复使用和测试能力得到提升。我们的系统是多层的,我们在服务层之前构建了所有内容。请注意,到目前为止,我们还没有考虑“MVC”Web层,甚至没有选择给定的框架。
在我们的服务层内(我将这个层视为问题中提到的层),我们假设我们采用了业务逻辑和对象图构造(或依赖关系)之间的principle分离解析度)。所以我们可能有一些通过工厂构建的复杂服务。
现在我们的服务层已经构建完毕,我们决定将其“插入”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层(否则它将是Separation of Concerns违规和不必要的跨层耦合)。服务管理器+我们的“改编”工厂类在某种意义上是我们的依赖解析机制。事实是我们的系统仍然是可移植的,例如我们可以选择另一个系统(另一个框架)并且相对于MVC平台具有较低的依赖性。
答案 1 :(得分:4)
非常有用的问题。他一再被抚养长大。我想你可以得到一个准确的答案HERE
答案 2 :(得分:3)
向@yechabbis回答:
添加内容 ServiceConfiguration
的工厂模式确实适用于复杂的基础架构,或者只是在配置中不使用closures
/ callable functions
。这有两个原因:
在getServiceConfig
内设置工厂模式很好,干净,快速。没有实例化类,但是在调用service-key时也是如此。但是,如果使用闭包模式或可调用函数设置服务,则每次请求时都会实例化这些类!
这可能仅仅是一个示例,但您展示的代码也最适合作为ViewHelper
答案 3 :(得分:0)
我认为第二种方法更好,这使得控制器类独立于GreetingService。当您想要使用控制器使用的另一个问候语服务时,此方法非常有用。您根本不需要更改控制器类中的代码,而是通过使用该工厂闭包更改服务管理器来实现此目的。
我认为这是控制或依赖注入反转的主要思想,所有布线和配置都在类外部完成(在本例中为:控制器类)。
因此,在我看来,这就是为什么第二种方法更好的原因。