Added Service Locator pattern

This commit is contained in:
tafax 2014-02-17 16:41:21 +01:00
parent c232381f3b
commit 67c26edeb3
8 changed files with 279 additions and 0 deletions

View File

@ -0,0 +1,8 @@
<?php
namespace DesignPatterns\ServiceLocator;
class DatabaseService implements DatabaseServiceInterface
{
}

View File

@ -0,0 +1,8 @@
<?php
namespace DesignPatterns\ServiceLocator;
interface DatabaseServiceInterface
{
}

View File

@ -0,0 +1,8 @@
<?php
namespace DesignPatterns\ServiceLocator;
class LogService implements LogServiceInterface
{
}

View File

@ -0,0 +1,8 @@
<?php
namespace DesignPatterns\ServiceLocator;
interface LogServiceInterface
{
}

16
ServiceLocator/README.md Normal file
View File

@ -0,0 +1,16 @@
# Service Locator
## 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.
## 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.
## 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...)

View File

@ -0,0 +1,105 @@
<?php
namespace DesignPatterns\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;
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace DesignPatterns\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);
}

View File

@ -0,0 +1,102 @@
<?php
/**
* DesignPatternsPHP
*
* Copyright (c) 2014 Matteo Tafani Alunno
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
namespace DesignPatterns\Tests\ServiceLocator;
use DesignPatterns\ServiceLocator\DatabaseService;
use DesignPatterns\ServiceLocator\LogService;
use DesignPatterns\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\ServiceLocator\LogServiceInterface', $this->logService);
$this->serviceLocator->add('DesignPatterns\ServiceLocator\DatabaseServiceInterface', $this->databaseService);
$this->assertTrue($this->serviceLocator->has('DesignPatterns\ServiceLocator\LogServiceInterface'));
$this->assertTrue($this->serviceLocator->has('DesignPatterns\ServiceLocator\DatabaseServiceInterface'));
$this->assertFalse($this->serviceLocator->has('DesignPatterns\ServiceLocator\FakeServiceInterface'));
}
public function testServicesWithObject()
{
$this->serviceLocator->add('DesignPatterns\ServiceLocator\LogServiceInterface', $this->logService);
$this->serviceLocator->add('DesignPatterns\ServiceLocator\DatabaseServiceInterface', $this->databaseService);
$this->assertSame($this->logService, $this->serviceLocator->get('DesignPatterns\ServiceLocator\LogServiceInterface'));
$this->assertSame($this->databaseService, $this->serviceLocator->get('DesignPatterns\ServiceLocator\DatabaseServiceInterface'));
}
public function testServicesWithClass()
{
$this->serviceLocator->add('DesignPatterns\ServiceLocator\LogServiceInterface', get_class($this->logService));
$this->serviceLocator->add('DesignPatterns\ServiceLocator\DatabaseServiceInterface', get_class($this->databaseService));
$this->assertNotSame($this->logService, $this->serviceLocator->get('DesignPatterns\ServiceLocator\LogServiceInterface'));
$this->assertInstanceOf('DesignPatterns\ServiceLocator\LogServiceInterface', $this->serviceLocator->get('DesignPatterns\ServiceLocator\LogServiceInterface'));
$this->assertNotSame($this->databaseService, $this->serviceLocator->get('DesignPatterns\ServiceLocator\DatabaseServiceInterface'));
$this->assertInstanceOf('DesignPatterns\ServiceLocator\DatabaseServiceInterface', $this->serviceLocator->get('DesignPatterns\ServiceLocator\DatabaseServiceInterface'));
}
public function testServicesNotShared()
{
$this->serviceLocator->add('DesignPatterns\ServiceLocator\LogServiceInterface', $this->logService, false);
$this->serviceLocator->add('DesignPatterns\ServiceLocator\DatabaseServiceInterface', $this->databaseService, false);
$this->assertNotSame($this->logService, $this->serviceLocator->get('DesignPatterns\ServiceLocator\LogServiceInterface'));
$this->assertInstanceOf('DesignPatterns\ServiceLocator\LogServiceInterface', $this->serviceLocator->get('DesignPatterns\ServiceLocator\LogServiceInterface'));
$this->assertNotSame($this->databaseService, $this->serviceLocator->get('DesignPatterns\ServiceLocator\DatabaseServiceInterface'));
$this->assertInstanceOf('DesignPatterns\ServiceLocator\DatabaseServiceInterface', $this->serviceLocator->get('DesignPatterns\ServiceLocator\DatabaseServiceInterface'));
}
}