4.2. Service Locator¶
4.2.1. Purpose¶
To implement a loosely coupled architecture in order to get better testable, maintainable and extendable code. DI pattern and Service Locator pattern are an implementation of the Inverse of Control pattern.
4.2.2. Usage¶
With ServiceLocator
you can register a service for a given
interface. By using the interface you can retrieve the service and use
it in the classes of the application without knowing its implementation.
You can configure and inject the Service Locator object on bootstrap.
4.2.3. Examples¶
- Zend Framework 2 uses Service Locator to create and share services used in the framework(i.e. EventManager, ModuleManager, all custom user services provided by modules, etc...)
4.2.4. UML Diagram¶
4.2.5. Code¶
You can also find these code on GitHub
ServiceLocatorInterface.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 | <?php
namespace DesignPatterns\More\ServiceLocator;
interface ServiceLocatorInterface
{
/**
* Checks if a service is registered.
*
* @param string $interface
*
* @return bool
*/
public function has($interface);
/**
* Gets the service registered for the interface.
*
* @param string $interface
*
* @return mixed
*/
public function get($interface);
}
|
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 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | <?php
namespace DesignPatterns\More\ServiceLocator;
class ServiceLocator implements ServiceLocatorInterface
{
/**
* All services.
*
* @var array
*/
private $services;
/**
* The services which have an instance.
*
* @var array
*/
private $instantiated;
/**
* True if a service can be shared.
*
* @var array
*/
private $shared;
public function __construct()
{
$this->services = array();
$this->instantiated = array();
$this->shared = array();
}
/**
* Registers a service with specific interface.
*
* @param string $interface
* @param string|object $service
* @param bool $share
*/
public function add($interface, $service, $share = true)
{
/**
* When you add a service, you should register it
* with its interface or with a string that you can use
* in the future even if you will change the service implementation.
*/
if (is_object($service) && $share) {
$this->instantiated[$interface] = $service;
}
$this->services[$interface] = (is_object($service) ? get_class($service) : $service);
$this->shared[$interface] = $share;
}
/**
* Checks if a service is registered.
*
* @param string $interface
*
* @return bool
*/
public function has($interface)
{
return (isset($this->services[$interface]) || isset($this->instantiated[$interface]));
}
/**
* Gets the service registered for the interface.
*
* @param string $interface
*
* @return mixed
*/
public function get($interface)
{
// Retrieves the instance if it exists and it is shared
if (isset($this->instantiated[$interface]) && $this->shared[$interface]) {
return $this->instantiated[$interface];
}
// otherwise gets the service registered.
$service = $this->services[$interface];
// You should check if the service class exists and
// the class is instantiable.
// This example is a simple implementation, but
// when you create a service, you can decide
// if $service is a factory or a class.
// By registering a factory you can create your services
// using the DependencyInjection pattern.
// ...
// Creates the service object
$object = new $service();
// and saves it if the service must be shared.
if ($this->shared[$interface]) {
$this->instantiated[$interface] = $object;
}
return $object;
}
}
|
LogServiceInterface.php
1 2 3 4 5 6 7 | <?php
namespace DesignPatterns\More\ServiceLocator;
interface LogServiceInterface
{
}
|
LogService.php
1 2 3 4 5 6 7 | <?php
namespace DesignPatterns\More\ServiceLocator;
class LogService implements LogServiceInterface
{
}
|
DatabaseServiceInterface.php
1 2 3 4 5 6 7 | <?php
namespace DesignPatterns\More\ServiceLocator;
interface DatabaseServiceInterface
{
}
|
DatabaseService.php
1 2 3 4 5 6 7 | <?php
namespace DesignPatterns\More\ServiceLocator;
class DatabaseService implements DatabaseServiceInterface
{
}
|
4.2.6. Test¶
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 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 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | <?php
namespace DesignPatterns\More\ServiceLocator\Tests;
use DesignPatterns\More\ServiceLocator\DatabaseService;
use DesignPatterns\More\ServiceLocator\LogService;
use DesignPatterns\More\ServiceLocator\ServiceLocator;
use \PHPUnit_Framework_TestCase as TestCase;
class ServiceLocatorTest extends TestCase
{
/**
* @var LogService
*/
private $logService;
/**
* @var DatabaseService
*/
private $databaseService;
/**
* @var ServiceLocator
*/
private $serviceLocator;
public function setUp()
{
$this->serviceLocator = new ServiceLocator();
$this->logService = new LogService();
$this->databaseService = new DatabaseService();
}
public function testHasServices()
{
$this->serviceLocator->add(
'DesignPatterns\More\ServiceLocator\LogServiceInterface',
$this->logService
);
$this->serviceLocator->add(
'DesignPatterns\More\ServiceLocator\DatabaseServiceInterface',
$this->databaseService
);
$this->assertTrue($this->serviceLocator->has('DesignPatterns\More\ServiceLocator\LogServiceInterface'));
$this->assertTrue($this->serviceLocator->has('DesignPatterns\More\ServiceLocator\DatabaseServiceInterface'));
$this->assertFalse($this->serviceLocator->has('DesignPatterns\More\ServiceLocator\FakeServiceInterface'));
}
public function testServicesWithObject()
{
$this->serviceLocator->add(
'DesignPatterns\More\ServiceLocator\LogServiceInterface',
$this->logService
);
$this->serviceLocator->add(
'DesignPatterns\More\ServiceLocator\DatabaseServiceInterface',
$this->databaseService
);
$this->assertSame(
$this->logService,
$this->serviceLocator->get('DesignPatterns\More\ServiceLocator\LogServiceInterface')
);
$this->assertSame(
$this->databaseService,
$this->serviceLocator->get('DesignPatterns\More\ServiceLocator\DatabaseServiceInterface')
);
}
public function testServicesWithClass()
{
$this->serviceLocator->add(
'DesignPatterns\More\ServiceLocator\LogServiceInterface',
get_class($this->logService)
);
$this->serviceLocator->add(
'DesignPatterns\More\ServiceLocator\DatabaseServiceInterface',
get_class($this->databaseService)
);
$this->assertNotSame(
$this->logService,
$this->serviceLocator->get('DesignPatterns\More\ServiceLocator\LogServiceInterface')
);
$this->assertInstanceOf(
'DesignPatterns\More\ServiceLocator\LogServiceInterface',
$this->serviceLocator->get('DesignPatterns\More\ServiceLocator\LogServiceInterface')
);
$this->assertNotSame(
$this->databaseService,
$this->serviceLocator->get('DesignPatterns\More\ServiceLocator\DatabaseServiceInterface')
);
$this->assertInstanceOf(
'DesignPatterns\More\ServiceLocator\DatabaseServiceInterface',
$this->serviceLocator->get('DesignPatterns\More\ServiceLocator\DatabaseServiceInterface')
);
}
public function testServicesNotShared()
{
$this->serviceLocator->add(
'DesignPatterns\More\ServiceLocator\LogServiceInterface',
$this->logService,
false
);
$this->serviceLocator->add(
'DesignPatterns\More\ServiceLocator\DatabaseServiceInterface',
$this->databaseService,
false
);
$this->assertNotSame(
$this->logService,
$this->serviceLocator->get('DesignPatterns\More\ServiceLocator\LogServiceInterface')
);
$this->assertInstanceOf(
'DesignPatterns\More\ServiceLocator\LogServiceInterface',
$this->serviceLocator->get('DesignPatterns\More\ServiceLocator\LogServiceInterface')
);
$this->assertNotSame(
$this->databaseService,
$this->serviceLocator->get('DesignPatterns\More\ServiceLocator\DatabaseServiceInterface')
);
$this->assertInstanceOf(
'DesignPatterns\More\ServiceLocator\DatabaseServiceInterface',
$this->serviceLocator->get('DesignPatterns\More\ServiceLocator\DatabaseServiceInterface')
);
}
}
|