PHP 服务定位器模式
这被认为是一种反模式!
对于某些人来说,服务定位器被认为是一种反模式。它违反了依赖倒置原则。服务定位器隐藏类的依赖关系,而不是像使用依赖注入那样暴露它们。如果这些依赖项发生变化,您可能会破坏使用它们的类的功能,从而使您的系统难以维护。
目的
实现松散耦合的架构以获得更好的可测试、可维护和可扩展的代码。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); } }