Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Dependency Injection Smells

8,816 views

Published on

Presentation about dependency injection smells in PHP.

Published in: Technology, Business
  • Be the first to comment

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

×