Podczas wieczornej przebieżki myślałem o tym jak ugryźć temat opisania Dependency Injection (DI). Nagle usłyszałem to zdanie, które chyba każdy z nas krzyczał będąc dzieckiem. “Mamo, rzuć picie!” 😂 I wtedy wyobraziłem sobie sytuację, w której to młody adept sztuki programowania po 12 godzinnej sesji siedzenia przed komputerem idzie sobie zrobić coś do jedzenia w kuchni.

Podejście klasyczne

Wyciąga z lodówki niezbędne składniki i zaczyna robić kanapkę:

1
2
3
4
5
Sandwich sandwich = new Sandwich();
sandwich.addIngredient(bread);
sandwich.addIngredient(butter);
sandwich.addIngredient(cheese);
sandwich.addIngredient(ham);

Ma on kontrolę nad tym jakie smakołyki będą w jego daniu. Korzysta z biblioteki jaką jest lodówka.

Inversion of Control

Następnego dnia sytuacja się powtarza, ale tym razem staje w kuchni zrezygnowany, zawraca i zmierza do pokoju rodziców. “Mamo, zrób mi kanapkę” grzecznie prosi. Mama idzie do kuchni i zaczynają dziać się czary.

1
2
SandwichMaker mom = new SandwichMaker();
Sandwich sandwich = mom.doSandwich();

Cała implementacja kanapki jest słodką tajemnicą mamy. To ona decyduje, które składniki użyje. Nasz młody adept skorzystał z pomocy mamy tak jak korzysta się z framework’a.

Dependency Injection

Nasz adept nauczony doświadczeniem staje od razu w drzwiach pokoju i prosi o kanapkę. Jednak tym razem chciałby, aby składniki były wyłącznie mięsne.

1
2
3
SandwichTypeOrder sandwichTypeOrder = new MeatTypeOrder();
SandwichMaker mom = new SandwichMaker(sandwichTypeOrder);
Sandwich sandwich = mom.doSandwich();

W ten sposób została wstrzyknięta zależność, która mówi nam o sposobie w jaki chcemy coś wykonać. Dalej nie mamy władzy nad tym jak to zostanie wykonane. Możemy dać jedynie pewną wskazówkę co byśmy chcieli uzyskać.

Podsumowanie

Dependency Injection

Chciałem Ci przedstawić moje zrozumienie tej koncepcji. IoC zostało wyjaśnione przeze mnie w poprzednim wpisie. Natomiast Dependency Injection (Wstrzykiwanie Zależności) jest wzorcem projektowym, który rozszerza koncepcję IoC. Pozwala nam dodatkowo wstrzykiwać dodatkowe zależności, które potrafią zmienić wykonanie metody framework’a. Dalej nie wiemy co dokładnie się w niej dzieje, ale mamy chociaż niewielki wpływ na wynik.

Warning! Warning!

Ale uwaga! Łatwo przesadzić z tym wstrzykiwaniem zależności. Poniżej przedstawiono przykład znaleziony w kodzie produkcyjnym (zmieniono nazwy klas dzięki czemu widać ile dokładnie potrzeba klas do stworzenia Generatora 😱).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Generator implements Interface {
  private FirstConverter firstConverter;
  private FirstService firstService;
  private SecondService secondService;
  private ThirdService thirdService;
  private FourthService fourthService;
  private FifthService fifthService;
  private SixthService sixthService;
  private SeventhService seventhService;
  private EighthService eighthService;
  private NinthService ninthService;
  private TenthService tenthService;
  
  public Generator(FirstConverter firstConverter, FirstService firstService, SecondService secondService, thirdService, FourthService fourthService, FifthService fifthService, SixthService sixthService, SeventhService seventhService, EighthService eighthService, NinthService ninthService, TenthService tenthService) {
    this.firstConverter = firstConverter;
    this.firstService = firstService;
    this.secondService = secondService;
    this.thirdService = thirdService;
    this.fourthService = fourthService;
    this.fifthService = fifthService;
    this.sixthService = sixthService;
    this.seventhService = seventhService;
    this.eighthService = eighthService;
    this.ninthService = ninthService;
    this.tenthService = tenthService;
  }
  
  ...
}

Na podstawie konstruktora widać, że ta klasa ma za dużo odpowiedzialności. Jedna z zasad SOLID została złamana jak nic! 😉 Dodatkowo może dla niektórych trudniej byłoby to zauważyć jeśli dodano by jeszcze adnotację Lombok’a @AllArgsConstructor (o tym wkrótce). Dlatego zaznaczam, że wszystkich narzędzi trzeba używać z umiarem i rozsądkiem.

Mam nadzieję, że chociaż w małym stopniu wyjaśniłem różnice występujące pomiędzy tymi podejściami. A jakie jest Twoje rozumienie tego tematu? Może masz swoje przykłady, które lepiej wyjaśniłyby zagadnienie? Podziel się nimi w komentarzu lub napisz do mnie.