kit*_*sei 9 dependency-injection factory-pattern zend-framework2
我有以下课程:
我想基于url加载一个工厂(用于DI)的正确类:
现在我正试图避免为我的每一项服务创建一个工厂,我想以恐怖的方式做到这一点:
<?php namespace App\Service\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class ServiceFactory implements FactoryInterface
{
/**
* Create service
* @param ServiceLocatorInterface $serviceLocator
* @return \App\Service\AbstractService
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$servicename = ''; // how can I get something like this, based on the route ?
$service = $serviceLocator->get('Service\' . $servicename . 'Service');
}
}
Run Code Online (Sandbox Code Playgroud)
如果可能的话,我想避免计算工厂内的路线,因为如果有一天这个工厂将从其他地方调用,它将无法工作.
那么你如何基本上做一个工厂" 处理创建对象的问题而不用 zend 2 指定将要创建的对象的确切类 "?
编辑 - 使用解决方案
再次编辑,这里是我根据接受的答案首选的最终解决方案:
$apiName = str_replace(' ', '', ucwords(str_replace('_', ' ', $this->params('api'))));
$serviceName = str_replace(' ', '', ucwords(str_replace('_', ' ', $this->params('service'))));
$di = new Di();
$di->instanceManager()->setParameters(sprintf('App\Service\%s\%sService', $apiName, $serviceName), [
'service' => sprintf('%s\Service\%sService', $apiName, $serviceName),
]);
$service = $di->get(sprintf('App\Service\%s\%sService', $apiName, $serviceName));
Run Code Online (Sandbox Code Playgroud)
AbstractService(任何服务的父类)
<?php namespace App\Service;
use Zend\Log\Logger;
abstract class AbstractService
{
/**
* @var object|mixed
*/
protected $api;
/**
* @var \Zend\Log\Logger
*/
protected $logger;
/**
* Constructor
* @param mixed $service Api service class
* @param Logger $logger Logger instance
*/
public function __construct($service, Logger $logger)
{
$this->api = $service;
$this->logger = $logger;
}
}
Run Code Online (Sandbox Code Playgroud)
理想情况下,抽象构造函数的$ service参数至少应该由接口输入,我正在研究它.
Zend\Di帮助我动态定义构造函数api,这就是我想要的.AbstractFactory更容易阅读,但正如您所指出的那样,每次调用所有抽象工厂的事实
$serviceLocator->get()
Run Code Online (Sandbox Code Playgroud)
被调用它不是那么好.
这是可能的获取Request,甚至Zend\Mvc\Controller\Plugin\Param从工厂内的实例; 然后,您可以从中访问所需的路径参数.
// within your factory
$params = $serviceLocator->get('ControllerPluginManager')->get('params');
$serviceName = $params->fromRoute('route_param_name');
Run Code Online (Sandbox Code Playgroud)
然后会导致
$service = $serviceLocator->get('Service\' . $servicename . 'Service');
Run Code Online (Sandbox Code Playgroud)
但是这不会像你期望的那样起作用; 对服务经理的调用get将意味着你需要另一个服务工厂加载 'Service\' . $serviceName . 'Service'- 所以你仍然需要为所述服务创建一个工厂(如果它不是'invoakable'类).回到你开始的地方!
解决方案?
虽然你明确表示你不想这样做,但我只能假设它是因为你是懒惰的!这是你应该这样做的方式.为什么?您列出的服务似乎不相关,这意味着它们应该各自具有不同的依赖关系.最简单的方式来注入这些特定的依赖将是对每个服务的基础-它会带你不到5个小步舞(你写一个你的问题),你的代码会更简单,你就会有一个更好的时间,当您的要求更改.
抽象工厂可以被视为"后备" - 如果管理器中不存在该服务,它将把它传递给附加到它的任何抽象工厂,直到其中一个能够返回一个对象.
如果您的服务非常相似(它们具有相同的依赖关系,但配置不同),您可以创建一个抽象工厂,在内部检查所请求服务的名称(例如Service\Client)并为该服务注入所需的依赖关系.
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\AbstractFactoryInterface;
class FooAbstractFactory implements AbstractFactoryInterface
{
protected $config;
public function getConfig(ServiceLocatorInterface $serviceLocator) {
if (! isset($this->config)) {
$config = $serviceLocator->get('Config');
$this->config = $config['some_config_key'];
}
return $this->config;
}
public function canCreateServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName)
{
$config = $this->getConfig($serviceLocator);
return (isset($config[$requestedName])) ? true : false;
}
public function createServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName)
{
$config = $this->getConfig($serviceLocator);
$config = $config[$requestedName];
$className = $config['class_name'];
// This could be more complicated
return new $className($config, $this->getSomethingElseExample($serviceLocator));
}
}
Run Code Online (Sandbox Code Playgroud)
要考虑的事情
get,这意味着(不必要的)性能损失.如果您的某项服务发生变化,您需要破解此更改,然后仍可以创建重新测试每项服务.
Zend\Di总之,这将允许您在配置中定义服务的所有依赖关系,或者如果您的服务编写得很好(参数是使用标准命名约定的类型提示),请通过Reflection.
我还没有看到DI需要这种复杂性; 虽然检查出来,在一个非常 非常大的应用程序在时间上的投资可能是有益的.
编辑
另一个选择是创建一个基础工厂类(实现FactoryInterface)
abstract class DefaultServiceFactory implements FactoryInterface {
public function createService(ServiceLocatorInterface $sl)
{
$className = $this->getClassName();
return new $className(
$this->getApiService($serviceLocator),
$this->getFooService($serviceLocator)
);
}
// Abstract or it could return the 'default' class name
// depending on requirements
abstract protected function getClassName();
protected function getApiService(ServiceLocatorInterface $sl)
{
return $sl->get('Default/Api/Service');
}
protected function getFooService(ServiceLocatorInterface $sl)
{
return $sl->get('Default/Foo/Service');
}
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,你不能再避免写一个具体的课程了.但是,由于大多数代码都封装在基类中,因此它可能会让您的生活变得更容易一些.
class ApiServiceFactory extends DefaultServiceFactory
{
protected function getClassName() {
return '\Custom\Api\Service';
}
protected function getApiService(ServiceLocatorInterface $sl) {
return $sl->get('Another/Api/Service');
}
}
Run Code Online (Sandbox Code Playgroud)
我会勉强鼓励你投资一个工厂每班选项,虽然使用抽象工厂确实没有问题,根据我的个人经验,他们在某些时候不会涵盖'边缘案例'你需要去写一个具体工厂无论如何.当创建的类具有相同的继承层次结构时,上述方法可能是首选方法.
| 归档时间: |
|
| 查看次数: |
5045 次 |
| 最近记录: |