ZF2中新的依赖注入方式是否没有serviceLocator-> get()方式效率更低?

时间:2016-04-22 14:08:46

标签: php dependency-injection zend-framework2 overhead zend-framework3

zend-mvc版本的2.7.0版本ServiceLocatorAwareInterface被删除后,控制器内的$this->serviceLocator->get()调用也被删除。

这就是为什么前几天我对我的所有模块进行了大量重构,以便通过构建器使用工厂注入所需的服务/对象。

当然,我明白为什么这是更好/更清洁的做事方式,因为现在更容易看到家属。但另一方面:

这会导致繁重的开销和更多从未使用过的类实例,不是吗?

让我们看一个例子:

因为我的所有控制器都有依赖关系,所以我为所有控制器创建了工厂。

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()内使用!在我$this->serviceLocator->get('Admin\Service\SyncRestClientService')中使用syncAction()之前,只有它才被实例化。

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

4 个答案:

答案 0 :(得分:7)

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

我认为您现在有两个选项(不是互斥的)来改善您的应用程序的工作方式:

  1. 拆分控制器,以便仅在需要时才会实现依赖关系。这肯定会产生更多的课程,更多的工厂等等,但你的代码将更多地达到单一的责任原则

  2. 你可以使用Lazy Services,这样,即使某些服务是整个控制器的依赖关系,它们实际上只会在它们第一次被调用时才会被实现(所以从不对它们所处的行为进行实例化)不叫!)

答案 1 :(得分: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',
        )
    ),
    // ...
);

现在您可以在控制器操作中使用它,如下所示:

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

详细了解控制器插件here in the documentation

答案 2 :(得分:0)

也许您只需要将一个依赖项注入到控制器构造函数(ServiceManager实例)中。我没有看到任何警察......

namespace Admin\Factory\Controller;

class CustomerControllerFactory implements FactoryInterface {

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

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

}

答案 3 :(得分: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语句注入依赖项。 我不知道这是否是最佳解决方案,但它对我有用。

希望这有帮助。