Zend Framework 2 - 控制器通过(修改?)工厂实例化服务

Zend Framework 2 - Controller instantiate Service via (modified?) Factory

我有一个抽象服务 class SAbstract,它由 ConcreteServiceAConcreteServiceB 继承。现在我在我的控制器的工厂 class 中实例化 ConcreteServiceA 并将服务注入我的控制器。

在我的控制器中的特定操作中,我想将 ConcreteServiceAConcreteServiceB 交换以更改行为。因为它们具有相同的接口(抽象 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);
    } 
}