4

zend-mvc版本2.7.0开始,ServiceLocatorAwareInterface被删除,$this->serviceLocator->get()调用在控制器内。在没有serviceLocator-> get()方法的情况下,ZF2中依赖注入的新方法效率更低?

这就是为什么前几天我做了大量的重构我的所有模块,通过使用工厂的构造函数注入所需的服务/对象主要是所有东西。

当然,我明白为什么这是更好/更干净的方式来做事情,因为现在依赖项更加可见。但另一方面:

这导致了大量的开销和更多未使用的类实例,不是吗?

让我们来看一个例子:

因为有依赖我所有的控制器中,我创建的工厂为他们所有。

CustomerControllerFactory.php

namespace Admin\Factory\Controller; 
class CustomerControllerFactory implements FactoryInterface { 
    public function createService(ServiceLocatorInterface $controllerManager) { 
     $serviceLocator = $controllerManager->getServiceLocator(); 
     $customerService = $serviceLocator->get('Admin\Service\CustomerService'); 
     $restSyncService = $serviceLocator->get('Admin\Service\SyncRestClientService'); 

     return new \Admin\Controller\CustomerController($customerService, $restSyncService); 
    } 
} 

CustomerController.php

namespace Admin\Controller; 

class CustomerController extends AbstractRestfulController { 
    public function __construct($customerService, $restSyncService) { 
     $this->customerService = $customerService; 
     $this->restSyncService = $restSyncService; 
    } 
} 

module.config.php

'controllers' => [ 
    'factories' => [ 
    'Admin\Controller\CustomerController' => 'Admin\Factory\Controller\CustomerControllerFactory', 
    ] 
], 
'service_manager' => [ 
    'factories' => [ 
    'Admin\Service\SyncRestClientService' => 'Admin\Factory\SyncRestClientServiceFactory', 
    ] 
] 

SyncRestClientServiceFactory.php

namespace Admin\Factory; 
class SyncRestClientServiceFactory implements FactoryInterface { 
    public function createService(ServiceLocatorInterface $serviceLocator) { 
     $entityManager = $serviceLocator->get('doctrine.entitymanager.orm_default'); 
     $x1 = $serviceLocator->get(...); 
     $x2 = $serviceLocator->get(...); 
     $x3 = $serviceLocator->get(...); 
     // ... 

     return new \Admin\Service\SyncRestClientService($entityManager, $x1, $x2, $x3, ...); 
    } 
} 

的SyncRestService是一个复杂的服务类,查询我们系统内部的一些服务器。它有很多依赖关系,并且如果请求发送到CustomerController,则始终创建它。但是这个同步服务是只有在CustomerController的syncAction()里面使用!在我使用syncAction()之前只需使用$this->serviceLocator->get('Admin\Service\SyncRestClientService'),所以只有在它被实例化之前。

通常看起来很多实例是通过工厂在每个请求中创建的,但大多数依赖关系不被使用。这是由于我的设计问题,还是“通过构造函数进行依赖注入”的正常副作用行为?

+0

我在这里问了几乎相同的问题:http://stackoverflow.com/questions/33496279/servicelocator-lets-thoughts-about-it-in-zf2-context – Hooli

回答

7

在我看来,这是通过构造函数进行依赖注入的正常效果。

我想你现在有两个选择(不是相互排斥的),以提高应用程序的工作原理:

  1. 分裂您的控制器,以便在需要的时候依赖只实例化。这肯定会导致更多班级,更多工厂等等,但是您的代码将获得更多的单一责任原则

  2. 您可以使用Lazy Services,这样即使某些服务是整个控制器的依赖关系,他们实际上只会在他们第一次被调用时被实例化(所以从来没有对他们没有被调用的行为!)

+0

好的,谢谢你的答案!我不确定我的设计中是否会遗漏一些要点。懒惰服务听起来很有趣。 – Moongazer

1

如果你只用SyncRestClientService控制器里面,你应该考虑从服务将其更改为控制器插件(或您在哪里注入控制器插件您SyncRestClientService)。
就像那样,你仍然可以在你的控制器syncAction方法中得到它,就像你之前做的一样。这正是ZF2控制器插件的目的。

首先,你需要创建(延长Zend\Mvc\Controller\Plugin\AbstractPlugin)控制器插件类:

<?php 
namespace Application\Controller\Plugin; 

use Zend\Mvc\Controller\Plugin\AbstractPlugin; 

class SyncPlugin extends AbstractPlugin{ 

    protected $syncRestClientService; 

    public function __constuct(SyncRestClientService $syncRestClientService){ 
     $this->syncRestClientService = $syncRestClientService 
    } 

    public function sync(){ 
     // do your syncing using the service that was injected 
    } 
} 

然后工厂在类中注入你的服务:

<?php 
namespace Application\Controller\Plugin\Factory; 

use Application\Controller\Plugin\SyncPlugin; 

class SyncPluginFactory implements FactoryInterface 
{ 
    /** 
    * @param ServiceLocatorInterface $serviceController 
    * @return SyncPlugin 
    */ 
    public function createService(ServiceLocatorInterface $serviceController) 
    { 
     $serviceManager = $serviceController->getServiceLocator(); 
     $syncRestClientService = $serviceManager>get('Admin\Service\SyncRestClientService'); 
     return new SyncPlugin($syncRestClientService); 
    } 
} 

然后,你需要注册你的插件在你的module.config.php

<?php 
return array(
    //... 
    'controller_plugins' => array(
     'factories' => array(
      'SyncPlugin' => 'Application\Controller\Plugin\Factory\SyncPluginFactory', 
     ) 
    ), 
    // ... 
); 

现在你可以SE这里面你的控制器动作是这样的:

protected function syncAction(){ 
    $plugin = $this->plugin('SyncPlugin'); 
    //now you can call your sync logic using the plugin 
    $plugin->sync(); 
} 

了解更多关于控制器插件here in the documentation

+0

谢谢,但如果它是一个控制器插件,它只与控制器绑定,我不能在其他服务中使用'SyncRestClientService',对吧?如果该服务仅用于控制器,则此解决方案可能是一种选择。 – Moongazer

+0

@Moongazer如果你更仔细地检查我的答案,你会发现该服务被注入到控制器插件中。这意味着您仍然可以将其用作普通服务。 – Wilt

0

也许你只需要一个依赖注入到控制器构造函数(在的ServiceManager实例)。我没有看到任何警察周围...

namespace Admin\Factory\Controller; 

class CustomerControllerFactory implements FactoryInterface { 

    public function createService(ServiceLocatorInterface $controllerManager) 
    { 
     $serviceLocator = $controllerManager->getServiceLocator(); 

     return new \Admin\Controller\CustomerController($serviceLocator); 
    } 

} 
+0

这是再次糟糕的做法,就像直到今天在许多模块中所做的一样。 – Moongazer

+0

我认为糟糕的做法是依赖来自何处的隐藏本质。 –

0

我个人在控制器工厂得到的行动名称,以每个行动的基础上注入服务。

看看我的网站控制器。

namespace Admin\Controller\Service; 

use Zend\ServiceManager\FactoryInterface; 
use Zend\ServiceManager\ServiceLocatorInterface; 
use Admin\Controller\SitesController; 
use Admin\Model\Sites as Models; 

class SitesControllerFactory implements FactoryInterface 
{ 

    public function createService(ServiceLocatorInterface $serviceLocator) 
    { 
     $actionName = $serviceLocator->getServiceLocator()->get('Application')->getMvcEvent()->getRouteMatch()->getParam('action'); 

     $controller = new SitesController(); 

     switch ($actionName) { 
      case 'list': 
       $controller->setModel($serviceLocator->getServiceLocator()->get(Models\ListSitesModel::class)); 
       break; 
      case 'view': 
       $controller->setModel($serviceLocator->getServiceLocator()->get(Models\ViewSiteModel::class)); 
       break; 
      case 'add': 
       $controller->setModel($serviceLocator->getServiceLocator()->get(Models\AddSiteModel::class)); 
       break; 
      case 'edit': 
       $controller->setModel($serviceLocator->getServiceLocator()->get(Models\EditSiteModel::class)); 
       break; 
     } 

     return $controller; 
    } 

} 

正如你可以看到我用$serviceLocator->getServiceLocator()->get('Application')->getMvcEvent()->getRouteMatch()->getParam('action');来获取操作名称和使用switch语句需要时注入的依赖关系。 我不知道这是否是最好的解决方案,但它适用于我。

希望这会有所帮助。

+0

对我来说,它看起来不是一个美丽的解决方案,但对于某些特殊情况来说很有趣,谢谢! – Moongazer

相关问题