Wiem, że jest to gorąca debata, a opinie zmieniają się z czasem co do najlepszej praktyki podejścia.
Kiedyś korzystałem wyłącznie z iniekcji terenowych do moich zajęć, dopóki nie zacząłem czytać na różnych blogach (np. Petrikainulainen i schauderhaft i fowler ) o korzyściach z iniekcji konstruktora. Od tego czasu zmieniłem swoje metodologie, aby używać wstrzykiwania konstruktora dla wymaganych zależności i wstrzykiwania setera dla zależności opcjonalnych.
Jednak ja niedawno zdobyty w dyskusji z autorem JMockit - ramy kpiący - w którym widzi konstruktora & zastrzyk seter jako złych praktyk i wskazuje, że społeczność JEE zgadza się z nim.
Czy w dzisiejszym świecie istnieje preferowany sposób wykonywania zastrzyków? Czy preferowany jest zastrzyk polowy?
Po przejściu na iniekcję konstruktora z iniekcji polowej w ciągu ostatnich kilku lat uważam, że korzystanie z niej jest o wiele łatwiejsze, ale zastanawiam się, czy powinienem ponownie przeanalizować mój punkt widzenia. Autor JMockit (Rogério Liesenfeld) jest wyraźnie dobrze zorientowany w DI, więc czuję się zobowiązany do przeglądu mojego podejścia, biorąc pod uwagę, że tak mocno odczuwa sprzeciw wobec iniekcji konstruktora / setera.
źródło
Odpowiedzi:
Jak na mój gust, iniekcja w polu to trochę zbyt „upiorna akcja na odległość” .
Rozważ przykład podany w poście w Grupach dyskusyjnych Google:
Więc w zasadzie mówisz, że „Mam tę klasę z państwem prywatnym, do której załączyłem
@injectable
adnotacje, co oznacza, że państwo może zostać automatycznie zaludnione przez jakiegoś agenta z zewnątrz, mimo że mój stan został uznany za prywatny. „Rozumiem motywacje do tego. Jest to próba uniknięcia dużej części ceremonii, która jest nieodłącznie związana z prawidłowym zorganizowaniem klasy. Zasadniczo chodzi o to, że „jestem zmęczony pisaniem całego tego bojlera, więc po prostu opiszę cały mój stan i pozwól pojemnikowi DI zająć się ustawieniem go dla mnie”.
To całkowicie poprawny punkt widzenia. Ale jest to również obejście funkcji językowych, które prawdopodobnie nie powinny być omijane. A po co się tu zatrzymywać? Tradycyjnie DI opierał się na każdej klasie posiadającej interfejs towarzyszący. Dlaczego nie wyeliminować również wszystkich tych interfejsów z adnotacjami?
Rozważ alternatywną opcję (będzie to C #, ponieważ znam ją lepiej, ale prawdopodobnie jest to dokładny odpowiednik w Javie):
Już wiem kilka rzeczy na temat tej klasy. To niezmienna klasa; stan można ustawić tylko w konstruktorze (w tym konkretnym przypadku odwołania). A ponieważ wszystko wywodzi się z interfejsu, mogę wymieniać implementacje w konstruktorze, czyli tam, gdzie przychodzą twoje symulacje.
Teraz wszystko, co musi zrobić mój kontener DI, to zastanowienie się nad konstruktorem, aby określić, jakie obiekty będą potrzebne do nowego. Ale ta refleksja odbywa się na członku publicznym, w pierwszej kolejności; tzn. metadane są już częścią klasy, ponieważ zostały zadeklarowane w konstruktorze, metodzie, której wyraźnym celem jest zapewnienie klasie potrzebnych zależności.
Przyznaję, że jest to duży bojler, ale tak właśnie zaprojektowano język. Adnotacje wyglądają jak brudny hack do czegoś, co powinno być wbudowane w sam język.
źródło
VeracodeService
jest prawie identyczna z tą, którą napisałeś (choć w Javie vs C #). WVeracodeServiceImplTest
rzeczywistości jest to klasa testu jednostkowego. Te@Injectable
pola są zasadniczo wyśmiewali obiekty są umieszczone w kontekście.@Tested
Pole jest przedmiotem / klasa z DI konstruktor zdefiniowane. Zgadzam się z twoim punktem widzenia, który woli wstrzykiwanie konstruktora niż wstrzykiwanie w terenie. Jednak, jak wspomniałem, autor JMockit uważa, że jest odwrotnie i staram się zrozumieć, dlaczegoArgument mniejszej próby inicjalizacji testu jest poprawny, ale istnieją inne obawy, które należy wziąć pod uwagę. Przede wszystkim musisz odpowiedzieć na pytanie:
Czy chcę, aby moja klasa była natychmiastowa tylko dzięki refleksji?
Stosowanie iniekcji w polu oznacza zawężenie kompatybilności klasy do środowisk iniekcji zależnych, które tworzą obiekty za pomocą odbicia i wspierają te adnotacje dotyczące iniekcji. Niektóre platformy oparte na języku Java nie obsługują nawet odbicia ( GWT ), więc klasa wstrzykiwana w pole nie będzie z nimi kompatybilna.
Drugi problem to wydajność . Wywołanie konstruktora (bezpośrednie lub przez odbicie) jest zawsze szybsze niż kilka przypisań pola odbicia. Struktury wstrzykiwania zależności muszą używać analizy odbić, aby budować drzewo zależności i tworzyć konstruktory odbić. Ten proces powoduje dodatkowe pogorszenie wydajności.
Wydajność wpływa na łatwość konserwacji . Jeśli każdy zestaw testowy musi zostać uruchomiony w jakimś pojemniku do wstrzykiwania zależności, uruchomienie testowe składające się z kilku tysięcy testów jednostkowych może potrwać kilkadziesiąt minut. W zależności od rozmiaru podstawy kodu może to stanowić problem.
To wszystko rodzi wiele nowych pytań:
Ogólnie rzecz biorąc, im większy i ważniejszy jest projekt, tym bardziej znaczące są te czynniki. Ponadto, jeśli chodzi o jakość, ogólnie chcielibyśmy utrzymać kod na wysokim poziomie pod względem kompatybilności, testowalności i łatwości konserwacji. Z filozoficznego punktu widzenia wstrzykiwanie w pole przerywa enkapsulację , która jest jedną z czterech podstaw programowania obiektowego , która jest głównym paradygmatem Javy.
Wiele, wiele argumentów przeciwko iniekcji w terenie.
źródło
Iniekcja otrzymuje ode mnie zdecydowane „nie” głosowanie.
Podobnie jak Robert Harvey, jest to zbyt automagiczne jak na mój gust. Wolę kod jawny niż niejawny i toleruję pośredniość tylko wtedy, gdy zapewnia on wyraźne korzyści, ponieważ utrudnia zrozumienie i uzasadnienie kodu.
Podobnie jak Maciej Chałapuk, nie lubię potrzeby refleksji ani struktury DI / IoC, aby utworzyć instancję klasy. To jak jazda do Rzymu, aby dostać się do Paryża z Amsterdamu.
Pamiętaj, że kocham DI i wszystkie zalety, które to przynosi. Po prostu nie jestem pewien, czy potrzebne są ramy do tworzenia instancji klasy. Szczególnie nie wpływ, jaki mogą mieć pojemniki IoC lub inne frameworki DI na kod testowy.
Lubię, żeby moje testy były bardzo proste. Nie lubię przechodzić przez pośrednie konfigurowanie kontenerów IoC lub innych struktur DI, aby następnie przetestować moją klasę. To po prostu dziwne.
I to uzależnia moje testy od globalnego stanu frameworka. To znaczy, że nie mogę już uruchamiać równolegle dwóch testów, które wymagają skonfigurowania instancji klasy X z różnymi zależnościami.
Przynajmniej w przypadku wstrzykiwania konstruktora i setera masz możliwość nie używania ram i / lub odbicia w swoich testach.
źródło
Cóż, to wydaje się polemiczna dyskusja. Pierwszą rzeczą, którą należy rozwiązać, jest to, że iniekcja polowa różni się od iniekcji Settera . Pracuję już z niektórymi ludźmi, którzy myślą, że zastrzyk z pola i setera to to samo.
Tak więc, dla jasności:
Iniekcja w terenie:
Zastrzyk setera:
Ale dla mnie mają te same obawy. I, mój ulubiony, zastrzyk konstruktora:
Mam następujące powody, by sądzić, że wstrzyknięcie konstruktora jest lepsze niż wstrzyknięcie setera / pola (podam kilka linków z wiosny, aby zademonstrować swój punkt):
@AutoWired
rozproszeniu kodu, twój kod jest mniej zależny od frameworka. Wiem, że możliwość prostej zmiany frameworka DI w projekcie nie uzasadnia tego (kto to robi, prawda?). Ale to pokazuje, dla mnie, lepszy kod (nauczyłem się tego na poważnie za pomocą EJB i wyszukiwań). Za pomocą Springa możesz nawet usunąć@AutoWired
adnotację za pomocą iniekcji konstruktora.Jeśli szukasz więcej informacji, polecam ten stary (ale nadal aktualny) artykuł z Spring Blog, który mówi nam, dlaczego używają tak dużo zastrzyku setera i zalecenie użycia zastrzyku konstruktora:
I ta uwaga o tym, dlaczego uważają, że ten konstruktor jest bardziej odpowiedni dla kodu aplikacji:
Końcowe przemyślenia
Jeśli nie masz pewności co do zalet konstruktora, być może pomysł mieszania setera (nie pola) z wstrzykiwaniem konstruktora jest wyjaśniony przez zespół Spring :
Pamiętaj jednak, że wstrzyknięcie pola jest prawdopodobnie tym, którego należy unikać wśród tych trzech.
źródło
Czy Twoja zależność prawdopodobnie zmieni się w trakcie życia klasy? Jeśli tak, użyj zastrzyku pola / nieruchomości. W przeciwnym razie użyj iniekcji konstruktora.
źródło