4.1. Localizador de Serviço
ESTE É CONSIDERADO UM ANTI-PATTERN!
O Localizador de Serviço (Service Locator) é considerado por muitas pessoas como um anti-padrão. Ele viola o princípio da inversão de dependência. O padrão Localizador de Serviço oculta as dependências da classe ao invés de expô-las, como você poderia fazer usando Injeção de Dependências (Dependency Injection). Em caso de mudanças destas dependências, você corre o risco de quebrar a funcionalidade das classes que estão as usando, fazendo com que seu sistema seja difícil de manter.
4.1.1. Objetivo
Oferece uma arquitetura desacoplada garantindo testabilidade manutenibilidade e extensão de um código. Injeção de dependências e Localizador de Serviços são implementações do padrão de Inversão de dependências.
4.1.2. Uso
Com Localizador de Serviço
é possível registrar um serviço para uma determinada interface. Usando esta interface é possível obter esse serviço e usá-la dentro de outras classes da aplicação sem conhecimento de sua implementação. É possível configurar e injetar a instancia de um Localizador de Serviço no _bootstrap_.
4.1.3. Diagrama UML
4.1.4. Código
Você também pode encontrar este código no 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. Teste
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}