PHP设计模式 PHP 服务定位器模式

2024-02-26 开发教程 PHP设计模式 匿名 3

这被认为是一种反模式!
对于某些人来说,服务定位器被认为是一种反模式。它违反了依赖倒置原则。服务定位器隐藏类的依赖关系,而不是像使用依赖注入那样暴露它们。如果这些依赖项发生变化,您可能会破坏使用它们的类的功能,从而使您的系统难以维护。

目的

实现松散耦合的架构以获得更好的可测试、可维护和可扩展的代码。DI 模式和服务定位器模式是逆控制模式的一种实现。

用法

使用ServiceLocator您可以为给定接口注册服务。通过使用该接口,您可以检索服务并在应用程序的类中使用它,而无需知道它的实现。您可以在引导程序上配置和注入服务定位器对象。

UML 图

代码

Service.php

<?php
namespace DesignPatterns\More\ServiceLocator;
interface Service
{
}

ServiceLocator.php

<?php
declare(strict_types=1);
namespace DesignPatterns\More\ServiceLocator;
use OutOfRangeException;
use InvalidArgumentException;
class ServiceLocator
{
/**
* @var string[][]
*/
private array $services = [];
/**
* @var Service[]
*/
private array $instantiated = [];
public function addInstance(string $class, Service $service)
{
$this->instantiated[$class] = $service;
}
public function addClass(string $class, array $params)
{
$this->services[$class] = $params;
}
public function has(string $interface): bool
{
return isset($this->services[$interface]) || isset($this->instantiated[$interface]);
}
public function get(string $class): Service
{
if (isset($this->instantiated[$class])) {
return $this->instantiated[$class];
}
$args = $this->services[$class];
switch (count($args)) {
case 0:
$object = new $class();
break;
case 1:
$object = new $class($args[0]);
break;
case 2:
$object = new $class($args[0], $args[1]);
break;
case 3:
$object = new $class($args[0], $args[1], $args[2]);
break;
default:
throw new OutOfRangeException('Too many arguments given');
}
if (!$object instanceof Service) {
throw new InvalidArgumentException('Could not register service: is no instance of Service');
}
$this->instantiated[$class] = $object;
return $object;
}
}

LogService.php

<?php
declare(strict_types=1);
namespace DesignPatterns\More\ServiceLocator;
class LogService implements Service
{
}

测试

Tests/ServiceLocatorTest.php

<?php
declare(strict_types=1);
namespace DesignPatterns\More\ServiceLocator\Tests;
use DesignPatterns\More\ServiceLocator\LogService;
use DesignPatterns\More\ServiceLocator\ServiceLocator;
use PHPUnit\Framework\TestCase;
class ServiceLocatorTest extends TestCase
{
private ServiceLocator $serviceLocator;
public function setUp(): void
{
$this->serviceLocator = new ServiceLocator();
}
public function testHasServices()
{
$this->serviceLocator->addInstance(LogService::class, new LogService());
$this->assertTrue($this->serviceLocator->has(LogService::class));
$this->assertFalse($this->serviceLocator->has(self::class));
}
public function testGetWillInstantiateLogServiceIfNoInstanceHasBeenCreatedYet()
{
$this->serviceLocator->addClass(LogService::class, []);
$logger = $this->serviceLocator->get(LogService::class);
$this->assertInstanceOf(LogService::class, $logger);
}
}