2.11. Registry (Registro)

2.11.1. Objetivo

Implementar um armazenamento centralizado para objetos frequentemente usados em toda a aplicação, é tipicamente implementado usando uma classe abstrata com apenas métodos estáticos (ou usando o padrão Singleton). Lembre que isto introduz em estado global, o qual deve ser evitado sempre! Em vez disso, implemente isto usando a Injeção de Dependência (Dependency Injection)!

2.11.2. Diagrama UML

Alt Registry UML Diagram

2.11.3. Código

Você também pode encontrar este código no GitHub

Registry.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Structural\Registry;
 6
 7use InvalidArgumentException;
 8
 9abstract class Registry
10{
11    public const LOGGER = 'logger';
12
13    /**
14     * this introduces global state in your application which can not be mocked up for testing
15     * and is therefor considered an anti-pattern! Use dependency injection instead!
16     *
17     * @var Service[]
18     */
19    private static array $services = [];
20
21    private static array $allowedKeys = [
22        self::LOGGER,
23    ];
24
25    final public static function set(string $key, Service $value)
26    {
27        if (!in_array($key, self::$allowedKeys)) {
28            throw new InvalidArgumentException('Invalid key given');
29        }
30
31        self::$services[$key] = $value;
32    }
33
34    final public static function get(string $key): Service
35    {
36        if (!in_array($key, self::$allowedKeys) || !isset(self::$services[$key])) {
37            throw new InvalidArgumentException('Invalid key given');
38        }
39
40        return self::$services[$key];
41    }
42}

Service.php

1<?php
2
3namespace DesignPatterns\Structural\Registry;
4
5class Service
6{
7}

2.11.4. Teste

Tests/RegistryTest.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Structural\Registry\Tests;
 6
 7use InvalidArgumentException;
 8use DesignPatterns\Structural\Registry\Registry;
 9use DesignPatterns\Structural\Registry\Service;
10use PHPUnit\Framework\TestCase;
11
12class RegistryTest extends TestCase
13{
14    private Service $service;
15
16    protected function setUp(): void
17    {
18        $this->service = $this->getMockBuilder(Service::class)->getMock();
19    }
20
21    public function testSetAndGetLogger()
22    {
23        Registry::set(Registry::LOGGER, $this->service);
24
25        $this->assertSame($this->service, Registry::get(Registry::LOGGER));
26    }
27
28    public function testThrowsExceptionWhenTryingToSetInvalidKey()
29    {
30        $this->expectException(InvalidArgumentException::class);
31
32        Registry::set('foobar', $this->service);
33    }
34
35    /**
36     * notice @runInSeparateProcess here: without it, a previous test might have set it already and
37     * testing would not be possible. That's why you should implement Dependency Injection where an
38     * injected class may easily be replaced by a mockup
39     *
40     * @runInSeparateProcess
41     */
42    public function testThrowsExceptionWhenTryingToGetNotSetKey()
43    {
44        $this->expectException(InvalidArgumentException::class);
45
46        Registry::get(Registry::LOGGER);
47    }
48}