Dependency Injection Smells

8,327 views
8,155 views

Published on

Presentation about dependency injection smells in PHP.

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

No Downloads
Views
Total views
8,327
On SlideShare
0
From Embeds
0
Number of Embeds
3,800
Actions
Shares
0
Downloads
49
Comments
0
Likes
6
Embeds 0
No embeds

No notes for slide

Dependency Injection Smells

  1. 1. Dependency Injection SmellsFeaturing Zend,Symfonyand DoctrinecodeMatthias Noback - PHP developer and consultantDutch PHP Conference - 6/7/2013
  2. 2. Matthias NobackDutch developerConsultancy, training, writingClean code···2/44
  3. 3. What is a dependency?A needs a B to do its jobthereforeB is a dependency of A3/44
  4. 4. Dependency managementTo make sure that A will have a B, A can:4/44
  5. 5. 1. Create a B5/44
  6. 6. 2. Fetch a B6/44
  7. 7. 3. Receive a B7/44
  8. 8. Dependency managementStrategy 1: Do it yourself8/44
  9. 9. Why not...Instantiate classes with the newoperator?classSomeClass{publicfunctiondoSomething(){$logger=newLogger();$logger->debug(Nothingtoseehere);}}PHP9/44
  10. 10. Why not...Instantiate variable classes?classSomeClass{publicfunction__construct($loggerClass){$this->loggerClass=$loggerClass;}publicfunctiondoSomething(){$logger=new{$this->loggerClass}();$logger->debug(Veryflexible,right?);}}PHP10/44
  11. 11. Why not...Instantiate in overridable methods?classSomeClass{publicfunctiondoSomething(){$logger=$this->createLogger();$logger->debug(Matthiaswashere);}protectedfunctioncreateLogger(){returnnewLogger();}}PHP11/44
  12. 12. Dependency managementStrategy 2: Fetch your dependencies12/44
  13. 13. Why not...Use a global/static variable?classSomeClass{publicfunctiondoSomething(){Logger::getInstance()->debug(Matthiaswashere);}}PHP13/44
  14. 14. Why not...Use a service container/manager/registry?classSomeClass{publicfunction__construct(ContainerInterface$container){$this->container=$container;}publicfunctiondoSomething(){$this->container->get(logger)->log(Wow,thatwasquiteadistance!)}}PHP14/44
  15. 15. Dependency managementStrategy 3: Be passive about your dependencies15/44
  16. 16. Injecting dependencies by their interfaceclassSomeClass{publicfunction__construct(LoggerInterface$logger){$this->logger=$logger;}publicfunctiondoSomething(){$this->logger->info(Ah,muchbetter);}}PHP16/44
  17. 17. Strategy review1. Create your own dependencies2. Fetch your dependencies17/44
  18. 18. Strategy reviewYou are in control of your dependencies18/44
  19. 19. Strategy review3. Have your dependencies injected19/44
  20. 20. Strategy reviewYou are not in control of your dependenciesSomeone elseThis is called "inversion of control" (IoC)···20/44
  21. 21. Dependency injectionDependency injection is a ,used to21/44
  22. 22. Code reviewZendCrypt
  23. 23. Code reviewZendCryptclassBlockCipher{publicfunction__construct(SymmetricInterface$cipher){$this->cipher=$cipher;}publicfunctionencrypt($data){...}publicfunctiondecrypt($data){...}}PHP23/44
  24. 24. ZendCryptDependency Injection Smell: Static dependencyStatic dependencies are hard-to-control dependenciesclassBlockCipher{publicstaticfunctionfactory($adapter,$options=array()){$plugins=static::getSymmetricPluginManager();$adapter=$plugins->get($adapter,(array)$options);returnnewstatic($adapter);}}PHP24/44
  25. 25. ZendCryptDependency Injection Smell: Missing dependency auto-recoveryIf you have a dependency, act like you are .publicstaticfunctiongetSymmetricPluginManager(){if(static::$symmetricPlugins===null){static::setSymmetricPluginManager(newSymmetricPluginManager());}returnstatic::$symmetricPlugins;}PHP25/44
  26. 26. ZendCryptDependency Injection Smell: Hidden dependenciesIf you have a dependency, make it explicit.classSymmetricPluginManagerextendsAbstractPluginManager{protected$invokableClasses=array(mcrypt=>ZendCryptSymmetricMcrypt,);}PHP26/44
  27. 27. ZendCryptSuggested refactoringuseZendCryptSymmetricMcrypt;useZendCryptSymmetricBlockCipher;$cipher=newMcrypt(array(algo=>aes));$blockCipher=newBlockCipher($cipher);PHPNo SymmetricPluginManagerNo factory()method··27/44
  28. 28. ZendCryptDependency Injection Smell: Creation logic reductionMake way for complex creation logic (and dont think the newoperator willsuffice).publicstaticfunctionsetSymmetricPluginManager($plugins){if(is_string($plugins)){$plugins=new$plugins();}if(!$pluginsinstanceofSymmetricPluginManager){thrownewExceptionInvalidArgumentException();}static::$symmetricPlugins=$plugins;}PHP28/44
  29. 29. Code reviewSymfonyBundleFrameworkBundle
  30. 30. SymfonyBundleFrameworkBundleHttpCacheabstractclassHttpCacheextendsBaseHttpCache{publicfunction__construct(HttpKernelInterface$kernel,$cacheDir=null){$this->kernel=$kernel;$this->cacheDir=$cacheDir;parent::__construct($kernel,$this->createStore(),$this->createEsi(),array_merge(array(debug=>$kernel->isDebug()),$this->getOptions()));}}PHP30/44
  31. 31. SymfonyBundleFrameworkBundleDependency Injection Smell: Factory methodsDont require developers to use inheritance for replacing dependencies.abstractclassHttpCacheextendsBaseHttpCache{protectedfunctioncreateEsi(){returnnewEsi();}protectedfunctioncreateStore(){returnnewStore($this->cacheDir?:$this->kernel->getCacheDir()./http_cache);}}PHP31/44
  32. 32. SymfonyBundleFrameworkBundleDependency Injection Smell: Programming against an implementationHttpCachehas a dependency on HttpKernelInterface...abstractclassHttpCacheextendsBaseHttpCache{publicfunction__construct(HttpKernelInterface$kernel,$cacheDir=null){...}}PHPinterfaceHttpKernelInterface{publicfunctionhandle(Request$request,$type=self::MASTER_REQUEST,$catch=true);}PHP32/44
  33. 33. SymfonyBundleFrameworkBundleDependency Injection Smell: Programming against an implementation... but it uses of the interface methodsarray(debug=>$kernel->isDebug()), PHPreturnnewStore($this->cacheDir?:$this->kernel->getCacheDir()./http_cache); PHP$this->getKernel()->boot(); PHP33/44
  34. 34. SymfonyBundleFrameworkBundleSuggested refactoringProgram against an interface (strictly)Or: program against an implementation (and type-hint accordingly)Or: expand the interface to contain the methods you need···34/44
  35. 35. Code reviewDoctrineDBAL
  36. 36. DoctrineDBALDependency Injection Smell: Dependencies prohibitedabstractclassType{publicstaticfunctionaddType($name,$className){self::$typesMap[$name]=$className;}publicstaticfunctiongetType($name){if(!isset(self::$typeObjects[$name])){self::$typeObjects[$name]=newself::$typesMap[$name]();}returnself::$typeObjects[$name];}}Type::getType(datetime)->convertToPHPValue(2013-06-08);PHP36/44
  37. 37. DoctrineDBALDependency Injection Smell: Dependencies prohibitedclassEncryptedStringTypeextendsType{publicfunction__construct(BlockCipher$blockCipher){$this->blockCipher=$blockCipher;}publicfunctionconvertToDatabaseValue($value){return$this->blockCipher->encrypt($value);}publicfunctionconvertToPHPValue($value){return$this->blockCipher->decrypt($value);}}PHP37/44
  38. 38. DoctrineDBALDependency Injection Smell: Dependencies prohibitedWe can not override Type::__construct()since it is final.Dont neglect other classes needs (even if your class has everything it needs)Type::addType(encrypted_string,EncryptedStringType); PHPabstractclassType{finalprivatefunction__construct(){}}PHP38/44
  39. 39. DoctrineDBALDependency Injection Smell: Dependencies prohibitedWork-around: static dependencyclassEncryptedStringType{publicstatic$blockCiper;}EncryptedStringType::$blockCipher=...;PHP39/44
  40. 40. DoctrineDBALRecommended refactoring1. Split the Typeclass into an AbstractTypeand a TypeRegistry.2. Set a type instance, instead of just a class.$blockCipher=...;//ha,weknowhowtomakeone!$typeRegistry=newTypeRegistry();$encryptedStringType=newEncryptedStringType($blockCipher);$typeRegistry->setType(encrypted_string,$encryptedStringType);PHP40/44
  41. 41. In conclusionGood API design
  42. 42. Dependency injection smellsStatic dependencyMissing dependency auto-recoveryHidden dependenciesCreation logic reductionFactory methodsProgramming against an implementationDependencies prohibited·······42/44
  43. 43. Keep in mind...Be clear and open about what your dependencies areRequire only a minimum amount of dependenciesDevelop with your users (other developers) in mind···43/44
  44. 44. Thank youPlease leave some feedback at joind.in/8447twitter @matthiasnobackwww php-and-symfony.matthiasnoback.nlgithub github.com/matthiasnobackleanpub leanpub.com/a-year-with-symfony

×