4

zend-mvcのバージョン2.7.0以降、ServiceLocatorAwareInterfaceは無効になりますので、コントローラ内で$this->serviceLocator->get()も呼び出されます。ZF2で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()の内部に使用する前は、インスタンス化されました。

一般に、すべての要求で工場から多数のインスタンスが作成されるようですが、ほとんどの依存関係は使用されません。これは私の設計上の問題なのでしょうか、あるいは「コンストラクタを介して依存性注入を行う」という通常の副作用の動作ですか?

+0

私はここでほぼ同じ質問をしました:http://stackoverflow.com/questions/33496279/servicelocator-lets-thoughts-about-it-in-zf2-context – Hooli

答えて

7

私の意見では、それはコンストラクタによる依存性注入の通常の効果です。

私はあなたのアプリケーションがどのように機能するかを改善することになりました(相互に排他的ではない)の2つの選択肢があると思う。必要なときにだけ依存関係がインスタンス化されるように

  1. は、お使いのコントローラを分割します。これにより、より多くのクラスやファクトリなどが生まれるでしょうが、あなたのコードは単一の責任の原則よりも多くなります

  2. Lazy Servicesを使用すると、一部のサービスがコントローラ全体の依存関係彼らは実際に呼び出されたときに初めてインスタンス化されます(呼び出されていないアクションでは決して使用されません!)

+0

OK、答えてくれてありがとう!デザインにいくつかの点がないかどうかはわかりませんでした。遅延サービスは興味深いように聞こえます。 – 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', 
     ) 
    ), 
    // ... 
); 

たぶん、あなたが唯一のコントローラのコンストラクタ(ServiceManagerのインスタンス)に注入する1つの依存関係を必要とするコントローラプラグインhere in the documentation

+0

ありがとうございますが、コントローラプラグインであれば、コントローラーにのみ接続されていて、他のサービスでは 'SyncRestClientService'を使用できません。サービスがコントローラでのみ使用される場合、このソリューションはオプションになる場合があります。 – Moongazer

+0

@モンゴザイア私の答えをより慎重にチェックすると、サービスがコントローラプラグインに注入されていることがわかります。それはあなたが通常のサービスとしてもそれを使用できることを意味します。 – Wilt

0

により

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

読む:このようなあなたのコントローラのアクション内でそれをSE。私は周りの警官を見ません...

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

} 

は、私はアクション名を取得し、必要なときに依存性を注入するためにswitch文を使用する$serviceLocator->getServiceLocator()->get('Application')->getMvcEvent()->getRouteMatch()->getParam('action');を使用しています。 これが最善の解決策であるかどうかはわかりませんが、それは私にとってはうまくいきます。

これが役に立ちます。

+0

私にとっては、それは美しい解決策のようには見えませんが、ある例外的な状況のために面白いものです。 – Moongazer