adding dependency injection to legacy applications
DESCRIPTION
Dependency Injection (DI) is a fantastic technique, but what if you what to use dependency injection in your legacy application. Fear not! As someone who as done this very thing, I will show how you can successful and incrementally add DI to any application. I will present a number of recipes and solutions to common problems and give a tour of the various PHP DI projects and how they can help.TRANSCRIPT
![Page 1: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/1.jpg)
Adding Dependency Injection To Legacy
Applications
![Page 2: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/2.jpg)
• Sam Hennessy• Originally from the UK• Now live in Denver, CO• Software Architect at i3logix• Ex-Pro Services Consultant For Zend• Regular at Front Range PHP Users Group
![Page 3: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/3.jpg)
Who Is i3logix?
• Involved in many different markets• Privately funded• SaaS
Always looking for good people!
![Page 4: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/4.jpg)
Inversion of Control
![Page 5: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/5.jpg)
Dependency Injection
![Page 6: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/6.jpg)
Why?
![Page 7: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/7.jpg)
![Page 8: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/8.jpg)
Built-InFlexibility
![Page 9: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/9.jpg)
interface Calc { public function __invoke($left, $right);}
class AddCalc implements Calc { public function __invoke($left, $right) { return $left + $right; }}
class SubCalc implements Calc { public function __invoke($left, $right) { return $left - $right; }}
![Page 10: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/10.jpg)
class Calculator{ public function __invoke(Calc $calc, $left, $right){ return $calc($left, $right); }}
$calculator = new Calculator();echo $calculator(new AddCalc(), 2, 1), "\n";echo $calculator(new SubCalc(), 2, 1), "\n";
![Page 11: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/11.jpg)
De-Coupling
![Page 12: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/12.jpg)
Have Clear Goals
![Page 13: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/13.jpg)
![Page 14: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/14.jpg)
It’sonly
a Tool
![Page 15: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/15.jpg)
INJECTION
TECHNIQUES
![Page 16: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/16.jpg)
Global Namespace, Off Limits!
![Page 17: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/17.jpg)
class Status { public function get($system) { $client = new Zend_Http_Client(); $r = $client->setUri(SERVICE_URI) ->setParameterGet(compact('system')) ->request('GET');
if ($r->isSuccessful()) { return $r->getBody();
} return false; }}
![Page 18: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/18.jpg)
Constructor Injection
Vs. Setter/Property
Injection
![Page 19: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/19.jpg)
class StatusSetterInject{ protected $client;
public function setClient(Zend_Http_Client $client){ $this->client = $client; }
public function get($system){ $r = $this->client->setUri(SERVICE_URI) ->setParameterGet(compact('system')) ->request('GET');
if ($r->isSuccessful()){ return $r->getBody();
} return false; }}
![Page 20: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/20.jpg)
class StatusConstructInject{ protected $client;
public function __construct(Zend_Http_Client $client){ $this->client = $client; }
public function get($system){ $r = $this->client->setUri(SERVICE_URI) ->setParameterGet(compact('system')) ->request('GET');
if($r->isSuccessful()){ return $r->getBody(); } return false; }}
![Page 21: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/21.jpg)
Init Pattern
![Page 22: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/22.jpg)
class StatusInit{ protected $client;
public function __construct(Zend_Http_Client $client){ $this->client = $client; $this->init(); }
public function init(){ //Left empty }
public function get($system){ $r = $this->client->setUri(SERVICE_URI) ->setParameterGet(compact('system')) ->request('GET');//... }}
![Page 23: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/23.jpg)
Op
tion
al
Inje
ctio
n
![Page 24: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/24.jpg)
class StatusConstructInjectOptional{ protected $client;
public function __construct( Zend_Http_Client $client = null){ if($client === null){ $client = new Zend_Http_Client(); } $this->client = $client; }
public function get($system){ $r = $this->client->setUri(SERVICE_URI) ->setParameterGet(compact('system')) ->request('GET');//... }}
![Page 25: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/25.jpg)
class StatusSetterInjectOptional{ protected $client;
public function setClient(Zend_Http_Client $client){ $this->client = $client; }
protected function getClient(){ if ($this->client === null){ $this->client = new Zend_Http_Client(); } return $this->client; }
public function get($system){ $r = $this->getClient()->setUri(SERVICE_URI) ->setParameterGet(compact('system')) ->request('GET');//...
![Page 26: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/26.jpg)
Class Name Override
![Page 27: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/27.jpg)
class StatusClassNameOverrideConstruct{ protected $clientClass;
public function __construct($class = 'Zend_Http_Client'){ $this->clientClass = $class; }
public function get($system){ $client = new $this->clientClass; $r = $this->getClient()->setUri(SERVICE_URI) ->setParameterGet(compact('system')) ->request('GET');
if($r->isSuccessful()){ return $r->getBody(); } return false; }}
![Page 28: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/28.jpg)
class StatusClassNameOverrideSetter{ protected $clientClass = 'Zend_Http_Client';
public function setClass($class){ $this->clientClass = $class; }
public function get($system){ $client = new $this->clientClass; $r = $this->getClient()->setUri(SERVICE_URI) ->setParameterGet(compact('system')) ->request('GET');
if($r->isSuccessful()){ return $r->getBody(); } return false; }}
![Page 29: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/29.jpg)
BUT I NEED TO CREATE OBJECTS?
![Page 30: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/30.jpg)
class PolyBad { public function foo($bar) { if (get_class($bar) === 'SomeOtherClass') { $tmp = new SomeClass(); } }}
![Page 31: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/31.jpg)
class PolyGood { public function foo(SomeClass $bar) { if ($baz instanceof SomeInterface) { // Do something } }}
![Page 32: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/32.jpg)
Factory
![Page 33: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/33.jpg)
class Factory{ public function create(){ return new SimpleClass(); }}
![Page 34: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/34.jpg)
Dynamic Factory
![Page 35: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/35.jpg)
abstract class FactoryAbstract{ protected abstract function getClassName();
public function create(){ $class = $this->getClassName(); $argList = func_get_args(); if (count($argList) < 1){ return new $class; } $rClass = new ReflectionClass($class); return $rClass->newInstanceArgs($argList); }}
![Page 36: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/36.jpg)
function __autoload($class){ $classPath = str_replace('_', '/', $class); $filePath = __DIR__."/src/$classPath.php";
if (file_exists($filePath)) return require $filePath;
if (substr($class, -7) !== 'Factory') return;
$subName = substr($class, 0, -8); eval("class $class extends FactoryAbstract{ protected function getClassName(){ return '$subName'; } }");}
![Page 37: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/37.jpg)
WHAT IS INJECTING THESE OBJECTS?
![Page 38: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/38.jpg)
Injector, Provider, Container
![Page 39: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/39.jpg)
Configuration
![Page 40: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/40.jpg)
<service id="bar" class="FooClass" shared="true" constructor="getInstance"> <file>%path%/foo.php</file> <argument>foo</argument> <argument type="service" id="foo" /> <argument type="collection"> <argument>true</argument> <argument>false</argument> </argument> <configurator function="configure" /> <call method="setBar" /> <call method="setBar"> <argument>foo</argument> <argument type="service" id="foo" /> <argument type="collection"> <argument>true</argument> <argument>false</argument> </argument> </call></service>
![Page 41: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/41.jpg)
Interfaces
![Page 42: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/42.jpg)
if($obj instanceof InjectDb){ $obj->setDb($this->serviceLocator->getDb());}
if($obj instanceof InjectTool){ $obj->setTool($this->serviceLocator->getTool());}
if($obj instanceof InjectClientBuilder){ $obj->setClientBuilder($this->getClientBuilder());}
if($obj instanceof InjectEvent){ $obj->setEvent($this->serviceLocator->getEvent());}
![Page 43: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/43.jpg)
Duck Typing
![Page 44: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/44.jpg)
abstract class Zend_View_Abstract implements Zend_View_Interface
//...public function registerHelper($helper, $name){//... if (!$helper instanceof Zend_View_Interface){ if (!method_exists($helper, $name)){ require_once 'Zend/View/Exception.php'; $e = new Zend_View_Exception( 'View helper must …'); $e->setView($this); throw $e; } } if (method_exists($helper, 'setView')){ $helper->setView($this); }//...
![Page 45: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/45.jpg)
Auto Wiring
![Page 46: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/46.jpg)
$rParmList = $methodReflection->getParameters();
foreach($rParmList as $rParm ){ $parmClass = $rParm->getClass();
if($parmClass !== null){ $bindConfig->getInjectionConfig() ->addMethodInstanceKey( $methodName, $parmClass->getName()); }else{ throw new Exception( "Not possible to configure automatically" ); }}
![Page 47: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/47.jpg)
Annotations
![Page 48: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/48.jpg)
/** * @Singleton */class LoggerSimple{ /** * @Inject Zend_Db_Adapter_Abstract */ public $db;
/** * @Inject * @Arg Zend_Db_Adapter_Abstract */ public function __construct($db){ $this->db = $db; } /** * @Inject * @Arg Zend_Db_Adapter_Abstract */ public function setDb($db){ $this->db = $db; }}
![Page 49: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/49.jpg)
/** * @ImplementedBy LoggerSimple */interface Logger{ public function log($message, $level);}
![Page 50: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/50.jpg)
$rClass = new ReflectionAnnotatedClass($config->getClass());
if($rClass->isInterface()){ if($rClass->hasAnnotation('ImplementedBy') === false){ throw new Exception(…); }
$impClass = $rClass->getAnnotation('ImplementedBy')->value;
if($impClass === null){ throw new Exception (…); } $config->setImplementationClass($impClass);}
![Page 51: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/51.jpg)
Win!Combin
e.
![Page 52: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/52.jpg)
BRIDGING THE GAPS
![Page 53: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/53.jpg)
Singleton
![Page 54: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/54.jpg)
class Db{ protected function __construct(){} protected function __clone(){}
public static function instance(){ static $instance = null; if($instance === null){ $instance = new static(); } return $instance; }}
![Page 55: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/55.jpg)
Singleton + Registry
![Page 56: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/56.jpg)
class Registry{ protected static $reg = array();
public static function add($key, $value){ static::$reg[$key] = $value; }
public static function get($key){ return static::$reg[$key]; }}
![Page 57: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/57.jpg)
class Registry{ protected static $reg = array();
public static function add($key, $value){ static::$reg[$key] = $value; }
public static function get($key){ $value = static::$reg[$key]; if(is_callable($value)){ return $value(); } return $value; }}
![Page 58: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/58.jpg)
Registry::add('db', function (){return new Db();});var_dump(Registry::get('db'));
![Page 59: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/59.jpg)
Service Locator
![Page 60: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/60.jpg)
class ServiceLocator{ protected function __construct(){} protected function __clone(){} public static function instance(){ static $instance = null; if($instance === null){ $instance = new static(); } return $instance; }
public function getDb(){ static $db = null; if($db === null){ $db = new Db(); } return $db; }}
![Page 61: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/61.jpg)
Coarse-Grained
Fine-Grained
![Page 62: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/62.jpg)
![Page 63: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/63.jpg)
Design by Contract
![Page 64: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/64.jpg)
class Logger{}
![Page 65: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/65.jpg)
abstract class LoggerAbstract{}
class LoggerEcho extends LoggerAbstract{}
class LoggerFile extends LoggerAbstract{}
![Page 66: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/66.jpg)
interface LoggerInterface{}
abstract class LoggerAbstract implements LoggerInterface{}
class LoggerEcho extends LoggerAbstract{}
class LoggerFile extends LoggerAbstract{}
class LoggerOddBall implements LoggerInterface{}
![Page 67: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/67.jpg)
class Logger{}
![Page 68: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/68.jpg)
abstract class Logger{}
class LoggerEcho extends Logger{}
class LoggerFile extends Logger{}
![Page 69: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/69.jpg)
interface Logger{}
abstract class LoggerAbstract implements Logger{}
class LoggerEcho extends Logger{}
class LoggerFile extends Logger{}
class LoggerOddBall implements Logger{}
![Page 70: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/70.jpg)
Prefer Composition Over Inheritance
![Page 71: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/71.jpg)
WHAT’S ALREADY OUT THERE?
![Page 72: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/72.jpg)
![Page 73: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/73.jpg)
![Page 74: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/74.jpg)
![Page 75: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/75.jpg)
YOU NEED A
GAME PLAN!
![Page 76: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/76.jpg)
![Page 77: Adding Dependency Injection to Legacy Applications](https://reader035.vdocuments.net/reader035/viewer/2022062419/557c1d6ad8b42a22218b4bbc/html5/thumbnails/77.jpg)
THANK YOU!
http://joind.in/3740