我想使用服务处理表中的数据,并在记录中指定要使用的正确服务。因此,假设我有一个包含以下数据的表
" "
我想处理父服务| 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
}
但是...这不是非常可测试的-因为即时消息传递了整个服务容器。将来,我也希望能够添加新服务-因此,第三方可以添加具有特定服务名称的捆绑软件,然后使用该捆绑软件来处理表中的记录
这是我要执行的操作的非常简化的版本-但是要动态从其他服务传递/获取服务是我要执行的操作。
我如何修改此代码,以便我可以基于数据库中的动态值获得服务,并允许第三方添加“处理”服务-当然,这将实现接口-因此请确保存在正确的方法
答案 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);
}
}