php | tek - Chicago, Chic ago, May 16-18, 2007
Dependency Injection Modular it i t y for Reuse
Jeff Moore ht tp://w w w.proc .procata ata.com .com//
Interdependence Te chn chnol olog ogyy h as made us we weal alth thyy Nobody Nobo dy in our so socie ciett y is selfself-suffi sufficie cient nt We specialize We build bui ld on the work of ot o t he herrs
So u rce of Sou Materia Mater iall We al altt h Interchangeable Interchange able parts Mass product production ion Machine tool toolss
A Ta Ta le of o f Two Two C l o c k s Master Clockmaker
Apprentice
We ll organi org anized zed
Haphazard
Moree reliable Mor relia ble
Lesser qualit qualityy
A Mo de r n Cl Cloo ck Cheap Reliable Available Predictable Ugly?
Software Craftsmanship Each piec piecee large largely ly unique Large var variation iation in qualit y be bett wee eenn master mas ter and ap appr pren entt ice
Software Industrialization Linux Li nux,, Apach Apache, e, MySQL, MySQL, and PHP PHP is a thi thinn lay laye r over many C librarie librar iess PEAR PEA R or Smart y Drup Dr upal al or WordP WordPre ress s
Reus Re usable able C o de Interchangeable Interchange able Parts Parts Mass Product Production ion Machi Ma chine ne To To ol olss
Craftsmanship is h e re to st s t ay Higherr level Highe leve l code is h ard arder er to reuse Reuse requi requirre s customization customizat ion and configuration
Drawb Dr awback ackss to Re Reuse use
Yo u r Fate Fate in i n S ome omeoo ne Else’s Hands Reusing code in intt roduces risk riskyy dependencies Change in dir direc ectt ion Abandonment Missing Missi ng ne new w technolo technologies gies Infrequent updates
Reason Re asonss No Nott to Re Reuse use Doesn Doe sn’’t do e xac xactt ly what wh at you nee needd It does doe s too much much Not extensibl e xtensible e Nott configurable No configurable L ack of support Lack of docum documen entat tation ion
G o o d C o de Is Ha Harrd to Fi Fi nd Eva valuat luation ion costs cos ts are are high high Fe w packages with wi th repu reputation tation for qualit y and reusabili eusabilitt y Hard to separate sepa rate th thee whe at f rom th the e chaff
Pro roduci ducing ng Re Reus usable able C o de is Har Ha rd Solve Sol vess a problem Simple Configurable Documented Flexible
H o w t o Prom romoo te Reuse?
De pe pend ndee nc ncyy Inj Injee c t ion
Wh at is a De pe pende ndency ncy??
You can c an’’t use that th at wit wi t h ou outt this I com come with strings st rings attac att ached hed
A Ta Ta ng le d We We b Of O f Dependencies
FeedFetcher Class FeedFetcher {}
Fe tch a fee feed, d, given gi ven an Url This could be a per perform formance ance bot t le neck?
FeedCache class FeedFetcher { protected $cache; function __construct() { $this->cache = new FeedCache(); } }
Fe e dCache C on onfifigu gurat ration ion Maximum Ma ximum age Cache dir direc ector toryy Er ror hand handling ling
H o w do we we C o nfigu gurre FeedCache? class FeedFetcher { protected $cache; function __construct() { $this->cache = new FeedCache(); } }
Intt roduce C ons In onstt an ants ts?? define('FEED_CACHE_DIR', '/tmp'); define('FEED_CACHE_AGE', 3600); define('FEED_CACHE_DEBUG', true);
Th e Proble roblems ms w i t h Constants What if you nee needd dif differen ferentt confi configurat guration ion options opt ions for different dif ferent feeds fee ds?? Global
H o w do we we C o nfigu gurre FeedCache? class FeedFetcher { protected $cache; function __construct() { $this->cache = new FeedCache(); } }
Add Cach Add Cachee C o nfigu gurat ratio ionn Optt ions to Fe e dFe tche Op tcher r class FeedFetcher { protected $cache; function __construct($cacheDir, $maxAge) { $this->cache = new FeedCache($cacheDir, $maxAge); } }
Bad AP AP I
Bloated interf inter f ace for Fee FeeddFe tcher Can’’t use Fee Can FeedF dFee tche tcherr with wi thou outt Fee FeedCac dCache he
H o w Do Do We We Hav Havee Mo Mo re Th an One K i nd of Cache Cach e? class FeedFetcher { protected $cache; function __construct() { $this->cache = new FeedCache(); } }
Pos ossi sible ble Cache Cl Clas asse sess File Sys Sys tem Cach Cache e Data atabas basee Cache Sharre d Memor Sha emoryy Cache Null Cache
The Dri Drivve r Pat atter ternn class FeedCache { static function getCache( $driver, $options = array()) { $file = ‘/drivers/’ . $driver . ‘.php’; require_once $file; $classname = ucfirst($driver) . 'Cache'; return new $classname($opt $classname($options); ions); } }
Defini ning ng t he Dr Dr i ve ver r class DriverCommon { protected $options; function __construct($options) { $this->options = $options; } } class FileCache extends DriverCommon {}
Using t he Dri Drivve r class FeedFetcher { protected $cache; protected $options; function __construct($options = array()) { $this->cache = FeedCache::getCache( $options[‘driver’], $options); } }
St i l l h av avee a problem w i t h Connfigurat Co guration ion Op Optt ions Fee eeddFe tcher API API is still st ill coupled couple d to the caching confi configurat guration ion options opt ions Fee eeddCache rece eceii ves configuration configuration options opt ions it might not care abo abouu t Amorpho morphous us opt options ions array complicates both bo th APIs APIs
Difffic ul Di ultt t o Te s t o r Extend Mapping be bett wee eenn driver dri ver name, name, file file and classs name clas name Hardd to injec Har injectt an arbi arbitt rar raryy dri ver class Each mock obje objecc t wou would ld req requi uirre a physic physical al file fil e
Ho w to Inje How Inje c t a Mo ck Fe e dCach Cachee f or Te s t i ng ng?? class FeedFetcher { protected $cache; function __construct() { $this->cache = new FeedCache(); } }
Fac actor toryy Me t h o ds ds?? class FeedFetcher { protected $cache; function __construct() { $this->cache = $this->createCache(); } function createCache() { } }
Use Inhe Inherr i t ance ance?? class MyFeedFetcher extends FeedFetcher { function createCache() { return new FileFeedCache(‘/tmp’); } }
Inhe In herr i tance ... No Not t Heav He avyy weight Was aste tess you yourr one chance cha nce at si single ngle inheritance Cumbersome API
Untangling Dependencies
H o w to Confi C onfigu gurre FeedCache? class FeedFetcher { protected $cache; function __construct() { $this->cache = new FeedCache(); } }
Bad Op Optt ions Connfigur Co figuree Fee FeeddCache through th rough Fee FeeddFetch Fetcher er Connfigur Co figuree Fee FeeddCache around Feed Fee dFetch Fetcher er Extend Exte nd Fee FeedF dFee tche tcherr to configure configure Fee FeedCac dCache he
Tu r n t h e Re Rell at atio ionsh nshii p Insid Ins idee Ou Out t
Do n’t pu Don pul l t he de pe pend ndee ncy In class FeedFetcher { protected $cache; function __construct() { $this->cache = new FeedCache(); } }
Inje c t or Push Injec Push t he de pe pend ndee ncy In class FeedFetcher { protected $cache; function __construct($cache) { $this->cache = $cache; } }
Configuration on t he Ou Outsid tside e $cache = new FileCache(‘/tmp’); $fetcher = new FeedFetcher($cache);
Fac actor tor Ou Outt an In Inter terff ace interface Cache {} class FileCache implements Cache {} class MemoryCache implements Cache {} class DbCache implements Cache {}
De pe pend nd on t he in intte rf rface, ace, nott th no thee Clas Classs class FeedFetcher { protected $cache; function __construct(Cache $cache) { $this->cache = $cache; } }
Use Adap Adapte terrs to Plugi Pl uginn Thi hirrd Par artt y Cach Cache e class CacheAdapter extends ThirdPartyCache implements Cache { } new FeedFetcher(new CacheAdapter(...));
Fe e dFe tche tcherr is No Now w Easy To Test new FeedFetcher(new NullCache());
No cach cachee confi configurat guration ion requ required ired to te te s t FeedFetcher No e xte xtensio nsionn or modifi modificat cation ion to Fee eeddFe tcher requi requirred for test tes t ing
Explicitt De pe Explici pend ndee ncie nciess Hidde n, implici Hidd implicitt dep epee ndencie ndenciess ma make ke code h ard to test tes t and hard ha rd to re re use Explici t ly decl Explicit declar aree depe depend ndencie enciess on the Interface Inter face of the class Easier to mai maint ntain ain
Al terr n at Alte atii ve s To De pe pend ndee nc ncyy Inj Injee c t ion
Servv ice Loc Ser Locaator class FeedFetcher { function __construct() { $this->cache = Locator::get('cache'); } }
C on onfifigu gurr i ng Wi t h a Locator Locator::set('cache', new FileCache(‘/tmp’)); $fetcher = new FeedFetcher();
Wh at is t he Di Diff fe ferre nce nce?? Locator::set('cacheDir', ‘/tmp’); ... $value = Locator::get('cacheDir') $_GLOBALS[‘cacheDir’] = ‘/tmp’; ... $value = $_GLOBALS[‘cacheDir’] define(‘CACHE_DIR’, ‘/tmp’); ... $value = CACHE_DIR
Servv ice Loc Ser Locaator Clients Clie nts have a dep epee ndency on the locator locat or Difficultt to integrate Difficul inte grate loc locators ators f rom mult ultiple iple partie partiess Easie r to use if you are usi Easier using ng single sing le instance ins tancess of an ob objec jectt t ype Easierr to use Easie use whe whenn in dee deepl plyy ne ness ted code Lesss fle Les fle xible
Drawbackss to Drawback De pe pend ndee nc ncyy Inj Injee c t ion All po poten tentt ial depe depend ndee ncies must must be instantiated L az azyy lo loading ading requ require iress prox proxyy obj objee cts in PHP Cons t ructor me Const me th thods ods can end up wi with th many para pa ram me ters
Containers
De pe pend ndee nc ncyy Inj Injee c t ion
Sep aratess ob Separate objec jectt confi configurat guration ion f rom ob objec ject t use us e
De pe pend ndee nc ncyy Inj Injee c t ion Container Take akess over the con co nfigurat guration ion and assembly assem bly of objec objects ts Use sess a domai domainn specific specific language to desc descrr ibe objects Codde Base Co Basedd XMLL Based XM Base d
Th e Va lue o f a C o n t ai aine ner r Bett ter for more Be more complic complicated ated objec objects ts?? Value ad added ded se serr v ice icess No mat mature ure con contai tainers ners for PHP I haven have n’t run in i n to a need nee d for one yet ye t
You don’t need a C o n t ai aine nerr to Tr y De pe pend ndee nc ncyy Inj Injee c t ion
Fluidd Inte Flui Interf rfac acee s
St and andar ardd Con C onfifigu gurat ratio ionn APII AP $controller->addParameter( new PostParameter('name', 'name'));
Whatt is nam Wha n amee? Why is i t ther the re twice t wice?? Confusing. Most common us usage age c ase for this thi s co code de
Constructor class PostParameter { function __contruct( $name, $binding = NULL) { $this->name = $name; $this->bindingLocation = $binding; } }
St andar andardd C ol olle lecc t ion Accessor function addParameter($param) { $this->params[] = $param; }
Intt ro In roduce duce Non-S Non-St an anda darrd Collectt ion Acc Collec Accee ssor function addParameter($param) { $this->params[] = $param; return $param; }
Move Op Optt ion ional al Para aram me te r to Se Sett ter class PostParameter { function __contruct($name) { $this->name = $name; } function setBinding($binding) { $this->bindingLocation = $binding; } }
Me th thod od Chaini Chaining ng Instead Inste ad of $controller->addParameter( new PostParameter('name', 'name'));
We now no w have h ave $controller->addParameter( new PostParameter('name')) ->setBinding(‘name’);
Adoptt Flu Adop Fluid id N ame s Instead Inste ad of $controller->addParameter( new PostParameter('name', 'name'));
We now no w have h ave $controller->DefineInput( new PostParameter('name')) ->bindToModel(‘name’);
Addd Th Ad Th e Commo Commo n De f aul ault t Case class PostParameter { function __contruct($name) { $this->name = $name; } function setBinding($binding = NULL) { $this->bindingLocation = isset($binding) ? $binding : $name; } }
More Und Undee rs t an anda dable ble?? Instead Inste ad of $controller->addParameter( new PostParameter('name', 'name'));
We now no w have h ave $controller->defineInput( new PostParameter('name')) ->bindToModel();
Dom omai ainn Spe Specifi cificc Languages Depe epend ndency ency Inject Injection ion Con Contai tainer ner Implemen ts a ge Implements gene nerr ic DS DSLL fo forr objec object t configuration Fluidd Interfaces Flui Interface s Implemen ts a cl Implements clas asss specifi spec ificc DS DSL for configuration
De pe pend ndee nc ncyy Inj Injee c t ion
Tr y it i t
Questions?