Rozumiem korzyści płynące z samego wstrzyknięcia zależności. Weźmy na przykład wiosnę. Rozumiem również zalety innych funkcji Springa, takich jak AOP, pomocników różnego rodzaju itp. Zastanawiam się tylko, jakie są zalety konfiguracji XML, takich jak:
<bean id="Mary" class="foo.bar.Female">
<property name="age" value="23"/>
</bean>
<bean id="John" class="foo.bar.Male">
<property name="girlfriend" ref="Mary"/>
</bean>
w porównaniu ze zwykłym starym kodem java, takim jak:
Female mary = new Female();
mary.setAge(23);
Male john = new Male();
john.setGirlfriend(mary);
który jest łatwiejszy do debugowania, sprawdzania czasu kompilacji i może być zrozumiany przez każdego, kto zna tylko java. Więc jaki jest główny cel struktury wstrzykiwania zależności? (lub fragment kodu, który pokazuje jego zalety).
UPDATE: w
przypadku
IService myService;// ...
public void doSomething() {
myService.fetchData();
}
W jaki sposób framework IoC może odgadnąć, którą implementację myService chcę wprowadzić, jeśli jest więcej niż jedna? Jeśli istnieje tylko jedna implementacja danego interfejsu, a ja pozwolę kontenerowi IoC automatycznie zdecydować się na jej użycie, to po pojawieniu się drugiej implementacji zostanie zerwany. A jeśli celowo istnieje tylko jedna możliwa implementacja interfejsu, nie musisz jej wstrzykiwać.
Byłoby naprawdę interesujące zobaczyć mały fragment konfiguracji IoC, który pokazuje jego zalety. Spring używam od jakiegoś czasu i nie mogę podać takiego przykładu. Mogę pokazać pojedyncze linie, które pokazują zalety hibernacji, dwr i innych frameworków, których używam.
UPDATE 2:
Zdaję sobie sprawę, że konfigurację IoC można zmienić bez ponownej kompilacji. Czy to naprawdę dobry pomysł? Rozumiem, kiedy ktoś chce zmienić poświadczenia bazy danych bez ponownej kompilacji - może nie być programistą. W twojej praktyce, jak często ktoś inny niż programista zmienia konfigurację IoC? Myślę, że dla programistów nie ma żadnego wysiłku, aby przekompilować tę konkretną klasę zamiast zmieniać konfigurację. A dla osób niebędących programistami prawdopodobnie chciałbyś ułatwić mu życie i zapewnić prostszy plik konfiguracyjny.
AKTUALIZACJA 3:
Zewnętrzna konfiguracja mapowania między interfejsami i ich konkretnymi implementacjami
Co jest takiego dobrego w uczynieniu go ekstensywnym? Nie możesz uczynić całego kodu zewnętrznymi, chociaż zdecydowanie możesz - po prostu umieść go w pliku ClassName.java.txt, czytaj i kompiluj ręcznie w locie - wow, uniknąłeś ponownej kompilacji. Dlaczego należy unikać kompilacji ?!
Oszczędzasz czas potrzebny na kodowanie, ponieważ odwzorowania podajesz deklaratywnie, a nie w kodzie proceduralnym
Rozumiem, że czasami deklaratywne podejście oszczędza czas. Na przykład deklaruję tylko raz mapowanie między właściwością bean a kolumną DB, a hibernacja używa tego mapowania podczas ładowania, zapisywania, budowania SQL w oparciu o HSQL itp. W tym miejscu działa podejście deklaratywne. W przypadku Springa (w moim przykładzie) deklaracja miała więcej linii i miała taką samą wyrazistość jak odpowiadający jej kod. Jeśli jest przykład, kiedy taka deklaracja jest krótsza niż kod - chciałbym to zobaczyć.
Zasada odwrócenia kontroli pozwala na łatwe testowanie jednostkowe, ponieważ można zastąpić rzeczywiste implementacje fałszywymi (np. Zastąpienie bazy danych SQL bazą danych w pamięci)
Rozumiem odwrócenie korzyści ze sterowania (wolę nazywać omawiany tu wzorzec projektowy jako Dependency Injection, ponieważ IoC jest bardziej ogólny - jest wiele rodzajów kontroli, a odwracamy tylko jeden z nich - sterowanie inicjalizacją). Pytałem, dlaczego ktoś potrzebuje do tego czegoś innego niż język programowania. Zdecydowanie mogę zamienić prawdziwe implementacje na fałszywe z użyciem kodu. Ten kod będzie wyrażał to samo co konfiguracja - po prostu zainicjuje pola z fałszywymi wartościami.
mary = new FakeFemale();
Rozumiem korzyści płynące z DI. Nie rozumiem, jakie korzyści daje zewnętrzna konfiguracja XML w porównaniu z konfiguracją kodu, który robi to samo. Nie uważam, że należy unikać kompilacji - kompiluję codziennie i wciąż żyję. Myślę, że konfiguracja DI jest złym przykładem podejścia deklaratywnego. Deklaracja może być przydatna, jeśli jest zadeklarowana raz ORAZ jest używana wiele razy na różne sposoby - jak hibernacja cfg, gdzie mapowanie między właściwością fasoli a kolumną DB jest używane do zapisywania, ładowania, budowania zapytań wyszukiwania itp. Konfigurację Spring DI można łatwo przetłumaczyć na konfigurowanie kodu, jak na początku tego pytania, czyż nie? I jest używany tylko do inicjalizacji fasoli, prawda? Co oznacza, że podejście deklaratywne niczego tutaj nie dodaje, prawda?
Kiedy deklaruję mapowanie hibernacji, po prostu podaję informacje o hibernacji i działa na tej podstawie - nie mówię mu, co ma robić. W przypadku wiosny moja deklaracja mówi wiosnie dokładnie, co ma robić - więc po co to deklarować, dlaczego po prostu tego nie robić?
OSTATNIA AKTUALIZACJA:
Chłopaki, wiele odpowiedzi mówi mi o zastrzyku zależności, o którym WIEM, że JEST DOBRY. Pytanie dotyczy celu konfiguracji DI zamiast inicjalizacji kodu - wydaje mi się, że kod inicjujący jest krótszy i bardziej przejrzysty. Jedyną odpowiedzią, jaką uzyskałem do tej pory na moje pytanie, jest to, że unika się ponownej kompilacji, gdy zmienia się konfiguracja. Chyba powinienem zadać kolejne pytanie, bo to dla mnie wielka tajemnica, dlaczego w tym przypadku należy unikać kompilacji.
źródło
Odpowiedzi:
Dla mnie jednym z głównych powodów używania IoC (i korzystania z konfiguracji zewnętrznej) są dwa obszary:
Testowanie
Jeśli podzielisz testy na 3 scenariusze (co jest dość normalne w przypadku programowania na dużą skalę):
To, co będziesz chciał zrobić, to dla dwóch ostatnich scenariuszy testowych (integracja i czarna skrzynka), nie rekompiluj żadnej części aplikacji.
Jeśli którykolwiek z twoich scenariuszy testowych wymaga zmiany konfiguracji (np .: użyj innego komponentu, aby naśladować integrację bankową lub wykonaj obciążenie wydajnościowe), można to łatwo obsłużyć (wiąże się to z zaletami konfiguracji strony DI Chociaż IoC.
Ponadto, jeśli Twoja aplikacja jest używana w wielu witrynach (z inną konfiguracją serwera i składników) lub ma zmienną konfigurację w środowisku na żywo, możesz skorzystać z późniejszych etapów testowania, aby sprawdzić, czy aplikacja obsłuży te zmiany.
Produkcja
Jako programista nie masz (i nie powinieneś) mieć kontroli nad środowiskiem produkcyjnym (w szczególności gdy Twoja aplikacja jest dystrybuowana do wielu klientów lub do oddzielnych witryn), jest to dla mnie prawdziwa korzyść z używania zarówno konfiguracji IoC, jak i konfiguracji zewnętrznej , ponieważ od wsparcia infrastruktury / produkcji zależy modyfikowanie i dostosowywanie środowiska na żywo bez konieczności wracania do programistów i testowania (wyższy koszt, gdy chcą tylko przenieść komponent).
Podsumowanie
Główne korzyści płynące z zewnętrznej konfiguracji IoC wynikają z przyznania innym (nie-programistom) możliwości konfigurowania aplikacji, z mojego doświadczenia wynika, że jest to przydatne tylko w ograniczonych okolicznościach:
W praktyce odkryłem, że nawet jeśli tworzysz coś, na czym masz kontrolę nad środowiskiem, na którym będzie działać, z czasem lepiej jest dać komuś innemu możliwość zmiany konfiguracji:
Uwaga: aplikacja oznacza kompletne rozwiązanie (nie tylko plik wykonywalny), a więc wszystkie pliki wymagane do działania aplikacji .
źródło
Wstrzykiwanie zależności to styl kodowania, który ma swoje korzenie w obserwacji, że delegowanie obiektu jest zwykle bardziej użytecznym wzorcem projektowym niż dziedziczenie obiektu (tj. Obiekt ma relację jest bardziej przydatny niż obiekt jest relacją). Jednak do działania DI niezbędny jest jeszcze jeden składnik, czyli tworzenie interfejsów obiektów. Łącząc te dwa potężne wzorce projektowe, inżynierowie oprogramowania szybko zdali sobie sprawę, że mogą stworzyć elastyczny, luźno powiązany kod, i tak narodziła się koncepcja Dependency Injection. Jednak dopiero gdy odbicie obiektów stało się dostępne w niektórych językach wysokiego poziomu, DI naprawdę nabrało tempa. Element odbicia jest podstawą większości dzisiejszych czasów ”
Język musi zapewniać dobre wsparcie zarówno dla zwykłych technik programowania zorientowanego obiektowo, jak i obsługi interfejsów obiektowych i odbicia obiektów (na przykład Java i C #). Chociaż można budować programy przy użyciu wzorców DI w systemach C ++, brak obsługi odbić we właściwym języku uniemożliwia mu obsługę serwerów aplikacji i innych platform DI, a tym samym ogranicza ekspresywność wzorców DI.
Mocne strony systemu zbudowanego w oparciu o wzorce DI:
Zdecydowanie kod DI wydaje się bardziej nieporęczny, a wady posiadania wszystkich tych plików XML, które konfigurują obiekty do wstrzyknięcia do innych obiektów, wydają się trudne. Jest to jednak celem systemów DI. Zdolność do mieszania i dopasowywania obiektów kodu jako serii ustawień konfiguracyjnych umożliwia tworzenie złożonych systemów przy użyciu kodu innych firm przy minimalnym kodowaniu z Twojej strony.
Przytoczony w pytaniu przykład dotyka jedynie powierzchni siły ekspresji, jaką może zapewnić odpowiednio rozłożona na czynniki biblioteka obiektów DI. Przy odrobinie praktyki i dużej samodyscyplinie większość praktyków DI przekonuje się, że mogą budować systemy, które mają 100% pokrycie testowe kodu aplikacji. Już ten jeden punkt jest niezwykły. Nie jest to stuprocentowe pokrycie testowe małej aplikacji składającej się z kilkuset linii kodu, ale stuprocentowe pokrycie testowe aplikacji składających się z setek tysięcy linii kodu. Nie jestem w stanie opisać żadnego innego wzorca projektowego, który zapewnia taki poziom testowalności.
Masz rację w tym, że zastosowanie zaledwie 10 linii kodu jest łatwiejsze do zrozumienia niż kilka obiektów plus seria plików konfiguracyjnych XML. Jednak, podobnie jak w przypadku najpotężniejszych wzorców projektowych, korzyści można zaobserwować w miarę dodawania nowych funkcji do systemu.
Krótko mówiąc, aplikacje oparte na DI na dużą skalę są zarówno łatwiejsze do debugowania, jak i łatwiejsze do zrozumienia. Podczas gdy konfiguracja XML nie jest „sprawdzana w czasie kompilacji”, wszystkie usługi aplikacji, o których wie ten autor, będą dostarczać programistom komunikaty o błędach, jeśli spróbują wstrzyknąć obiekt z niezgodnym interfejsem do innego obiektu. A większość zapewnia funkcję „sprawdzania”, która obejmuje wszystkie znane konfiguracje obiektów. Można to łatwo i szybko zrobić, sprawdzając, czy obiekt A, który ma zostać wstrzyknięty, implementuje interfejs wymagany przez obiekt B dla wszystkich skonfigurowanych wstrzyknięć obiektów.
źródło
To trochę załadowane pytanie, ale zgadzam się, że ogromne ilości konfiguracji XML nie przynoszą tak naprawdę dużych korzyści. Lubię, gdy moje aplikacje są jak najmniej zależne od zależności, w tym potężne frameworki.
Często upraszczają kod, ale mają też narzut związany ze złożonością, co sprawia, że śledzenie problemów jest dość trudne (widziałem takie problemy z pierwszej ręki, a sama Java, z którą byłbym o wiele wygodniejszy).
Myślę, że zależy to trochę od stylu i tego, z czym czujesz się komfortowo ... lubisz latać własnym rozwiązaniem i mieć tę przewagę, że znasz go od podszewki, czy też możesz liczyć na istniejące rozwiązania, które mogą okazać się trudne, gdy konfiguracja nie jest '' t po prostu dobrze? To wszystko jest kompromisem.
Jednak konfiguracja XML jest trochę moja irytująca ... Staram się tego unikać za wszelką cenę.
źródło
Za każdym razem, gdy możesz zmienić kod na dane, robisz krok we właściwym kierunku.
Kodowanie czegokolwiek jako danych oznacza, że sam kod jest bardziej ogólny i wielokrotnego użytku. Oznacza to również, że Twoje dane mogą być określone w języku, który dokładnie do nich pasuje.
Ponadto plik XML można wczytać do GUI lub innego narzędzia i łatwo manipulować nim pragmatycznie. Jak byś to zrobił na przykładzie kodu?
Ciągle uwzględniam rzeczy, które większość ludzi zaimplementowałaby jako kod do danych, dzięki czemu kod pozostaje ZNACZNIE czystszy. Wydaje mi się nie do pomyślenia, aby ludzie tworzyli menu w kodzie, a nie jako dane - powinno być oczywiste, że robienie tego w kodzie jest po prostu błędne z powodu schematu.
źródło
Powodem używania kontenera DI jest to, że nie musisz mieć miliarda wstępnie skonfigurowanych właściwości w kodzie, które są po prostu pobierającymi i ustawiającymi. Czy naprawdę chcesz zakodować wszystkie te z nowym X ()? Jasne, możesz mieć wartość domyślną, ale kontener DI umożliwia tworzenie singletonów, co jest niezwykle łatwe i pozwala skupić się na szczegółach kodu, a nie na dodatkowym zadaniu jego inicjalizacji.
Na przykład Spring pozwala na zaimplementowanie interfejsu InitializingBean i dodanie metody afterPropertiesSet (możesz również określić metodę init, aby uniknąć łączenia kodu ze Spring). Te metody pozwolą Ci upewnić się, że każdy interfejs określony jako pole w Twojej instancji klasy jest poprawnie skonfigurowany po uruchomieniu, a następnie nie musisz już sprawdzać wartości null swoich pobierających i ustawiających (zakładając, że pozwolisz swoim singletonom pozostać bezpiecznymi wątkowo ).
Ponadto znacznie łatwiej jest wykonywać złożone inicjalizacje za pomocą kontenera DI, zamiast wykonywać je samodzielnie. Na przykład pomagam w używaniu XFire (nie CeltiXFire, używamy tylko Javy 1.4). Aplikacja korzystała ze Springa, ale niestety korzystała z mechanizmu konfiguracji services.xml firmy XFire. Kiedy kolekcja elementów musiała zadeklarować, że ma ZERO lub więcej wystąpień zamiast JEDNEGO lub więcej wystąpień, musiałem zastąpić część dostarczonego kodu XFire dla tej konkretnej usługi.
W schemacie Spring Bean zdefiniowano pewne wartości domyślne XFire. Tak więc, gdybyśmy używali Springa do konfigurowania usług, można by użyć fasoli. Zamiast tego musiałem dostarczyć instancję określonej klasy w pliku services.xml zamiast używać komponentów bean. Aby to zrobić, musiałem dostarczyć konstruktora i ustawić odniesienia zadeklarowane w konfiguracji XFire. Prawdziwa zmiana, której musiałem dokonać, wymagała przeciążenia jednej klasy.
Ale dzięki plikowi services.xml musiałem stworzyć cztery nowe klasy, ustawiając ich wartości domyślne zgodnie z domyślnymi w plikach konfiguracyjnych Springa w ich konstruktorach. Gdybyśmy mogli skorzystać z konfiguracji Spring, mógłbym po prostu stwierdzić:
Zamiast tego wyglądało to bardziej tak:
Wynik netto jest taki, że cztery dodatkowe, w większości bezsensowne klasy Java musiały zostać dodane do bazy kodu, aby uzyskać efekt, jaki osiągnęła jedna dodatkowa klasa i kilka prostych informacji o kontenerze zależności. To nie jest „wyjątek potwierdzający regułę”, to JEST reguła ... obsługa dziwactw w kodzie jest znacznie czystsza, gdy właściwości są już podane w kontenerze DI i po prostu zmieniasz je, aby pasowały do specjalnej sytuacji, co zdarza się częściej niż nie.
źródło
Mam twoją odpowiedź
W każdym podejściu istnieją oczywiście pewne kompromisy, ale zewnętrzne pliki konfiguracyjne XML są przydatne w programowaniu przedsiębiorstw, w których systemy kompilacji są używane do kompilowania kodu, a nie środowiska IDE. Korzystając z systemu kompilacji, możesz chcieć wstrzyknąć określone wartości do swojego kodu - na przykład wersję kompilacji (co może być bolesne, gdy trzeba aktualizować ręcznie za każdym razem, gdy kompilujesz). Ból jest większy, gdy system kompilacji pobiera kod z jakiegoś systemu kontroli wersji. Modyfikowanie prostych wartości w czasie kompilacji wymagałoby zmiany pliku, zatwierdzenia go, kompilacji, a następnie przywracania za każdym razem dla każdej zmiany. To nie są zmiany, które chcesz wprowadzić do kontroli wersji.
Inne przydatne przypadki użycia dotyczące systemu kompilacji i zewnętrznych konfiguracji:
Aktualizacja: Wszystkie powyższe przykłady dotyczyły rzeczy, które niekoniecznie wymagały zależności od klas. Ale możesz łatwo budować przypadki, w których potrzebny jest zarówno złożony obiekt, jak i automatyzacja - na przykład:
źródło
Nie musisz ponownie kompilować kodu za każdym razem, gdy zmieniasz coś w konfiguracji. Uprości to wdrażanie i konserwację programu. Na przykład możesz zamienić jeden komponent na inny, dokonując tylko jednej zmiany w pliku konfiguracyjnym.
źródło
Możesz włożyć nową implementację dla dziewczyny. Tak więc nowa kobieta może zostać wstrzyknięta bez ponownej kompilacji kodu.
(Powyższe zakłada, że Kobieta i HotFemale mają ten sam interfejs GirlfFriend)
źródło
W świecie .NET większość platform IoC zapewnia konfigurację zarówno XML, jak i Code.
Na przykład StructureMap i Ninject używają płynnych interfejsów do konfigurowania kontenerów. Nie jesteś już ograniczony do używania plików konfiguracyjnych XML. Spring, który istnieje również w .NET, w dużym stopniu opiera się na plikach XML, ponieważ jest to jego historyczny główny interfejs konfiguracyjny, ale nadal możliwe jest programowe konfigurowanie kontenerów.
źródło
Łatwość łączenia częściowych konfiguracji w ostateczną kompletną konfigurację.
Na przykład w aplikacjach internetowych model, widok i kontrolery są zwykle określone w oddzielnych plikach konfiguracyjnych. Użyj podejścia deklaratywnego, możesz załadować na przykład:
Lub załaduj za pomocą innego interfejsu użytkownika i kilku dodatkowych kontrolerów:
Aby zrobić to samo w kodzie, wymagana jest infrastruktura do łączenia częściowych konfiguracji. Nie jest to niemożliwe w kodzie, ale z pewnością łatwiejsze do zrobienia przy użyciu frameworka IoC.
źródło
Często ważne jest, kto zmienia konfigurację po napisaniu programu. W przypadku konfiguracji w kodzie zakłada się niejawnie, że osoba zmieniająca ją ma takie same umiejętności i dostęp do kodu źródłowego itp., Jakie miał pierwotny autor.
W systemach produkcyjnych bardzo praktyczne jest wyodrębnienie pewnego podzbioru ustawień (np. Wieku w tobie) do pliku XML i umożliwienie np. Administratorowi systemu lub personelowi wsparcia zmiany wartości bez nadawania im pełnej władzy nad kodem źródłowym lub innymi ustawieniami - lub po prostu aby oddzielić je od zawiłości.
źródło
Z wiosennej perspektywy mogę dać dwie odpowiedzi.
Po pierwsze, konfiguracja XML nie jest jedynym sposobem zdefiniowania konfiguracji. Większość rzeczy można skonfigurować za pomocą adnotacji, a rzeczy, które należy zrobić z XML, to konfiguracja kodu, którego i tak nie piszesz, na przykład pula połączeń, której używasz z biblioteki. Spring 3 zawiera metodę definiowania konfiguracji DI przy użyciu języka Java, podobną do ręcznej konfiguracji DI w Twoim przykładzie. Tak więc używanie Springa nie oznacza, że musisz używać pliku konfiguracyjnego opartego na XML.
Po drugie, Spring to znacznie więcej niż tylko framework DI. Posiada wiele innych funkcji, w tym zarządzanie transakcjami i AOP. Konfiguracja Spring XML łączy w sobie wszystkie te koncepcje. Często w tym samym pliku konfiguracyjnym określam zależności bean, ustawienia transakcji i dodaję fasolki o zasięgu sesji, które faktycznie obsługiwały używanie AOP w tle. Uważam, że konfiguracja XML zapewnia lepsze miejsce do zarządzania wszystkimi tymi funkcjami. Uważam również, że konfiguracja oparta na adnotacjach i konfiguracja XML skalują się lepiej niż konfiguracja oparta na Javie.
Ale rozumiem twój punkt widzenia i nie ma nic złego w definiowaniu konfiguracji wstrzykiwania zależności w Javie. Zwykle robię to samodzielnie w testach jednostkowych i kiedy pracuję nad projektem na tyle małym, że nie dodałem frameworka DI. Zwykle nie określam konfiguracji w Javie, ponieważ wydaje mi się, że jest to rodzaj kodu hydraulicznego, od którego pisania próbuję uciec, gdy zdecydowałem się użyć Spring. To jednak preferencja, nie oznacza to, że konfiguracja XML jest lepsza od konfiguracji opartej na Javie.
źródło
Wiosna ma również właściwości ładujące. Używamy tej metody do ustawiania zmiennych zależnych od środowiska (np. Programowanie, testowanie, akceptacja, produkcja, ...). Może to być na przykład kolejka do słuchania.
Jeśli nie ma powodu, dla którego właściwość miałaby się zmieniać, nie ma również powodu, aby konfigurować ją w ten sposób.
źródło
Twoja sprawa jest bardzo prosta i dlatego nie potrzebuje kontenera IoC (Inversion of Control), takiego jak Spring. Z drugiej strony, kiedy "programujesz do interfejsów, a nie implementacji" (co jest dobrą praktyką w OOP), możesz mieć taki kod:
(zwróć uwagę, że typ myService to IService - interfejs, a nie konkretna implementacja). Teraz może być przydatne, aby pozwolić swojemu kontenerowi IoC automatycznie dostarczać poprawną konkretną instancję IService podczas inicjalizacji - jeśli masz wiele interfejsów i wiele implementacji, ręczne wykonanie tego może być uciążliwe. Główne zalety kontenera IoC (framework iniekcji zależności) to:
źródło
Inicjowanie w pliku konfiguracyjnym XML uprości debugowanie / dostosowywanie pracy z klientem, który ma wdrożoną aplikację na swoich komputerach. (Ponieważ nie wymaga ponownej kompilacji + wymiany plików binarnych)
źródło
Jednym z najbardziej interesujących powodów jest „ zasada Hollywood ”: nie dzwoń do nas, my zadzwonimy. Składnik nie musi sam wyszukiwać innych składników i usług; zamiast tego są do niego dostarczane automatycznie. W Javie oznacza to, że nie ma już potrzeby wyszukiwania JNDI wewnątrz komponentu.
Dużo łatwiej jest również przeprowadzić testy jednostkowe komponentu w izolacji: zamiast nadawać mu rzeczywistą implementację potrzebnych komponentów, po prostu używasz (prawdopodobnie automatycznie generowanych) prób.
źródło