Desmistificando Injeção de Dependências

Desmistificando Injeção de Dependências

A injeção de dependências é uma ferramenta muito importante no desenvolvimento de software orientado a objetos. Porém, ainda pairam muitas dúvidas sobre o seu funcionamento e conceitos relacionados. Nesta palestra serão abordados os conceitos de injeção de dependência, inversão de controle e contêiner de injeção de dependência. Será apresentado o princípio da inversão de dependências e conceitos relacionados como autowire, resolução automática e binding. Será apresentada a PSR-11 e bibliotecas e ferramentas que tratam de contêineres de injeção de dependências. E, por fim, será disponbilizado um lista com referências para estudos.

52711e2157a6fed933b0361cc06a6953?s=128

Marcel dos Santos

April 24, 2019
Tweet

Transcript

  1. Marcel Gonçalves dos Santos @marcelgsantos desmistificando injeção de dependências

  2. pensandonaweb.com.br desenvolvedor web full-stack Marcel Gonçalves dos Santos @marcelgsantos

  3. None
  4. @femugsp sp.femug.com

  5. @phpsp phpsp.org.br

  6. Interaja nas mídias sociais!
 
 - fale sobre o evento,

    palestrantes e conteúdo - tire fotos do evento e publique
 - interaja com outros participantes do evento - tire dúvidas ou dê feedbacks para os palestrantes
  7. O que é uma dependência?

  8. uma dependência é um outro objeto que a sua classe

    necessita para ela funcionar
  9. !// class with a dependency
 class Car { private $engine;

    public function !__construct() { $this!->engine = new Engine(); } public function start() { $this!->engine!->start(); !// engine is a dependency } }
  10. um módulo A depende do módulo B quando qualquer código

    do módulo A referencia qualquer código do módulo B
  11. a referência é feita ao chamar uma função, usar algum

    dado ou usar algum tipo definido no módulo A
  12. O que é um objeto?

  13. um objeto é uma representação concreta de uma abstração…

  14. …que possui características, comportamentos e estado atual

  15. !// class to create a User object
 class User {

    private $name; private $age; public function !__construct(string $name, int $age) { $this!->name = $name; $this!->age = $age; } public function sayName() : void { echo "Hello, my name is {$this!->name}!"; } }
  16. !// two objects representing user abstractions
 $alice = new User('Alice',

    22); $bob = new User('Bob', 27); $alice!->sayName(); !// Hello, my name is Alice! $bob!->sayName(); !// Hello, my name is Bob!
  17. !// current state of $alice object
 var_dump($alice); !/* class User#3

    (2) { private $name !=> string(5) "Alice" private $age !=> int(22) } !*/
  18. Coesão e acoplamento

  19. indica o grau de relação entre os membros de um

    módulo coesão
  20. !// not cohesive class
 class Cart { private $items; public

    function !__construct() { $this!->items = []; } public function numberOfItems() : int { return count($this!->items); } public function calculateDeliveryPrice() : float { !// calculates the delivery price } }
  21. indica o grau de dependência entre classes acoplamento

  22. o acoplamento ocorre quando o código de um módulo utiliza

    código de outro módulo, seja ao chamar uma função ou acessar algum dado
  23. !// class Car is coupled to Engine class
 class Car

    { private $engine; public function !__construct() { $this!->engine = new Engine(); } public function start() { $this!->engine!->start(); !// engine is a dependency } }
  24. o acoplamento é algo desejado, porém, que deve ser controlado

  25. ao controlar o acoplamento, o software torna-se mais flexível e

    fácil de manter
  26. Como reduzir o acoplamento?

  27. pode-se reduzir o acoplamento através da injeção de dependências

  28. O que é injeção de dependências?

  29. a injeção de dependências é fornecer uma dependência do mundo

    externo para uma classe
  30. a injeção de dependências pode ser feita através de um

    parâmetro do construtor ou utilizando um método setter
  31. !// dependency injection via constructor
 class Car { private $engine;

    public function !__construct(Engine $engine) { $this!->engine = $engine; } public function start() { $this!->engine!->start(); } }
  32. !// injecting the dependency
 $engine = new Engine(); $car =

    new Car($engine); $car!->start(); !// Starting the engine
  33. não é recomendável instanciar uma dependência no método construtor da

    sua classe utilizando new
  34. !// coupling your object construction with !// the construction of

    its dependencies
 class Car { private $engine; public function !__construct() { $this!->engine = new Engine; } public function start() { $this!->engine!->start(); } }
  35. isso torna a sua classe altamente acoplada a sua dependência

  36. lembre-se que o objetivo é diminuir o acoplamento entre a

    classe e suas dependências
  37. “prefira classes com alta coesão e baixo acoplamento”

  38. Princípio da inversão de dependência
 Dependency Inversion Principle

  39. o princípio de inversão de dependência é a letra “D”

    do SOLID
  40. 3. módulos de baixo nível também devem
 depender de abstrações

    2. módulos de alto nível devem depender 
 de abstrações e não de implementações 1. módulos de alto nível não devem 
 depender de módulos de baixo nível o princípio de inversão de dependência diz:
  41. Classe A Classe B referencia

  42. !// concrete implementation in order to get coordinates !// from

    address in Google Maps and OpenStreetMap class GoogleMaps { public function getCoordinatesFromAddress($address) { !// calls Google Maps webservice } } class OpenStreetMap { public function getCoordinatesFromAddress($address) { !// calls OpenStreetMap webservice } }
  43. !// class StoreService depends on a concrete implementation !// of

    GoogleMaps geolocation service class StoreService { public function getStoreCoordinates($store) { $geolocationService = new GoogleMaps(); return $geolocationService !->getCoordinatesFromAddress($store!->getAddress()); } }
  44. uma classe não deve depender de um módulo de baixo

    nível
  45. Classe B referencia Classe A Interface implementa

  46. !// geolocation services abstraction (contract) !// each concrete implementation must

    follow this contract interface GeolocationService { public function getCoordinatesFromAddress($address); }
  47. !// concrete implementation must follow the contract class GoogleMaps implements

    GeolocationService { public function getCoordinatesFromAddress($address) { !// calls Google Maps webservice } } class OpenStreetMap implements GeolocationService { public function getCoordinatesFromAddress($address) { !// calls OpenStreetMap webservice } }
  48. !// the class should depends on abstraction (GeolocationService) class StoreService

    { private $geolocationService; public function !__construct(GeolocationService $geolocationService) { $this!->geolocationService = $geolocationService; } public function getStoreCoordinates($store) { return $this!->geolocationService !->getCoordinatesFromAddress($store!->getAddress()); } }
  49. uma classe deve depender de um contrato, abstração ou interface

  50. ao depender de abstrações você desacopla o seu código da

    dependência
  51. Inversão de Controle
 Inversion of Control

  52. a inversão de controle é sobre deixar de ir atrás

    das dependências e deixar que elas sejam levadas até você
  53. Mais sobre injeção de dependências

  54. a inversão de dependência é diferente da injeção de dependência

  55. a injeção de dependência permite alcançar a inversão de dependência

  56. é possível utilizar a injeção de dependência sem a necessidade

    de bibliotecas
  57. !// dependency injection is possible without libraries
 $geolocationService = new

    GoogleMaps(); $storeService = new StoreService($geolocationService);
  58. ao utilizar injeção de dependência o código é desacoplado de

    implementações de baixo nível
  59. utilizar injeção de dependências auxilia nos testes unitários pois tornam

    as classes fracamente acopladas, altamente coesas e facilita o mocking de objetos
  60. ter muitas dependências pode ser considerado um mau cheiro ou

    bad smell
  61. a injeção de dependências é uma boa prática, mas torna

    o processo de instanciação mais complicado
  62. !// complex instantiation process
 $handler = new StreamHandler(!__DIR!__ . '/email.log');

    $logger = new Logger('main', [$handler]); $mailer = new Mailer($logger); $notifier = new UserNotifier($mailer);
  63. Como tornar mais fácil o processo de instanciação?

  64. o processo de instanciação de objetos se torna mais fácil

    ao utilizar um contêiner de injeção de dependência
  65. O que é um contêiner de injeção de dependências?

  66. um contêiner de injeção de dependências é um mapa das

    dependências que a sua classe necessita e com a lógica para criar essas dependências
  67. um contêiner de injeção de dependências descreve objetos e suas

    dependências e instancia e configura os objetos sob demanda
  68. !// get a StoreService object from container
 $storeService = $container!->get('StoreService');

  69. !// config to inject a GoogleMaps instance when using 


    !// GeolocationService type hint
 $container!->set('GeolocationService', \DI\create(‘GoogleMaps'));
  70. !// get a StoreService object from container (Laravel) $storeService =

    $this!->app!->make('StoreService');
  71. !// config to inject a GoogleMaps instance when using 


    !// GeolocationService type hint (Laravel)
 $this!->app!->bind( 'App\Services\GeolocationService', 'App\Services\GoogleMaps' );
  72. um contêiner de injeção de dependências é conhecido também como

    service container, IOC container ou dependency injection container (DIC)
  73. class Mailer { public function mail($recipient, $content) { !// send

    an email to the recipient } }
  74. class UserManager { private $mailer; public function !__construct(Mailer $mailer) {

    $this!->mailer = $mailer; } public function register($email, $password) { !// The user just registered, we create his account !// !!... $this!->mailer!->mail($email, 'Hello and welcome!'); } }
  75. !// injecting the dependency manually
 
 $mailer = new Mailer();

    $userManager = new UserManager($mailer);
  76. !// get a UserManager object from container, the !// dependencies

    are resolved automatically
 $userManager = $container!->get('UserManager');
  77. !// get a UserManager object from container, the !// dependencies

    are resolved automatically (Laravel) 
 $userManager = $this!->app!->make('UserManager');
  78. o contêiner utiliza uma técnica chamada autowiring para analisar as

    dependências e criá-las conforme necessário
  79. o autowiring permite a resolução automática das dependências

  80. quando a resolução automática não é possível, utiliza-se arquivos de

    configuração
  81. o autowiring é possível graças às anotações de tipo e

    o suporte à reflection
  82. reflection é a capacidade de um programa inspecionar ele mesmo

    e modificar a sua lógica em tempo de execução
  83. reflection é perguntar a um objeto sobre suas propriedades e

    métodos e alterar seus membros (mesmo privados)
  84. frameworks web como Laravel e Symfony possuem um contêiner de

    injeção de dependências como parte integrante de suas estruturas
  85. um contêiner deve ser capaz de gerenciar qualquer objeto e

    os objetos não devem saber que são gerenciados por contêineres
  86. um contêiner pode também conter parâmetros que são utilizados para

    a configuração de objetos no contêiner
  87. um contêiner não gerencia todos os seus objetos, ele gerencia

    objetos de serviço como loggers, mailers…
  88. a injeção de dependências torna mais fácil de personalizar, permite

    usar estratégias diferentes e facilita a utilização de mock sem a necessidade de alterar a classe
  89. PSR-11
 Container Interface

  90. a PSR-11 descreve uma interface comum para contêineres de injeção

    de dependência
  91. None
  92. o objetivo é padronizar como frameworks e bibliotecas fazem o

    uso de contêineres para obter objetos e parâmetros
  93. utiliza-se um identificador (string) para identificar unicamente um item contido

    no contêiner
  94. um contêiner que segue a PSR-11 deve implementar a interface

    Psr\Container\ContainerInterface
  95. None
  96. a interface ContainerInterface expõe os métodos get e has

  97. o método get é responsável por obter uma instância de

    objeto do contêiner
  98. o contêiner pode lançar a exceção NotFoundExceptionInterface caso o identificador

    informado não seja encontrado
  99. pacotes que fornecem uma implementação da PSR de contêineres devem

    declarar que fornecem a psr/container-implementation
  100. None
  101. existem inúmeras bibliotecas de injeção de dependências em PHP e

    cada uma possui suas características
  102. None
  103. não se deve utilizar o contêiner de injeção de dependências

    como um service locator
  104. recomenda-se injetar diretamente as dependências ao invés de obtê-las 


    * lembre-se do princípio da inversão de controle (IoC)
  105. a PSR-11 ou Container Interface foi precedida pelo projeto container-interop

  106. None
  107. o projeto container-interop serviu como base e preparou o caminho

    para a PSR-11
  108. Conclusão

  109. 1. prefira classes com alta coesão e baixo acoplamento 2.

    utilize injeção de dependências ao seu favor 3. classes devem depender de abstrações ou interfaces 4. utilize um contêiner para o gerenciamento de suas dependências 5. o seu framework favorito provavelmente disponibiliza um contêiner para uso
  110. Referências

  111. bit.ly/referencias-palestra-di

  112. Avalie!

  113. Anúncio!

  114. bit.ly/workshop-php74 Informações:
 
 • o PHP 7.4 é a maior

    release em relação ao número de funcionalidades • analisaremos a evolução da linguagem • abordaremos as principais funcionalidades do PHP 7.4 como typed properties, arrow functions, spread operator em arrays, preloading, FFI (foreign function interface), operador de atribuição null coalesce, numeric literal separator entre outras de forma prática • e o que há por vir?
  115. @marcelgsantos speakerdeck.com/marcelgsantos Obrigado. Perguntas?