Этот шаблон считается анти-паттерном!
Некоторые считают Локатор Служб анти-паттерном. Он нарушает принцип инверсии зависимостей (Dependency Inversion principle) из набора принциповSOLID. Локатор Служб скрывает зависимости данного класса вместо их совместного использования, как в случае шаблона Внедрение Зависимости (Dependency Injection). В случае изменения данных зависимостей мы рискуем сломать функционал классов, которые их используют, вследствие чего затрудняется поддержка системы.
4.1.1. Назначение
Для реализации слабосвязанной архитектуры, чтобы получить хорошо тестируемый, сопровождаемый и расширяемый код. Паттерн Инъекция зависимостей (DI) и паттерн Локатор Служб — это реализация паттерна Инверсия управления (Inversion of Control, IoC).
4.1.2. Использование
СЛокаторомСлужб
вы можете зарегистрировать сервис для определенного интерфейса. С помощью интерфейса вы можете получить зарегистрированный сервис и использовать его в классах приложения, не зная его реализацию. Вы можете настроить и внедрить объект Service Locator на начальном этапе сборки приложения.
4.1.3. Примеры
- Zend Framework 2 использует Service Locator для создания и совместного использования сервисов, задействованных в фреймворке (т.е. EventManager, ModuleManager, все пользовательские сервисы, предоставляемые модулями, и т.д …)
4.1.4. UML Диаграмма
4.1.5. Код
Вы можете найти этот код наGitHub
ServiceLocator.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
<?php
namespace DesignPatterns\More\ServiceLocator;
class ServiceLocator
{
/**
* @var array
*/
private $services = [];
/**
* @var array
*/
private $instantiated = [];
/**
* @var array
*/
private $shared = [];
/**
* instead of supplying a class here, you could also store a service for an interface
*
* @param string $class
* @param object $service
* @param bool $share
*/
public function addInstance(string $class, $service, bool $share = true)
{
$this->services[$class] = $service;
$this->instantiated[$class] = $service;
$this->shared[$class] = $share;
}
/**
* instead of supplying a class here, you could also store a service for an interface
*
* @param string $class
* @param array $params
* @param bool $share
*/
public function addClass(string $class, array $params, bool $share = true)
{
$this->services[$class] = $params;
$this->shared[$class] = $share;
}
public function has(string $interface): bool
{
return isset($this->services[$interface]) || isset($this->instantiated[$interface]);
}
/**
* @param string $class
*
* @return object
*/
public function get(string $class)
{
if (isset($this->instantiated[$class]) && $this->shared[$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 ($this->shared[$class]) {
$this->instantiated[$class] = $object;
}
return $object;
}
}
|
LogService.php
1 2 3 4 5 6 7 |
<?php
namespace DesignPatterns\More\ServiceLocator;
class LogService
{
}
|
4.1.6. Тест
Tests/ServiceLocatorTest.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?php
namespace DesignPatterns\More\ServiceLocator\Tests;
use DesignPatterns\More\ServiceLocator\LogService;
use DesignPatterns\More\ServiceLocator\ServiceLocator;
use PHPUnit\Framework\TestCase;
class ServiceLocatorTest extends TestCase
{
/**
* @var ServiceLocator
*/
private $serviceLocator;
public function setUp()
{
$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);
}
}
|