08448380779 Call Girls In Friends Colony Women Seeking Men
Dependency Inversion and Dependency Injection in PHP
1. Dependency Inversion
and
Dependency Injection
in PHP
Michael Toppa
University of Pennsylvania
Perelman School of Medicine
Information Services
August 11, 2011
5. The SRP is about objects that do one thing
The DIP is about how to wire them together
to create working, flexible software
6. Formal Definition of the DIP
● High level modules should not depend on low-
level modules. Both should depend on
abstractions.
● Abstractions should not depend on details.
Details should depend on abstractions.
This definition and the following example are from
Bob Martin's book “Agile Software Development”
7. Naïve model of a button and lamp
Lamp
Button
+ turnOn()
+ poll()
+ turnOff()
class Button {
private $lamp;
public function __construct(Lamp $lamp) {
$this->lamp = $lamp;
}
public function poll() {
if (/* some condition */) {
$this->lamp->turnOn();
}
}
}
8. This solution violates the DIP
● Button depends directly on Lamp
● Changes to Lamp may require changes to Button
● Button is not reusable
● It can't control, for example, a Motor
● The high level abstraction is missing
● “the truths that do not vary when the details are
changed”
● “To detect an on/off gesture from a user and relay
that gesture to a target object”
10. Dependency Inversion Applied
<<interface>>
Button SwitchableDevice
+ poll() + turnOn()
+ turnOff()
Lamp
This is the Abstract Server pattern
11. class Lamp implements SwitchableDevice {
public function turnOn() {
// code
}
public function turnOff() {
// code
}
}
class Button {
private $switchableDevice;
public function __construct(SwitchableDevice $switchableDevice) {
$this->switchableDevice = $switchableDevice;
}
public function poll() {
if (/* some condition */) {
$this->switchableDevice->turnOn();
}
}
}
12. What it means
● Neither Button nor Lamp “own” the interface
● Buttons can now control any device that
implements SwitchableDevice
● Lamps and other SwitchableDevices can now
be controlled by any object that accepts a
SwitchableDevice
13. Patterns that implement the DIP
● Abstract Server
● Constructor injection
● Setter injection
● Interface injection
● Factory pattern
● Adapter pattern
● Service locator pattern
● Contextualized lookup (push)
14. Never do this
class MySqlDb {
public function __construct($username, $password, $host) {
// .. snip ..
}
public function executeSql($sql) {
// .. snip ..
}
}
class BookReader {
private $_db;
public function __construct() {
$this->_db = new MySqlDb(DB_USER, DB_PASS, DB_HOST);
}
public function getChapters() {
return $this->_db->executeSql('SELECT name FROM chapter');
}
}
Example from Crafty documentation
http://phpcrafty.sourceforge.net/documentation.php
15. In addition to other DIP violations,
you cannot write unit tests for that code
16. Constructor injection solution
interface Db {
public function executeSql($sql);
}
class MySqlDb implements Db {
public function __construct($username, $password, $host) {
// .. snip ..
}
public function executeSql($sql) {
// .. snip ..
}
}
class BookReader {
private $_db;
public function __construct(Db $db) {
$this->_db = $db;
}
public function getChapters() {
return $this->_db->executeSql('SELECT name FROM chapter');
}
}
17. Setter injection solution
class BookReader {
private $_db;
public function __construct() {
}
public function setDb(Db $db) {
$this->_db = $db;
}
public function getChapters() {
return $this->_db->executeSql('SELECT name FROM chapter');
}
}
18. Which to use?
● Constructor injection gives you a valid object,
with all its dependencies, upon construction
● But constructor injection becomes hard to read
and use when there are more than a few
objects to inject
● This is especially true when subclassing
● More about this in an upcoming slide...
19. If class A depends on class B,
and class B depends on class C,
class A should be blissfully unaware of class C
20. This supports loose coupling
and
lets you do dependency injection “just in time”
21. To do this without going insane,
you need an injection container
22. Example from Shashin
class Lib_ShashinContainer {
// ...
public function __construct($autoLoader) {
$this->autoLoader = $autoLoader;
}
public function getDatabaseFacade() {
if (!$this->dbFacade) {
$this->dbFacade = new ToppaDatabaseFacadeWp($this->autoLoader);
}
return $this->dbFacade;
}
public function getClonablePhoto() {
if (!$this->clonablePhoto) {
$this->getDatabaseFacade();
$this->clonablePhoto = new Lib_ShashinPhoto($this->dbFacade);
}
return $this->clonablePhoto; I am making the objects properties of the
} container, because they happen to be
} immutable objects, so they are reusable
23. Container Benefits
● Loose coupling - objects don't have to worry
about the dependencies of the objects they use
● Facilitates portability - specific implementations
or subtypes are centralized in the container
● Dependencies are clearly articulated in one
place
● Simple design
24. Constructor vs setter injection:
my personal preference
● Start with constructor injection
● As your design evolves, switch to setter
injection once there are more than 2 objects to
inject
● If you rely on an injection container, you don't
have to worry about forgetting to call a required
setter
25. Injection containers for PHP
● It's not hard to roll your own
● There are also many available for PHP
● Bucket
● PicoContainer
● Crafty
● Pimple
● Symfony comes with one
27. What to do when you need
a new object inside a loop
One solution is cloning
28. Example from Shashin
class Admin_ShashinSynchronizerPicasa extends Admin_ShashinSynchronizer {
// …
public function syncAlbumPhotos(array $decodedAlbumData) {
// …
foreach ($decodedAlbumData['feed']['entry'] as $entry) {
$photoData = $this->extractFieldsFromDecodedData($entry, $photoRefData,
'picasa');
// ...
$photo = clone $this->clonablePhoto;
$photo->set($photoData);
$photo->flush();
}
// ...
}
// ...
}
https://github.com/toppa/Shashin/
29. What if you need a new object inside a loop,
but can't know the subtype you'll need
ahead of time?
Let the injection container figure it out
30. Example from Shashin
class Public_ShashinLayoutManager {
// ...
public function setTableBody() {
// …
for ($i = 0; $i < count($this->collection); $i++) {
// ...
$dataObjectDisplayer = $this->container->getDataObjectDisplayer(
$this->shortcode,
$this->collection[$i],
$this->thumbnailCollection[$i]
);
$this->tableBody .= $dataObjectDisplayer->run();
// ...
}
// ...
getDataObjectDisplayer() uses the passed in
}
arguments to determine which subtype of
// ...
DataObjectDisplayer to return
}
31. What makes an injection container
different from the factory pattern?
32. Good question!
● An injection container can be used to generate
more than one class of objects
● A factory generates objects of a single class (or set
of class subtypes)
● An injection container consists of methods that
create and return objects – it's a simple design
● A full factory pattern implementation can be
complex, and hard to test*
● They're not mutually exclusive – you can use a
container to create and inject a factory!
See http://blog.astrumfutura.com/2009/03/the-case-for-dependency-injection-part-1/
33. Will this proliferation of objects eat
up all the server memory?
● No
● “In PHP 5, the infrastructure of the object model
was rewritten to work with object handles. Unless
you explicitly clone an object by using the clone
keyword you will never create behind the scene
duplicates of your objects. In PHP 5, there is neither
a need to pass objects by reference nor assigning
them by reference.”
● From http://devzone.zend.com/article/1714
34. A web of collaborating objects
● Dependency injection is all about a
“composition” approach to OO design
● From Growing Object Oriented Software,
Guided by Tests:
"An object oriented system is a web of
collaborating objects... The behavior of the
system is an emergent property of the
composition of the objects - the choice of
objects and how they are connected... Thinking
of a system in terms of its dynamic
communication structure is a significant mental
shift from the static classification that most of us
learn when being introduced to objects."