Migrating to
Dependency Injection
@josh_adell
www.servicetrade.com
blog.everymansoftware.com
github.com/jadell/neo4jphp
https://joind.in/10419
Legacy Code

Began with PHP 5.0 (now 5.3)
~80k LoC
Mixed PHP & HTML
3 x functions.php with ~9k LoC each
magic_quotes AND r...
class UserRepository {
public function findUser($id) {
$db = new Database(/* connection params*/);
$userInfo = $db->query(...
Dependency Injection (DI)
Components should not
create the other components
on which they depend.
Injector injects Depende...
class PageController {
public function __construct(
Database $db) {
$this->repo = new Repository($db);
}
}
===============...
DI Container

Wires objects together
I like Pimple: http://pimple.sensiolabs.org/
Parameters
Services / Shared Objects
Fac...
$di = new Pimple();
// Parameters
$di['dbHost'] = "localhost";
$di['dbUser'] = "username";
$di['dbPass'] = "password";
// ...
// Factory Services
$di['user'] = $di->factory(function () {
return new User();
});
// Factory Callbacks
$di['userFactory'...
Re-architect the application
so that object instantiation
only occurs in the DI Container.
Re-architect the application
so...
Why Bother?

Testing / Quality
Maintenance
Extensibility
Flexibility
One Step at a Time
1.
2.
3.

Create a DI factory method for the class
Replace "new" with calls to the DI factory method
a....
class UserRepository {
public function findUser($id) {
$db = new Database(/* connection params*/);
$userInfo = $db->query(...
Step #1: DI Container method
$di['userRepository'] = function () {
return new UserRepository();
};
Step #2: Replace “new”
class UserController {
public function getUser() {
global $di;
$repo = $di['userRepository'];
retur...
Step #3a: Move shared objects
class UserRepository {
public function __construct() {
$this->db = new Database(/* connectio...
Step #3b: Move factory creation
class UserRepository {
public function __construct() {
$this->db = new Database(/* connect...
Step #4a: Pass in shared objects
$di['database'] = function () {
return new Database();
};
$di['userRepository'] = functio...
Step #4b: Pass in factory callbacks
$di['userFactory'] = $di->protect(function () {
return new User();
});
$di['userReposi...
Done with UserRepository!
class UserRepository {
public function __construct(Database $db, callable $userFactory) {
$this-...
Repeat for UserController
$di['userController'] = function ($di) {
return new UserController($di['userRepository']);
};
==...
That seems like a lot of steps
New Requirement

Every time a user submits a report,
send an email.
Setup Event Publisher
class User {
public function __construct(
EventEmitter $ee) {
$this->event = $ee;
}
public function ...
Setup Event Subscribers
$di['eventEmitter'] = function () {
return new EventEmitter();
};
$di['emailService'] = function (...
I have to admit, it's getting better
Questions?
class Presentation {
public function __construct(Presenter $presenter) {
$this->presenter = $presenter;
}
publi...
@josh_adell
www.servicetrade.com
blog.everymansoftware.com
github.com/jadell/neo4jphp
http://pimple.sensiolabs.org/
http:/...
Upcoming SlideShare
Loading in …5
×

Migrating to dependency injection

2,159 views

Published on

"Dependency injection" (DI) seems like one of those hot buzzwords that will solve all your problems. But what is DI really? How does it help keep code clean and maintainable? And how do you take a legacy codebase and rewrite it to take advantage of DI? This talk takes an application written without DI and walks through the steps for "injecting" DI into the code. Learn the difference between "dependency injection" and "dependency injection containers". See how DI makes things like event-driven architectures simple to implement. And learn how DI leads to code that is easier to debug and test.

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
2,159
On SlideShare
0
From Embeds
0
Number of Embeds
72
Actions
Shares
0
Downloads
15
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Migrating to dependency injection

  1. 1. Migrating to Dependency Injection
  2. 2. @josh_adell www.servicetrade.com blog.everymansoftware.com github.com/jadell/neo4jphp https://joind.in/10419
  3. 3. Legacy Code Began with PHP 5.0 (now 5.3) ~80k LoC Mixed PHP & HTML 3 x functions.php with ~9k LoC each magic_quotes AND register_globals No abstract classes or interfaces Tightly coupled components No tests
  4. 4. class UserRepository { public function findUser($id) { $db = new Database(/* connection params*/); $userInfo = $db->query(/* User query with $id */); $user = new User(); $user->setProperties($userInfo); return $user; } } ======================================================== class UserController { public function getUser() { $repo = new UserRepository(); return $repo->findUser($_GET['id']); } }
  5. 5. Dependency Injection (DI) Components should not create the other components on which they depend. Injector injects Dependencies into Consumers
  6. 6. class PageController { public function __construct( Database $db) { $this->repo = new Repository($db); } } ======================================================== class PageController { public function __construct( Repository $repo) { $this->repo = $repo; } }
  7. 7. DI Container Wires objects together I like Pimple: http://pimple.sensiolabs.org/ Parameters Services / Shared Objects Factory Services Factory Callbacks
  8. 8. $di = new Pimple(); // Parameters $di['dbHost'] = "localhost"; $di['dbUser'] = "username"; $di['dbPass'] = "password"; // Services / Shared Objects $di['database'] = function ($di) { return new Database($di['dbHost'], $di['dbUser'], $di ['dbPass']); }; $di['userRepository'] = function ($di) { return new UserRepository($di['database]); };
  9. 9. // Factory Services $di['user'] = $di->factory(function () { return new User(); }); // Factory Callbacks $di['userFactory'] = $di->protect(function ($name) { return new User($name); });
  10. 10. Re-architect the application so that object instantiation only occurs in the DI Container. Re-architect the application so the DI Container is only referenced from the DI Container.
  11. 11. Why Bother? Testing / Quality Maintenance Extensibility Flexibility
  12. 12. One Step at a Time 1. 2. 3. Create a DI factory method for the class Replace "new" with calls to the DI factory method a. b. Move all shared objects to the constructor Move all factory creations to anonymous function in the constructor a. b. Pass in shared objects from the DI Container Pass in factories callbacks from the DI Container 4. Repeat from Step 1 for all classes
  13. 13. class UserRepository { public function findUser($id) { $db = new Database(/* connection params*/); $userInfo = $db->query(/* User query with $id */); $user = new User(); $user->setProperties($userInfo); return $user; } } ======================================================== class UserController { public function getUser() { $repo = new UserRepository(); return $repo->findUser($_GET['id']); } }
  14. 14. Step #1: DI Container method $di['userRepository'] = function () { return new UserRepository(); };
  15. 15. Step #2: Replace “new” class UserController { public function getUser() { global $di; $repo = $di['userRepository']; return $repo->findUser($_GET['id']); } }
  16. 16. Step #3a: Move shared objects class UserRepository { public function __construct() { $this->db = new Database(/* connection params*/); } public function findUser($id) { $userInfo = $this->db->query(/* User query with $id */); $user = new User(); $user->setProperties($userInfo); return $user; } }
  17. 17. Step #3b: Move factory creation class UserRepository { public function __construct() { $this->db = new Database(/* connection params*/); $this->userFactory = function () { return new User(); }; } public function findUser($id) { $userInfo = $this->db->query(/* User query with $id */); $user = call_user_func($this->userFactory); $user->setProperties($userInfo); return $user; } }
  18. 18. Step #4a: Pass in shared objects $di['database'] = function () { return new Database(); }; $di['userRepository'] = function ( $di) { return new UserRepository( $di['database']); }; ======================================================== class UserRepository { public function __construct( Database $db) { $this->db = $db; $this->userFactory = function () { return new User(); }; } // ...
  19. 19. Step #4b: Pass in factory callbacks $di['userFactory'] = $di->protect(function () { return new User(); }); $di['userRepository'] = function ($di) { return new UserRepository($di['database'], $di['userFactory']); }; ======================================================== class UserRepository { public function __construct(Database $db, callable $userFactory) { $this->db = $db; $this->userFactory = $userFactory; } // ...
  20. 20. Done with UserRepository! class UserRepository { public function __construct(Database $db, callable $userFactory) { $this->db = $db; $this->userFactory = $userFactory; } public function findUser($id) { $userInfo = $this->db->query(/* User query with $id */); $user = call_user_func($this->userFactory); $user->setProperties($userInfo); return $user; } }
  21. 21. Repeat for UserController $di['userController'] = function ($di) { return new UserController($di['userRepository']); }; ======================================================== class UserController { public function __construct(UserRepository $repo) { $this->userRepo = $repo; } public function getUser() { global $di; $repo = $di['userRepository']; return $this->userRepo->findUser($_GET['id']); } }
  22. 22. That seems like a lot of steps
  23. 23. New Requirement Every time a user submits a report, send an email.
  24. 24. Setup Event Publisher class User { public function __construct( EventEmitter $ee) { $this->event = $ee; } public function submitReport(Report $report) { // ... $this->event->publish('newReport', $this, $report); } }
  25. 25. Setup Event Subscribers $di['eventEmitter'] = function () { return new EventEmitter(); }; $di['emailService'] = function ($di) { return new EmailService(); }; $di['userFactory'] = $di->protect(function () use ($di)) { $di['eventEmitter']->subscribe('newReport', $di['emailService']); return new User($di['eventEmitter']); });
  26. 26. I have to admit, it's getting better
  27. 27. Questions? class Presentation { public function __construct(Presenter $presenter) { $this->presenter = $presenter; } public function askQuestion(Question $question) { return $this->presenter->answer($question); } }
  28. 28. @josh_adell www.servicetrade.com blog.everymansoftware.com github.com/jadell/neo4jphp http://pimple.sensiolabs.org/ http://martinfowler.com/articles/injection.html https://joind.in/10419

×