Zend Framework 2 - 控制器通过(修改?)工厂实例化服务
Zend Framework 2 - Controller instantiate Service via (modified?) Factory
我有一个抽象服务 class SAbstract
,它由 ConcreteServiceA
和 ConcreteServiceB
继承。现在我在我的控制器的工厂 class 中实例化 ConcreteServiceA
并将服务注入我的控制器。
在我的控制器中的特定操作中,我想将 ConcreteServiceA
与 ConcreteServiceB
交换以更改行为。因为它们具有相同的接口(抽象 class SAbstract
)我也可以将它注入我的控制器(服务是一种策略模式)。
但我不想在我的控制器中直接实例化 ConcreteServiceB
以保持我的代码干净(以便于重构和交换行为)。
一个可能的解决方案是为我的控制器创建第二个工厂,它注入 ConcreteServiceB
而不是 ConcreteServiceA
但后来我复制了很多代码,这不是很好......
另一种解决方案是在我的控制器中注入这两种服务(但这 "smells" 就像错误的代码)。
a delegator factory 这样做是正确的方法吗?然后我必须在我的控制器中实现设置器...
有没有更好的方法?
我试图用示意图形象化我的 class 关系。
AbstractService <|--<inherit>- ConcreteServiceA
AbstractService <|--<inherit>- ConcreteServiceB
Controller -<use>--> AbstractService
Controller:ActionA -<use>--> ConcreteServiceA:exportAction()
Controller:ActionB -<use>--> ConcreteServiceB:exportAction()
In a specific action in my controller I want to exchange ConcreteServiceA with ConcreteServiceB to change behavior. Because they have same interface.
您可以将路由配置为对每个操作使用不同的控制器服务名称;然后配置控制器工厂以使用配置注入所需的服务。
路由配置可能如下所示。
'router' => [
'routes' => [
'foo' => [
'type' => 'literal',
'options' => [
'route' => '/foo',
'defaults' => [
'controller' => 'MyControllerWithFooService',
'action' => 'actionThatNeedsFooService',
],
],
],
'bar' => [
'type' => 'literal',
'options' => [
'route' => '/bar',
'defaults' => [
'controller' => 'MyControllerWithBarService',
'action' => 'actionThatNeedsBarService',
],
],
],
],
]
然后添加服务和控制器的配置。
'app_config' => [
'MyControllerWithFooService' => [
'service_name' => 'FooService',
],
'MyControllerWithFooService' => [
'service_name' => 'BarService',
],
],
'service_manager' => [
'factories' => [
'FooService' => 'FooServiceFactory'
'BarService' => 'BarServiceFactory'
],
],
'controllers' => [
'factories' => [
'MyControllerWithFooService' => 'MyControllerServiceFactory'
'MyControllerWithBarService' => 'MyControllerServiceFactory'
],
]
MyControllerServiceFactory
可以很简单
class MyControllerServiceFactory
{
public function __invoke($controllerManager, $name, $requestedName)
{
$sm = $controllerManager->getServiceLocator();
$config = $sm->get('config');
if (empty($config['app_config'][$requestedName])) {
throw new ServiceNotCreatedException('No config set!');
}
$serviceName = $config['app_config'][$requestedName]['service_name'];
$service = $sm->get($serviceName);
return new MyController($service);
}
}
我有一个抽象服务 class SAbstract
,它由 ConcreteServiceA
和 ConcreteServiceB
继承。现在我在我的控制器的工厂 class 中实例化 ConcreteServiceA
并将服务注入我的控制器。
在我的控制器中的特定操作中,我想将 ConcreteServiceA
与 ConcreteServiceB
交换以更改行为。因为它们具有相同的接口(抽象 class SAbstract
)我也可以将它注入我的控制器(服务是一种策略模式)。
但我不想在我的控制器中直接实例化 ConcreteServiceB
以保持我的代码干净(以便于重构和交换行为)。
一个可能的解决方案是为我的控制器创建第二个工厂,它注入 ConcreteServiceB
而不是 ConcreteServiceA
但后来我复制了很多代码,这不是很好......
另一种解决方案是在我的控制器中注入这两种服务(但这 "smells" 就像错误的代码)。
a delegator factory 这样做是正确的方法吗?然后我必须在我的控制器中实现设置器...
有没有更好的方法?
我试图用示意图形象化我的 class 关系。
AbstractService <|--<inherit>- ConcreteServiceA
AbstractService <|--<inherit>- ConcreteServiceB
Controller -<use>--> AbstractService
Controller:ActionA -<use>--> ConcreteServiceA:exportAction()
Controller:ActionB -<use>--> ConcreteServiceB:exportAction()
In a specific action in my controller I want to exchange ConcreteServiceA with ConcreteServiceB to change behavior. Because they have same interface.
您可以将路由配置为对每个操作使用不同的控制器服务名称;然后配置控制器工厂以使用配置注入所需的服务。
路由配置可能如下所示。
'router' => [
'routes' => [
'foo' => [
'type' => 'literal',
'options' => [
'route' => '/foo',
'defaults' => [
'controller' => 'MyControllerWithFooService',
'action' => 'actionThatNeedsFooService',
],
],
],
'bar' => [
'type' => 'literal',
'options' => [
'route' => '/bar',
'defaults' => [
'controller' => 'MyControllerWithBarService',
'action' => 'actionThatNeedsBarService',
],
],
],
],
]
然后添加服务和控制器的配置。
'app_config' => [
'MyControllerWithFooService' => [
'service_name' => 'FooService',
],
'MyControllerWithFooService' => [
'service_name' => 'BarService',
],
],
'service_manager' => [
'factories' => [
'FooService' => 'FooServiceFactory'
'BarService' => 'BarServiceFactory'
],
],
'controllers' => [
'factories' => [
'MyControllerWithFooService' => 'MyControllerServiceFactory'
'MyControllerWithBarService' => 'MyControllerServiceFactory'
],
]
MyControllerServiceFactory
可以很简单
class MyControllerServiceFactory
{
public function __invoke($controllerManager, $name, $requestedName)
{
$sm = $controllerManager->getServiceLocator();
$config = $sm->get('config');
if (empty($config['app_config'][$requestedName])) {
throw new ServiceNotCreatedException('No config set!');
}
$serviceName = $config['app_config'][$requestedName]['service_name'];
$service = $sm->get($serviceName);
return new MyController($service);
}
}