动态服务使用

时间:2019-01-07 16:27:09

标签: symfony

我想使用服务处理表中的数据,并在记录中指定要使用的正确服务。因此,假设我有一个包含以下数据的表

" "

我想处理父服务| id | value1 | value2 | service | result | |----|---------|---------|----------------|--------| | 1 | string1 | string2 | string_version | | | 2 | int1 | int2 | int_version | | | 3 | string3 | string3 | string_version | | 中的每一行,它循环每条记录并根据calculator的值获取服务,该服务使用service计算结果,并value1,然后将结果存储在value2

此刻,我已经创建了result服务,其中一个参数是服务容器-然后我可以使用calculator来获取我需要的实际服务:

get

}

但是...这不是非常可测试的-因为即时消息传递了整个服务容器。将来,我也希望能够添加新服务-因此,第三方可以添加具有特定服务名称的捆绑软件,然后使用该捆绑软件来处理表中的记录

这是我要执行的操作的非常简化的版本-但是要动态从其他服务传递/获取服务是我要执行的操作。

我如何修改此代码,以便我可以基于数据库中的动态值获得服务,并允许第三方添加“处理”服务-当然,这将实现接口-因此请确保存在正确的方法

1 个答案:

答案 0 :(得分:2)

您有两个明显的候选人:

在这种情况下,我本人(总是)会使用标记服务(策略模式),但仍然会为每个示例提供示例,因此您可以自行决定。

注意:如果您使用服务定位器(如下所示),则服务中将出现重复项和难看的代码。

服务中心

interface ServiceLocatorInterface
{
    public function locate(string $id);
}

-

use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;

class ServiceLocator implements ServiceLocatorInterface, ServiceSubscriberInterface
{
    private $locator;

    public function __construct(ContainerInterface $locator)
    {
        $this->locator = $locator;
    }

    public static function getSubscribedServices()
    {
        return [
            'string_version' => StringCalculator::class,
            'int_version' => IntCalculator::class,
        ];
    }

    public function locate(string $id)
    {
        if (!$this->locator->has($id)) {
            throw new ServiceLocatorException('Service was not found.');
        }

        try {
            return $this->locator->get($id);
        } catch (ContainerExceptionInterface $e) {
            throw new ServiceLocatorException('Failed to fetch service.');
        }
    }
}

-

class StringCalculator
{
    public function calculate($value1, $value2)
    {
        return $value1.' - '.$value2;
    }
}

-

class IntCalculator
{
    public function calculate($value1, $value2)
    {
        return $value1 + $value2;
    }
}

用法:

class YourService
{
    private $serviceLocator;

    public function __construct(\App\ServiceLocatorInterface $serviceLocator)
    { 
        $this->serviceLocator = $serviceLocator;
    }

    public function yourMethod()
    {
        /** @var StringCalculator $calculator */
        $calculator = $this->serviceLocator->locate('string_version');
        $result = $calculator->calculate('1', '2'); // result: 1 - 2

        /** @var IntCalculator $calculator */
        $calculator = $this->serviceLocator->locate('int_version');
        $result = $calculator->calculate(1, 2); // result: 3
    }
}

标记服务

service:
    App\Strategy\Calculator:
        arguments: [!tagged calculator]

    App\Strategy\StringCalculatorStrategy:
        tags:
            - { name: calculator }

    App\Strategy\IntCalculatorStrategy:
        tags:
            - { name: calculator }

-

use Traversable;

class Calculator
{
    private $calculators;

    public function __construct(Traversable $calculators)
    {
        $this->calculators = $calculators;
    }

    public function calculate(string $serviceName, $value1, $value2)
    {
        /** @var CalculatorStrategyInterface $calculator */
        foreach ($this->calculators as $calculator) {
            if ($calculator->canProcess($serviceName)) {
                return $calculator->process($value1, $value2);
            }
        }
    }
}

-

interface CalculatorStrategyInterface
{
    public function canProcess(string $serviceName): bool;

    public function process($value1, $value2);
}

-

class StringCalculatorStrategy implements CalculatorStrategyInterface
{
    public function canProcess(string $serviceName): bool
    {
        return $serviceName === 'string_version';
    }

    public function process($value1, $value2)
    {
        return $value1.' '.$value2;
    }
}

-

class IntCalculatorStrategy implements CalculatorStrategyInterface
{
    public function canProcess(string $serviceName): bool
    {
        return $serviceName === 'int_version';
    }

    public function process($value1, $value2)
    {
        return $value1 + $value2;
    }
}

用法:

class YourService
{
    private $calculator;

    public function __construct(\App\Strategy\Calculator $calculator)
    { 
        $this->calculator = $calculator;
    }

    public function yourMethod()
    {
        // result: 1 - 2
        $result = $this->calculator->calculate('string_version', 1, 2);
        // result: 3
        $result = $this->calculator->calculate('int_version', 1, 2);
    }
}