Jakie są zalety kontenerów iniekcji zależności?


104

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.


21
Wreszcie ktoś miał odwagę zadać to pytanie. Dlaczego rzeczywiście miałbyś chcieć uniknąć ponownej kompilacji, gdy zmiana implementacji kosztem poświęcenia (lub przynajmniej degradacji) wsparcia narzędzi / IDE?
Christian Klauser

3
Wygląda na to, że tytuł nie jest całkiem poprawny. Autor powiedział, że kontenery IOC są w porządku, ale wydaje się, że ma problem z używaniem konfiguracji XML zamiast konfigurowania za pomocą kodu (i też całkiem sprawiedliwie). Zasugerowałbym na przykład: „Jakie są korzyści z konfigurowania kontenerów IOC za pomocą XML lub innych metod niekodowych?”
Orion Edwards

@Orion Przykłady, które dostarczyłem (z mężczyzną i kobietą) nie wymagają żadnego kontenera IOC. Nic mi nie jest z IOC; używanie kontenera, niezależnie od tego, czy jest on skonfigurowany przy użyciu XML, czy nie, jest dla mnie nadal otwartą kwestią.
Pavel Feldman

@Orion 2: Chociaż używam jakiejś formy IOC w większości projektów, niektóre z nich korzystają z kontenera IOC tak samo, jak korzystają z kontenera przypisania zmiennych lub kontenera if Statement - sam język często mi wystarcza. Nie mam problemu z rekompilacją projektów, nad którymi pracuję, i posiadaniem wygodnego oddzielenia kodu inicjującego tworzenie / test / produkcję. Więc dla mnie tytuł jest w porządku.
Pavel Feldman

Widzę problem z próbką. Jedną z zasad są usługi Inject, a nie dane
Jacek Cz

Odpowiedzi:


40

Dla mnie jednym z głównych powodów używania IoC (i korzystania z konfiguracji zewnętrznej) są dwa obszary:

  • Testowanie
  • Utrzymanie produkcji

Testowanie

Jeśli podzielisz testy na 3 scenariusze (co jest dość normalne w przypadku programowania na dużą skalę):

  1. Testów jednostkowych
  2. Testy integracyjne
  3. Testowanie czarnoskrzynkowe

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:

  • Aplikacja jest dystrybuowana do wielu witryn / klientów, w których środowiska będą się różnić.
  • Ograniczona kontrola nad rozwojem / dane wejściowe dotyczące środowiska produkcyjnego i konfiguracji.
  • Scenariusze testowe.

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:

  • Podczas programowania nie wiesz, kiedy to się zmieni (aplikacja jest tak przydatna, że ​​Twoja firma sprzedaje ją komuś innemu).
  • Nie chcę tkwić w zmianie kodu za każdym razem, gdy wymagana jest niewielka zmiana, która mogłaby zostać obsłużona przez ustawienie i użycie dobrego modelu konfiguracji.

Uwaga: aplikacja oznacza kompletne rozwiązanie (nie tylko plik wykonywalny), a więc wszystkie pliki wymagane do działania aplikacji .


14

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:

  1. Kod DI jest znacznie łatwiejszy do ponownego wykorzystania, ponieważ „zależna” funkcjonalność jest ekstrapolowana na dobrze zdefiniowane interfejsy, co pozwala na dowolne podłączanie oddzielnych obiektów, których konfiguracja jest obsługiwana przez odpowiednią platformę aplikacji, do innych obiektów.
  2. Kod DI jest znacznie łatwiejszy do przetestowania. Funkcjonalność wyrażoną przez obiekt można przetestować w czarnej skrzynce, budując „pozorowane” obiekty implementujące interfejsy oczekiwane przez logikę aplikacji.
  3. Kod DI jest bardziej elastyczny. Jest to z natury luźno powiązany kod - aż do skrajności. Pozwala to programiście wybierać i wybierać, w jaki sposób obiekty są połączone wyłącznie na podstawie ich wymaganych interfejsów na jednym końcu i wyrażonych interfejsów na drugim.
  4. Konfiguracja zewnętrzna (XML) obiektów DI oznacza, że ​​inni mogą dostosowywać kod w nieprzewidzianych kierunkach.
  5. Konfiguracja zewnętrzna jest również oddzieleniem wzorca problemów, ponieważ wszystkie problemy związane z inicjalizacją obiektów i zarządzaniem współzależnościami obiektów mogą być obsługiwane przez serwer aplikacji.
  6. Należy zauważyć, że konfiguracja zewnętrzna nie jest wymagana do korzystania ze wzorca DI, ponieważ w przypadku prostych połączeń często wystarczający jest mały obiekt konstruktora. Istnieje kompromis w elastyczności między nimi. Obiekt budujący nie jest tak elastyczną opcją, jak zewnętrznie widoczny plik konfiguracyjny. Twórca systemu DI musi rozważyć zalety elastyczności nad wygodą, zwracając uwagę na to, aby kontrola na małą skalę, drobnoziarnista nad konstrukcją obiektu, wyrażona w pliku konfiguracyjnym, może zwiększyć zamieszanie i zwiększyć koszty utrzymania.

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.


4
zrozumieć 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. Korzyści, o których wspomniałeś, zapewnia wzorzec projektowy DI. Pytanie dotyczyło korzyści z konfiguracji DI w porównaniu ze zwykłym kodem inicjującym.
Pavel Feldman,

> Konfiguracja zewnętrzna to także separacja ... Oddzielenie konfiguracji jest sercem DI, co jest dobre. I można go zdemontować za pomocą kodu inicjującego. Co dodaje cfg w porównaniu do inicjalizacji kodu? Wydaje mi się, że każda linia cfg ma odpowiednią linię kodu inicjującego.
Pavel Feldman

7

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ę.


5

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.


ma sens, nie myślałem o tym w tej perspektywie
Pavel Feldman

7
z drugiej strony, ludzie często idą w drugą stronę i próbują wprowadzić logikę do danych, co po prostu oznacza, że
kończysz

@Casebash To ciekawy punkt widzenia - przykład byłbym niezwykle zainteresowany. Uważam, że wszystko, co mogę przenieść do danych, pomaga. Uważam również, że jeśli robię to, co mówisz, język jest faktycznie ulepszany, ponieważ jest to DSL - ale nawet wtedy stworzenie zupełnie nowego języka wymaga poważnego uzasadnienia.
Bill K

1
„Za każdym razem, gdy możesz zmienić kod na dane, robisz krok we właściwym kierunku”. Witamy w anty-wzorcu Soft Coding.
Raedwald

@Raedwald To, o czym mówisz, to eksternalizacja, która może być naprawdę trudna, jeśli nie wiesz, co robisz (i powód, dla którego ktoś niekompetentny próbował tego, zawiódł i nazwał to anty-wzorcem). Bardziej pozytywnymi przykładami byłyby iniekcja, iteratory , prawie wszystko z adnotacjami, wszystko, co inicjuje się za pomocą tablic. Większość wspaniałych struktur programistycznych to próba wyodrębnienia różnic w kodzie i połączenia tego, co zostało, napędzając go czymś, co można lepiej grupować i lepiej zarządzać.
Bill K

3

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ć:

<bean id="base" parent="RootXFireBean">
    <property name="secondProperty" ref="secondBean" />
</bean>

<bean id="secondBean" parent="secondaryXFireBean">
    <property name="firstProperty" ref="thirdBean" />
</bean>

<bean id="thirdBean" parent="thirdXFireBean">
    <property name="secondProperty" ref="myNewBean" />
</bean>

<bean id="myNewBean" class="WowItsActuallyTheCodeThatChanged" />

Zamiast tego wyglądało to bardziej tak:

public class TheFirstPointlessClass extends SomeXFireClass {
    public TheFirstPointlessClass() {
        setFirstProperty(new TheSecondPointlessClass());
        setSecondProperty(new TheThingThatWasHereBefore());
    }
}

public class TheSecondPointlessClass extends YetAnotherXFireClass {
    public TheSecondPointlessClass() {
        setFirstProperty(TheThirdPointlessClass());
    }
}

public class TheThirdPointlessClass extends GeeAnotherXFireClass {
    public TheThirdPointlessClass() {
        setFirstProperty(new AnotherThingThatWasHereBefore());
        setSecondProperty(new WowItsActuallyTheCodeThatChanged());
    }
}

public class WowItsActuallyTheCodeThatChanged extends TheXFireClassIActuallyCareAbout {
    public WowItsActuallyTheCodeThatChanged() {
    }

    public overrideTheMethod(Object[] arguments) {
        //Do overridden stuff
    }
}

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.


3

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:

  • wstrzykiwanie stylów / arkuszy stylów dla jednej bazy kodu dla różnych kompilacji
  • wstrzykiwanie różnych zestawów dynamicznej zawartości (lub odniesień do nich) dla pojedynczego kodu
  • wstrzykiwanie kontekstu lokalizacji dla różnych kompilacji / klientów
  • zmiana URI usługi sieciowej na serwer zapasowy (gdy główny nie działa)

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:

  • Wyobraź sobie, że masz system, w którym monitoruje ruch w Twojej witrynie. W zależności od liczby jednoczesnych użytkowników włącza / wyłącza mechanizm logowania. Być może, gdy mechanizm jest wyłączony, na jego miejsce umieszczany jest obiekt pośredni.
  • Wyobraź sobie, że masz system konferencji internetowych, w którym w zależności od liczby użytkowników chcesz wyłączyć możliwość wykonywania P2P w zależności od liczby uczestników

+1 za wyróżnienie aspektu biznesowego u góry. Testowanie źle napisanego starszego kodu może czasami być koszmarem na całe dni.
Richard Le Mesurier

2

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.


rozlokowanie? prawdopodobnie ... utrzymanie wdrożenia? prawdopodobnie ... konserwacja kodu? Wydaje mi się, że nie ... debugowanie przez frameworki jest dużym bólem głowy, a pojos są o wiele łatwiejsze do rozwiązania w tym zakresie.
Mike Stone,

1
Mike, nie mówiłem nic o kodzie. Wszyscy wiemy, że konfiguracja XML jest do niczego :)
aku,

Hmm… jak często zmieniasz komponenty bez ponownej kompilacji i po co? Rozumiem, jeśli ktoś zmienia dane uwierzytelniające DB i nie chce ponownie kompilować programu - może nie być tym, który go rozwija. Ale nie mogę sobie wyobrazić kogoś innego niż programista zmieniający konfigurację wiosny
Pavel Feldman

Pavel zwykle taka sytuacja ma miejsce, gdy trzeba wdrożyć program u setek klientów. W takiej sytuacji dużo łatwiej jest zmienić konfigurację niż wdrożyć nową wersję produktu. Masz rację mówiąc o programistach. Zwykle deweloper tworzy nowy plik cfg, a administrator go wdraża.
aku

2

Możesz włożyć nową implementację dla dziewczyny. Tak więc nowa kobieta może zostać wstrzyknięta bez ponownej kompilacji kodu.

<bean id="jane" class="foo.bar.HotFemale">
  <property name="age" value="19"/>
</bean>
<bean id="mary" class="foo.bar.Female">
  <property name="age" value="23"/>
</bean>
<bean id="john" class="foo.bar.Male">
  <property name="girlfriend" ref="jane"/>
</bean>

(Powyższe zakłada, że ​​Kobieta i HotFemale mają ten sam interfejs GirlfFriend)


Dlaczego modyfikacje logiki bez ponownej kompilacji są uważane za dobry pomysł?
Pavel Feldman,

Zdecydowanie mogę zrobić HotFemale jane = new HotFmale (); jane.setAge (19); john.setGirlfriend (jane); Więc jedyną różnicą jest to, że cfg można zmienić bez ponownej kompilacji? Wydaje się, że jest to częsta odpowiedź, gdy mowa o wiośnie. Czemu?! Dlaczego warto unikać kompilacji?
Pavel Feldman,

Cóż, mogę lepiej przetestować kod. Mogę wyszydzić żeński obiekt.
Paul Whelan,

@Pavel Feldman: ponieważ jeśli masz już wdrożoną aplikację na kliencie, jest to łatwiejsze.
Andrei Rînea

1

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.


To świetnie, że w końcu mogę używać kodu zgodnie z jego przeznaczeniem :) Ale po co mi w ogóle potrzeba czegoś innego niż język programowania?
Pavel Feldman,

Myślę, że dzieje się tak dlatego, że XML umożliwia zmiany w czasie wykonywania lub przynajmniej zmiany konfiguracji bez konieczności ponownej kompilacji projektu.
Romain Verdier,

1

Ł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:

  UI-context.xml
  Model-context.xml
  Controller-context.xml

Lub załaduj za pomocą innego interfejsu użytkownika i kilku dodatkowych kontrolerów:

  AlternateUI-context.xml
  Model-context.xml
  Controller-context.xml
  ControllerAdditions-context.xml

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.


1

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.


To ważny punkt, ale konfiguracja sprężyn często jest raczej złożona. Chociaż łatwo jest zmienić wiek, sysadmin nadal musi radzić sobie z dużym plikiem xml, którego nie musi całkowicie rozumieć. Czy nie lepiej jest wyodrębnić część, która ma być skonfigurowana w coś jeszcze prostszego niż konfiguracja Spring XML? Podobnie jak plik właściwości, z pojedynczą linią „wiek = 23” i nie pozwala administratorowi zmieniać innych szczegółów, takich jak nazwy klas itp., Które wymagają znajomości wewnętrznej struktury programu.
Pavel Feldman

Niedawno pracowałem nad projektem, który zawierał mieszankę kodu Java i XSLT. Zespół był mieszanką ludzi, którzy byli silni w Javie (i być może mniej wygodni w pracy z XML i XSLT); oraz ludzie, którzy bardzo dobrze radzili sobie z XML i XSLT (i mniej czuli się w Javie). Ponieważ konfiguracja miała być zarządzana przez drugą grupę, sensowne było użycie Springa i posiadanie konfiguracji XML. Innymi słowy, Spring rozwiązał problem podziału pracy w zespole. Nie rozwiązało to problemu „technicznego”; w tym sensie, że konfigurację można było równie łatwo wykonać za pomocą kodu Java.
Dawood ibn Kareem

Kiedy „personel pomocy” wiedziałby cokolwiek o konieczności zmiany niektórych klas tworzonych w kontenerze iniekcji zależności? Naprawdę przy założeniu, że zrobienie tego jest zadaniem programisty?
Jimbo

Właśnie z tego powodu wyodrębnianie wartości konfiguracyjnych (np. Adresu URL systemu, z którym się integrujesz) ma sens: personel pomocniczy edytuje plik właściwości lub (w najgorszym przypadku) plik XML, skompilowana klasa Javy pozostaje taka sama.
Miro A.

1

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.


0

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.


0

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:

IService myService;
// ...
public void doSomething() {
  myService.fetchData();
}

(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:

  • Zewnętrzna konfiguracja mapowania między interfejsami i ich konkretnymi implementacjami
  • Kontener IoC obsługuje niektóre trudne problemy, takie jak rozwiązywanie skomplikowanych wykresów zależności, zarządzanie okresem życia komponentu itp.
  • Oszczędzasz czas potrzebny na kodowanie, ponieważ odwzorowania podajesz deklaratywnie, a nie w kodzie proceduralnym
  • 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)

0

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)


-2

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.


Ta odpowiedź dotyczy wstrzykiwania zależności. Wiem, co to jest, wiem, że jest dobre i wyraźnie to stwierdzam w pierwszym zdaniu pytania. Pytanie dotyczyło korzyści z konfiguracji DI w porównaniu do zwykłego kodu inicjującego.
Pavel Feldman,

Tak naprawdę nie odpowiada na pytanie
Casebash,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.