Dependency Injection
And Inversion of Control
study-group-05
Created by Juan Manuel Torres / @onema / onema.io
Follow Live
http://slides.com/onema/dependency-injection/live
What is Dependency Injection? (DI)
Dependency injection menas providing objects the varaibles it needs instead of having it construct them. [1] [2]
It's a very useful technique for testing, since it allows dependencies to be mocked or stubbed out. [1]
NOT DEPENDENCY INJECTION
Remember our "Hello World" Translation controller?
If we would have been lazy we could have done it like this:
class HelloWorldController {
private $request;
public function __construct() {
$this->request = Request::createFromGlobals();
}
public function translateAction() {
$language =
$this->request->attributes->get('lang');
// . . .
return $response;
}
}
We are creating object dependencies in the constructor. Hard to customize
$controller = HelloWorldController();
$response = $controller->translateAction();
Very easy to use
DEpendency Injection
class HelloWorldController {
private $request;
public function __construct(Request $request) {
$this->request = $request;
}
public function translateAction() {
$language =
$this->request->attributes->get('lang');
// . . .
return $response;
}
}
We inject the Request dependency via the constructor. Easy to customize
$request = Request::createFromGlobals();
$controller = HelloWorldController($request);
$response = $controller->translateAction();
Not so easy to use
That is it!
we are done here...
If we want to modify the request
We could use Globals in order to make any modifications to the request.
Why is the first example not a good idea?
class HelloWorldController {
private $request;
public function __construct() {
$this->request =
Request::createFromGlobals();
global $attr;
if (isset($attr)) {
$request->attributes->add($attr);
}
}
// . . .
}
$attr = ['default_language' => 'EN']
$controller = HelloWorldController();
$response = $controller->translateAction();
Bad global, BAD!
If we want to modify the request
We could modify the request by passing custom attributes array.
Why is the first example not a good idea?
class HelloWorldController {
private $request;
public function __construct($attr = null)
{
$this->request =
Request::createFromGlobals();
if (isset($attr)) {
$request->attributes->add($attr);
}
}
// . . .
}
$attr = ['default_language' => 'EN']
$controller = HelloWorldController($attr);
$response = $controller->translateAction();
Is OK, but not a great solution
If we want to modify the request Implementation?
We could use a global registry.
Why is the first example not a good idea?
class HelloWorldController {
private $request;
public function __construct()
{
$this->request =
Registry::get('request');
}
// . . .
}
Registry::set('request', new CustomRequest)
$controller = HelloWorldController();
$response = $controller->translateAction();
Now we depend on
the Registry
Advantages:
- Decoupling: No need for us to know much about our dependecies (Maybe we could use an interface here?)
- Dependency inversion: Configuration is natural
- Reusability: Could use 3rd party libs (Interfaces anyone?)
- Simple Testing: Mock Request for Testing
class HelloWorldController {
private $request;
public function __construct($request)
{
$this->request = $request;
}
// . . .
}
$request = Request::createFromGlobals();
$request->attributes->add($parameters);
$controller = HelloWorldController($request);
$response = $controller->translateAction();
inject the dependecy to the constructor
Disadvantages:
- Complex initialization
$request = Request::createFromGlobals();
$request->attributes->add($parameters);
$controller = HelloWorldController($request);
$response = $controller->translateAction();
inject the dependecy to the constructor
$controller = HelloWorldController();
$response = $controller->translateAction();
This is harder:
Than this:
How?:
- Mocking or Stubbing Objects
class HelloWorldControllerTest extends PHPUnit_Framework_TestCase
{
public function testTranslation()
{
// Create a Mock Object for the Request class
$mockRequest = $this->getMock('Request');
$mockRequest->attributes->add(['lang' => 'EN']);
$controller = new HelloWorldController($mockRequest);
$response = $controller->translateAction();
$this->assertEquals('Hello World!',
$response->getContent(),
'Invalid English message.')
}
}
Simplify Testing
A few ways to inject Dependencies
- Constructor Injection
- Setter Injection
- Property Injection
Setter Injection
We use a setter instead of using the constructor
class HelloWorldController {
private $request;
public function __construct()
{
// . . .
}
public function setRequest($request)
{
$this->request = $request;
}
// . . .
}
$request = Request::createFromGlobals();
$controller = HelloWorldController();
$controller->setRequest($request);
$response = $controller->translateAction();
Parameter Injection
We set the class property directly, this requires the property to be public
class HelloWorldController {
public $request;
public function __construct()
{
// . . .
}
// . . .
}
$request = Request::createFromGlobals();
$controller = HelloWorldController();
$controller->request = $request;
$response = $controller->translateAction();
Dependency Injection Container
A Service Container (or dependency injection container) is simply a PHP object that manages the instantiation of services (i.e. objects).
Remember it was hard to Initialize classes?
A proper solution to simplify the creation of complex objects is to use a Dependency Injection Container
class Container
{
public function getRequest()
{
$request=Request::createFromGlobals();
$request->attributes->add([
'lang' => 'EN'
]);
return $request;
}
public function getHelloController()
{
$request = $this->getRequest();
return HelloWorldController($request);
}
}
$container = new Container();
$controller = $container->getHelloController();
$response = $controller->translateAction();
Use a third party DI Container
- Pimple
- Twittee
- Bullet Three
- Symfony2 Service Container
Using Pimple
THE END
BY Juan Manuel Torres / onema.io / @onema / kinojman@gmail.com
Reference
- StackOverflow: What is dependency Injection?
- Dependency Injection Demystified
- Inversion of Control Containers and the Dependency Injection pattern
- Dependency Injection in PHP
- Dependency Injection with PHP5.3... with a bit of PHP5.4
- PHPUnit Chapter 10. Mock Objects
- What is Dependency Injection?
- Pimple A simple DI Container
- Symfony Dependency Injection Component
- A Dependency Injection Container in a Tweet
Dependency Injection
By Juan Manuel Torres
Dependency Injection
- 1,597