2.11. 注册模式
2.11.1. 目的
为应用中常用的对象实现一个中央存储,通常用一个只有静态方法的抽象类来实现(或者使用单例模式)
2.11.2. UML 图

2.11.3. 代码
你可以在 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
8}
2.11.4. 测试
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}