4.1. Локатор Служб (Service Locator)
Этот шаблон считается анти-паттерном!
Некоторые считают Локатор Служб анти-паттерном. Он нарушает принцип инверсии зависимостей (Dependency Inversion principle) из набора принципов SOLID. Локатор Служб скрывает зависимости данного класса вместо их совместного использования, как в случае шаблона Внедрение Зависимости (Dependency Injection). В случае изменения данных зависимостей мы рискуем сломать функционал классов, которые их используют, вследствие чего затрудняется поддержка системы.
4.1.1. Назначение
Для реализации слабосвязанной архитектуры, чтобы получить хорошо тестируемый, сопровождаемый и расширяемый код. Паттерн Инъекция зависимостей (DI) и паттерн Локатор Служб — это реализация паттерна Инверсия управления (Inversion of Control, IoC).
4.1.2. Использование
С Локатором Служб
вы можете зарегистрировать сервис для определенного интерфейса. С помощью интерфейса вы можете получить зарегистрированный сервис и использовать его в классах приложения, не зная его реализацию. Вы можете настроить и внедрить объект Service Locator на начальном этапе сборки приложения.
4.1.3. Диаграмма UML
4.1.4. Код
Вы можете найти этот код на GitHub
Service.php
1<?php
2
3namespace DesignPatterns\More\ServiceLocator;
4
5interface Service
6{
7}
ServiceLocator.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\More\ServiceLocator;
6
7use OutOfRangeException;
8use InvalidArgumentException;
9
10class ServiceLocator
11{
12 /**
13 * @var string[][]
14 */
15 private array $services = [];
16
17 /**
18 * @var Service[]
19 */
20 private array $instantiated = [];
21
22 public function addInstance(string $class, Service $service)
23 {
24 $this->instantiated[$class] = $service;
25 }
26
27 public function addClass(string $class, array $params)
28 {
29 $this->services[$class] = $params;
30 }
31
32 public function has(string $interface): bool
33 {
34 return isset($this->services[$interface]) || isset($this->instantiated[$interface]);
35 }
36
37 public function get(string $class): Service
38 {
39 if (isset($this->instantiated[$class])) {
40 return $this->instantiated[$class];
41 }
42
43 $object = new $class(...$this->services[$class]);
44
45 if (!$object instanceof Service) {
46 throw new InvalidArgumentException('Could not register service: is no instance of Service');
47 }
48
49 $this->instantiated[$class] = $object;
50
51 return $object;
52 }
53}
LogService.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\More\ServiceLocator;
6
7class LogService implements Service
8{
9}
4.1.5. Тест
Tests/ServiceLocatorTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\More\ServiceLocator\Tests;
6
7use DesignPatterns\More\ServiceLocator\LogService;
8use DesignPatterns\More\ServiceLocator\ServiceLocator;
9use PHPUnit\Framework\TestCase;
10
11class ServiceLocatorTest extends TestCase
12{
13 private ServiceLocator $serviceLocator;
14
15 public function setUp(): void
16 {
17 $this->serviceLocator = new ServiceLocator();
18 }
19
20 public function testHasServices()
21 {
22 $this->serviceLocator->addInstance(LogService::class, new LogService());
23
24 $this->assertTrue($this->serviceLocator->has(LogService::class));
25 $this->assertFalse($this->serviceLocator->has(self::class));
26 }
27
28 public function testGetWillInstantiateLogServiceIfNoInstanceHasBeenCreatedYet()
29 {
30 $this->serviceLocator->addClass(LogService::class, []);
31 $logger = $this->serviceLocator->get(LogService::class);
32
33 $this->assertInstanceOf(LogService::class, $logger);
34 }
35}