Skip to content

Commit 67c26ed

Browse files
committed
Added Service Locator pattern
1 parent c232381 commit 67c26ed

File tree

8 files changed

+279
-0
lines changed

8 files changed

+279
-0
lines changed

ServiceLocator/DatabaseService.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace DesignPatterns\ServiceLocator;
4+
5+
class DatabaseService implements DatabaseServiceInterface
6+
{
7+
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace DesignPatterns\ServiceLocator;
4+
5+
interface DatabaseServiceInterface
6+
{
7+
8+
}

ServiceLocator/LogService.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace DesignPatterns\ServiceLocator;
4+
5+
class LogService implements LogServiceInterface
6+
{
7+
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace DesignPatterns\ServiceLocator;
4+
5+
interface LogServiceInterface
6+
{
7+
8+
}

ServiceLocator/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Service Locator
2+
3+
## Purpose
4+
5+
To implement a loosely coupled architecture in order to get better testable, maintainable and extendable code.
6+
DI pattern and Service Locator pattern are an implementation of the Inverse of Control pattern.
7+
8+
## Usage
9+
10+
With `ServiceLocator` you can register a service for a given interface. By using the interface you can retrieve the service
11+
and use it in the classes of the application without knowing its implementation. You can configure and inject the
12+
Service Locator object on bootstrap.
13+
14+
## Examples
15+
16+
* 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...)

ServiceLocator/ServiceLocator.php

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
3+
namespace DesignPatterns\ServiceLocator;
4+
5+
class ServiceLocator implements ServiceLocatorInterface
6+
{
7+
/**
8+
* All services.
9+
*
10+
* @var array
11+
*/
12+
private $services;
13+
14+
/**
15+
* The services which have an instance.
16+
*
17+
* @var array
18+
*/
19+
private $instantiated;
20+
21+
/**
22+
* True if a service can be shared.
23+
*
24+
* @var array
25+
*/
26+
private $shared;
27+
28+
public function __construct()
29+
{
30+
$this->services = array();
31+
$this->instantiated = array();
32+
$this->shared = array();
33+
}
34+
35+
/**
36+
* Registers a service with specific interface.
37+
*
38+
* @param string $interface
39+
* @param string|object $service
40+
* @param bool $share
41+
*/
42+
public function add($interface, $service, $share = true)
43+
{
44+
/**
45+
* When you add a service, you should register it
46+
* with its interface or with a string that you can use
47+
* in the future even if you will change the service implementation.
48+
*/
49+
50+
if(is_object($service) && $share)
51+
$this->instantiated[$interface] = $service;
52+
53+
$this->services[$interface] = (is_object($service) ? get_class($service) : $service);
54+
$this->shared[$interface] = $share;
55+
}
56+
57+
/**
58+
* Checks if a service is registered.
59+
*
60+
* @param string $interface
61+
*
62+
* @return bool
63+
*/
64+
public function has($interface)
65+
{
66+
return (isset($this->services[$interface]) || isset($this->instantiated[$interface]));
67+
}
68+
69+
/**
70+
* Gets the service registered for the interface.
71+
*
72+
* @param string $interface
73+
*
74+
* @return mixed
75+
*/
76+
public function get($interface)
77+
{
78+
// Retrieves the instance if it exists and it is shared
79+
if(isset($this->instantiated[$interface]) && $this->shared[$interface])
80+
return $this->instantiated[$interface];
81+
82+
// otherwise gets the service registered.
83+
$service = $this->services[$interface];
84+
85+
// You should check if the service class exists and
86+
// the class is instantiable.
87+
88+
// This example is a simple implementation, but
89+
// when you create a service, you can decide
90+
// if $service is a factory or a class.
91+
// By registering a factory you can create your services
92+
// using the DependencyInjection pattern.
93+
94+
// ...
95+
96+
// Creates the service object
97+
$object = new $service();
98+
99+
// and saves it if the service must be shared.
100+
if($this->shared[$interface])
101+
$this->instantiated[$interface] = $object;
102+
103+
return $object;
104+
}
105+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace DesignPatterns\ServiceLocator;
4+
5+
interface ServiceLocatorInterface
6+
{
7+
/**
8+
* Checks if a service is registered.
9+
*
10+
* @param string $interface
11+
*
12+
* @return bool
13+
*/
14+
public function has($interface);
15+
16+
/**
17+
* Gets the service registered for the interface.
18+
*
19+
* @param string $interface
20+
*
21+
* @return mixed
22+
*/
23+
public function get($interface);
24+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<?php
2+
/**
3+
* DesignPatternsPHP
4+
*
5+
* Copyright (c) 2014 Matteo Tafani Alunno
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
26+
namespace DesignPatterns\Tests\ServiceLocator;
27+
28+
use DesignPatterns\ServiceLocator\DatabaseService;
29+
use DesignPatterns\ServiceLocator\LogService;
30+
use DesignPatterns\ServiceLocator\ServiceLocator;
31+
use \PHPUnit_Framework_TestCase as TestCase;
32+
33+
class ServiceLocatorTest extends TestCase
34+
{
35+
/**
36+
* @var LogService
37+
*/
38+
private $logService;
39+
40+
/**
41+
* @var DatabaseService
42+
*/
43+
private $databaseService;
44+
45+
/**
46+
* @var ServiceLocator
47+
*/
48+
private $serviceLocator;
49+
50+
public function setUp()
51+
{
52+
$this->serviceLocator = new ServiceLocator();
53+
54+
$this->logService = new LogService();
55+
$this->databaseService = new DatabaseService();
56+
}
57+
58+
public function testHasServices()
59+
{
60+
$this->serviceLocator->add('DesignPatterns\ServiceLocator\LogServiceInterface', $this->logService);
61+
$this->serviceLocator->add('DesignPatterns\ServiceLocator\DatabaseServiceInterface', $this->databaseService);
62+
63+
$this->assertTrue($this->serviceLocator->has('DesignPatterns\ServiceLocator\LogServiceInterface'));
64+
$this->assertTrue($this->serviceLocator->has('DesignPatterns\ServiceLocator\DatabaseServiceInterface'));
65+
66+
$this->assertFalse($this->serviceLocator->has('DesignPatterns\ServiceLocator\FakeServiceInterface'));
67+
}
68+
69+
public function testServicesWithObject()
70+
{
71+
$this->serviceLocator->add('DesignPatterns\ServiceLocator\LogServiceInterface', $this->logService);
72+
$this->serviceLocator->add('DesignPatterns\ServiceLocator\DatabaseServiceInterface', $this->databaseService);
73+
74+
$this->assertSame($this->logService, $this->serviceLocator->get('DesignPatterns\ServiceLocator\LogServiceInterface'));
75+
$this->assertSame($this->databaseService, $this->serviceLocator->get('DesignPatterns\ServiceLocator\DatabaseServiceInterface'));
76+
}
77+
78+
public function testServicesWithClass()
79+
{
80+
$this->serviceLocator->add('DesignPatterns\ServiceLocator\LogServiceInterface', get_class($this->logService));
81+
$this->serviceLocator->add('DesignPatterns\ServiceLocator\DatabaseServiceInterface', get_class($this->databaseService));
82+
83+
$this->assertNotSame($this->logService, $this->serviceLocator->get('DesignPatterns\ServiceLocator\LogServiceInterface'));
84+
$this->assertInstanceOf('DesignPatterns\ServiceLocator\LogServiceInterface', $this->serviceLocator->get('DesignPatterns\ServiceLocator\LogServiceInterface'));
85+
86+
$this->assertNotSame($this->databaseService, $this->serviceLocator->get('DesignPatterns\ServiceLocator\DatabaseServiceInterface'));
87+
$this->assertInstanceOf('DesignPatterns\ServiceLocator\DatabaseServiceInterface', $this->serviceLocator->get('DesignPatterns\ServiceLocator\DatabaseServiceInterface'));
88+
}
89+
90+
public function testServicesNotShared()
91+
{
92+
$this->serviceLocator->add('DesignPatterns\ServiceLocator\LogServiceInterface', $this->logService, false);
93+
$this->serviceLocator->add('DesignPatterns\ServiceLocator\DatabaseServiceInterface', $this->databaseService, false);
94+
95+
$this->assertNotSame($this->logService, $this->serviceLocator->get('DesignPatterns\ServiceLocator\LogServiceInterface'));
96+
$this->assertInstanceOf('DesignPatterns\ServiceLocator\LogServiceInterface', $this->serviceLocator->get('DesignPatterns\ServiceLocator\LogServiceInterface'));
97+
98+
$this->assertNotSame($this->databaseService, $this->serviceLocator->get('DesignPatterns\ServiceLocator\DatabaseServiceInterface'));
99+
$this->assertInstanceOf('DesignPatterns\ServiceLocator\DatabaseServiceInterface', $this->serviceLocator->get('DesignPatterns\ServiceLocator\DatabaseServiceInterface'));
100+
}
101+
}
102+

0 commit comments

Comments
 (0)