服务定位器模式
注:该模式被视为一种反模式。
一些人认为服务定位器模式是一种反模式,因为它违反了依赖倒置原则。服务定位器隐藏了类的依赖关系,而不是像使用依赖注入那样暴露它们。如果这些依赖关系发生了变化,你有可能破坏使用这些依赖的类的功能,导致系统变得难以维护。
目的
为了获得更好的可测试、可维护和可扩展的代码,服务定位器模式实现了松散耦合的架构。依赖注入模式和服务定位器模式是控制模式的一种实现。
用法
通过 ServiceLocator,你可以为一个给定的接口注册一个服务。利用该接口,你可以在不知道具体实现的情况下,检索接口服务并在应用程序中使用它。你可以在加载时配置和注入 ServiceLocator 对象。
UML 类图

代码
Service.php
1 2 3 4 5 6 7 8
| <?php
namespace DesignPatterns\Extend\ServiceLocator;
interface Service {
}
|
ServiceLocator.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| <?php declare(strict_types = 1);
namespace DesignPatterns\Extend\ServiceLocator;
use OutOfRangeException; use InvalidArgumentException;
class ServiceLocator {
private array $services = [];
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
1 2 3 4 5 6 7 8
| <?php declare(strict_types = 1);
namespace DesignPatterns\Extend\ServiceLocator;
class LogService implements Service {
}
|
测试
Tests/ServiceLocatorTest.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <?php declare(strict_types = 1);
namespace DesignPatterns\Extend\ServiceLocator\Tests;
use DesignPatterns\Extend\ServiceLocator\LogService; use DesignPatterns\Extend\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); } }
|