fbpx
devstyle.pl - Blog dla każdego programisty
devstyle.pl - Blog dla każdego programisty
4 minut

Dependency Injection: z kontenerem czy bez?


22.11.2016

Dependency Injection to bardzo potężny i przydatny wzorzec projektowy. Pozwala zaprowadzić w kodzie porządek, jawnie zadeklarować powiązania między klasami i uprościć proces utrzymania kodu. Powstała cała masa narzędzi wspomagających nas w tym światłym dziele. I po co?

Akcja: BLOGvember! Post nr 17.
W listopadzie w każdy roboczy poranek na devstyle.pl znajdziesz nowy, świeżutki tekst. W sam raz do porannej kawy na dobry początek dnia.
Miłej lektury i do przeczytania jutro! :)

Kontenery

Kontenery Dependency Injection powstały, aby uprościć proces tworzenia obiektów w systemie. Mówimy im, jakie klasy wchodzą w skład systemu, a one sobie wszystkie klocki układają. Zależności między strukturami wykrywają. Drzewa zasadzają, grafy modelują. By na koniec: tworzyć instancje.

Co bardziej zaawansowane kontenery potrafią o wiele więcej. A to Aspect-Oriented Programming nam udostępnią, a to dekoratory w prosty sposób pozwolą dorzucić, a to Dispose() wywołają kiedy trzeba, a to to, a to tamto. Więcej można poczytać w poście Profesjonalne kontenery i DI: kontener.

Lata temu ludzie rzucili się na tę rodzinę narzędzi. Powstawały coraz to nowe, bardziej skomplikowane. Później, jak to często bywa, nastąpił odwrót i tendencja upraszczania całego procesu zarządzania zależnościami. Aż dotarliśmy do stanu równowagi: equilibrium. Wszyscy byli szczęśliwi.

A potem: z dev-Olimpu poleciały gromy.

Alternatywa

Po co nam dodatkowe narzędzia? Możemy to naklepać ręcznie! W każdym projekcie z osobna! Bo kontenery są trudne! I powinniśmy minimalizować liczbę zależności w projekcie!

Takie oto głosy zaczęły dobiegać z programistycznych niebiesiech. Okazało się, że na przykład Mark Seemann (autor książki “Dependency Injection in .NET”) i Greg Young (kto nie zna, ten niech pozna we własnym zakresie) wiele mają kontenerom do zarzucenia. Że to UNIKALNE (IUnikable) zło. Że niepotrzebne. Że skomplikowane.

Polecam chociażby te linki:

Argumenty przedstawiane w tych treściach na pewno nie są bezpodstawne. Ci kolesie, jak już otwierają paszczę lub siadają do klawiatury, to wiedzą co czynią. Mam tylko jeden problem: zaprezentowane remedium… zupełnie do mnie nie przemawia.

Eksperymentalnie zrealizowałem kiedyś projekt bez wykorzystania kontenera. Wszystkie zależności rzeźbiłem ręcznie. Projekt ten miał raptem kilkadziesiąt klas na krzyż (“las krzyży” vs “las klas”?), a i tak kod budujący obiekty wyglądał bardzo… kupiaszczo. Nie chciałbym teraz do niego wrócić.

Werdykt

Kontenery nie są narzędziami banalnymi w “obsłudze”. Ich niepoprawne skonfigurowanie może nieść za sobą katastrofalne konsekwencje. Co powiedz na aplikację, która wysypuje się co 2 tygodnie, bo z powodu nieprzemyślanych rejestracji obiekty mnożą się jak wirusy i zżerają całą pamięć? Wiesz, ile czasu może zająć diagnostyka i profilowanie? Ja wiem :), więc podpowiem: DNI całe!

Ale z drugiej strony: popatrz na kod w podlinkowanych postach i prezentacjach.

Lambda na lambdzie i lambdą pogania. Poukrywane, pozagnieżdżane. Ja osobiście wolę mieć 50 linii czytelnego kodu niż 8 linii kodu, który bez problemu “załapie” maksymalnie 10% populacji.

Albo IFy, SWITCHe. Kurde… ControllerFactory o nazwie PoorMansCompositionRoot? Z mega-switchem? Ciut nie pattern-matching, tylko dla BARDZO ubogich.

Bardzo szanuję obu Panów, są półbogami programowania. Gdyby ich scalić, to powstałby dev-bóg. Ale tego… po prostu nie kupuję.

Oczywiście, każda zależność, referencja, to dodatkowy koszt. Każde narzędzie to proces nauki, obowiązki związane z utrzymaniem i aktualizacjami. Każda biblioteka to ryzyko “cudzych” błędów. Ale czy to powód, żeby wszystko pisać samemu?

NIE! A nawet: NIH! NIH Syndrome się kłania. Tak, jak bez sensu jest pisać własny kontener (co na jutubie czynię :) ), tak bez sensu jest ręcznie, samodzielnie, dbać o wszystkie rejestracje i tworzenie obiektów.

Z głową i umiarem. Świadomie, panie i panowie. Ze zrozumieniem, bez tępego copy/paste ze StackOverflow. Oto uniwersalna porada i maść na ból wszelaki.

0 0 votes
Article Rating
16 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
pawelek
7 years ago

Albo wziąć język funkcyjny, gdzie nie ma obiektów i już :) Ale fakt tu już pewnie tylko 3% populacji zrozumie “o co kaman” :)

Bartek
Bartek
7 years ago

Zajmowałem się problemem wycieków pamięci w aplikacji webowej – spring+scala+akka. Scenariusz podobny do opisanego przez Ciebie – apka się wykładała średnio co 3-4 tygodnie. Diagnoza? – OutOfMemoryError. Przyczyna? heh… Debugowanie języka funkcyjnego tudzież scali z akką to była istna mordęga… Skoki między tonami klas, masa apply’jów i problemy z zatrzymywaniem się w breakpointach… Nie wspominając już o tych wszystkich skrócikach i “uproszczeniach” konstrukcji, które do mnie nie trafiają. Jak słyszę teraz o tym, że ktoś się zachwyca scalą, jej skalowalnością, lambdami i że jest taka wow – mam ochotę strzelać. Scala może sama w sobie nie jest aż taka zła, niechęć wynika z problemów z debuggowaniem. Może dlatego, że miałem starszą wersję IDE(późny 2014), chociaż z wersją scali wydaną w końcu 2013 roku powinien sobie radzić. Niemniej jednak, niesmak pozostał ;) A wracając do przyczyny – przyczyną był mechanizm dead-letters w akka, który nadawał wiadomości do endpointa, który nie istniał, bo został nieprawidłowo odrejestrowany przez framework przy utracie połączenia i nie dawało się go już ponownie zarejestrować, pomimo usilnych prób. A martwe wiadomości składowane były w pamięci przez akkę. Dzięki, ulżyło mi ;) Co do samego DI, bo małego offtopa trzasnąłem – jak dla mnie przydatny mechanizm/wzorzec i nie zgodzę się z Panami, którzy twierdzą, że jest to zło. “Narzędzie” jak każde inne, w niektórych przypadkach będzie miało lepsze zastosowanie w innych gorsze. Ważne jest, by szyć rozwiązania na miarę, a nie według jednego utartego schematu – “bo w projekcie X taka architektura była i było git”. Tak jak wspomniałeś – z głową i z umiarem :)

Darek
Darek
7 years ago

Również tego nie kupuję, zarówno bez sensu jest bezmyślne ładowanie do projektu miliona frameworków i bibliotek jak i pisane siłę swoich rozwiązań, które ktoś już zrobił dobrze i dopracował, bo siedzi w temacie od lat. Wyobraźmy sobie stolarza który zamiast robić zlecenie, zajmuje się produkcją narzędzi bo “zrobi to lepiej i uniezależni się od producentów”.

Bartosz Sypytkowski
7 years ago

– Sądzę jednak, że łatwiej jest ludziom wyjaśnić jak działają funkcje anonimowe / lambdy, niż tłumaczyć API do biblioteki mającej prawie tyle kodu, co projekt do którego jej używam.
– Używanie kontenera zasadniczo przenosi cię w strefę programowania dynamicznego: skąd wiesz, że poprawnie podpiąłeś wszystkie zależności i twój program działa? Cóż, nie dowiesz się tego po prostu kompilując swój projekt. Musisz go odpalić i wywołać kod w każdym miejscu gdzie używasz DI. Oczywiście możesz napisać do tego testy, tylko że to mija się z początkowym założeniem.
– Zauważyłem, że nagminnym problemem w projektach korzystających z DI jest tendencja ludzi do korzystania z wzorca God Object: “Mamy mało czasu, nie będę wydzielał tej funkcjonalności do nowego kontrolera, wystarczy że dorzucę 30ty parametr do konstruktora, który już istnieje i reszta sama się zrobi”. Spotkałem się z takim podejściem w co najmniej kilku firmach.

Szczerze, z moich obserwacji wynika, że to właśnie stosowanie kontenerów DI wymaga właśnie zdyscyplinowanego i “kumatego” zespołu. W przeciwnym razie projekt zaczyna gnić w bardzo szybkim tempie. Sam nie korzystam z DI od roku i nie widzę żadnego spadku wydajności w swojej pracy.

Widzę za to, że nie mam problemów ze ściganiem źle rozwiązanych zależności, które czasami ciężko jest wychwycić przed wrzuceniem aplikacji na serwer testowy, czasem nawet na produkcję (Przykład: źle rozwiązane connection stringi, które “u mnie działały” ponieważ wszystkie prowadziły do tej samego testowego serwera).

Bartosz Sypytkowski
7 years ago

Mój problem z connection strings wynikał ze specyfiki i faktu, że jeden interfejs może mieć wiele implementacji (kto w czasach kontenerów IoC pomyślałby o podobnej herezji :) ), które muszą być podpinane zależnie od przypadku.

Zabawne jest to, że uważamy, że ręczna konstrukcja obiektów jest uciążliwa, ale nie mamy nic przeciwko ręcznej rejestracji komponentów, ręcznemu pisaniu, utrzymywaniu i uruchamianiu testów, a potem ręcznemu grzebaniu się przez 40 linijek stack trace żeby zobaczyć co w tym wszystkim było nie tak.

Krzysztof Skrzynecki
7 years ago

Istnieją również kontenery, które sprawdzają zależności podczas kompilacji – Dagger (https://google.github.io/dagger/) jest dobrym przykładem. Jestem ciekaw czy istnieją podobnę rozwiązania dla świata poza javowego

Wojciech
Wojciech
7 years ago

Zysk z weryfikacji w trakcie kompilacji bije wszystko pozostałe na głowe. Jeśli mamy framework, który umie zrobić DI w czasie kompilacji (np. macwire w scali) to spoko. W pozostałych wypadkach, sorry memory.

trackback

[…] Dependency Injection: z kontenerem czy bez? […]

Dawid
Dawid
7 years ago

IMHO, kontenery (nikt nie mówi o IoC jako takim) powinny być dodawane do projektów w kolejnej fazie. To zrobiło się tak bardzo popularne, że nawet nikt nie zauważa wrzucania kontenerów (z mojego podwórka nawet tak prostych jak Guice) do 3 klasowej applikacji jako zwykły błąd i overengineering. Czy nie przypomina to mikroserwisów? W przypadku kontenerów bardzo ciężko wyznaczyć granicę, kiedy “narzut” się opłaca, a kiedy nie – bo właściwie to kwestia developera jaki styl pracy mu się bardziej podoba. Pomijam fakt, że po prostu można nie potrafić programować bez użycia kontenerów – co też się zdarza.

Kurs Gita

Zaawansowany frontend

Szkolenie z Testów

Szkolenie z baz danych

Książka

Zobacz również