Jaka jest najlepsza definicja wstrzykiwania zależności?

10

Za każdym razem, gdy ktoś do mnie dociera i prosi, abym w sposób koncepcyjny zdefiniował wtrysk zależności i wyjaśnił prawdziwe zalety i wady używania DI w projektowaniu oprogramowania. Przyznaję, że mam trudności z wyjaśnieniem koncepcji DI. Za każdym razem muszę im opowiedzieć historię o zasadzie pojedynczej odpowiedzialności, składzie nad spadkiem itp.

Czy ktoś może mi pomóc w wyjaśnieniu najlepszego sposobu opisania DI dla programistów?

Tiago Sampaio
źródło
2
Wyzwanie polega na tym, że istnieje tak wiele sprzecznych definicji DI. Przyjmuję postawę „czystego DI”: jeśli mam funkcję, która polega na jej parametrach w celu zapewnienia wszystkich stanów, danych itp., Wtedy ta funkcja używa DI. Z drugiej strony, niektórzy będą argumentować, że bez frameworka DI nie ma zastrzyku zależności (choć oczywiście są w błędzie;)). Więc jeśli nie potrafisz doprecyzować definicji, nie możesz zacząć wyjaśniać, co to jest ...
David Arno
Tak więc, jak rozumiem, to nie tylko mój problem.
Tiago Sampaio,
Wszystko sprowadza się do tego: wstrzykiwanie zależności jest jedną z technik stosowanych do osiągnięcia inwersji zależności; wszystko inne to po prostu dodatkowe elementy. Zauważ, że w tych dwóch terminach słowo „zależność” ma nieco inne znaczenie. W iniekcji zależności odnosi się do komponentu, od którego zależy kod. W inwersji zależności odnosi się do samej (ukierunkowanej) relacji - tej, którą chcemy odwrócić. To drugie jest celem, więc główne zalety i wady są takie same; plus dodatkowe obawy związane z faktyczną implementacją, takie jak zarządzanie czasem życia obiektu.
Filip Milovanović,

Odpowiedzi:

22

Dependency Injection to okropna nazwa (IMO) 1 dla dość prostej koncepcji. Oto przykład:

  1. Masz metodę (lub klasę z metodami), która wykonuje X (np. Pobiera dane z bazy danych)
  2. W ramach wykonywania X wspomniana metoda tworzy wewnętrzny zasób i zarządza nim (np. A DbContext). Ten wewnętrzny zasób nazywany jest zależnością
  3. Usuwasz tworzenie i zarządzanie zasobem (tj. DbContext) Z metody i nakładasz na osobę wywołującą obowiązek zapewnienia tego zasobu (jako parametru metody lub przy tworzeniu wystąpienia klasy)
  4. Robisz teraz wstrzykiwanie zależności.


[1] : Pochodzę z niższego poziomu i zajęło mi miesiące usiąść i nauczyć się wstrzykiwania zależności, ponieważ nazwa sugeruje, że byłoby to coś znacznie bardziej skomplikowanego, na przykład DLL Injection . Fakt, że Visual Studio (i my ogólnie programiści) odnosi się do bibliotek .NET (bibliotek DLL lub zestawów ), od których zależy projekt, ponieważ zależności wcale nie pomaga. Istnieje nawet coś takiego jak Dependency Walker (depends.exe) .


[Edytuj] Pomyślałem, że jakiś kod demo przydałby się dla niektórych, więc oto jeden (w C #).

Bez iniekcji zależności:

public class Repository : IDisposable
{
    protected DbContext Context { get; }

    public Repository()
    {
        Context = new DbContext("name=MyEntities");
    }

    public void Dispose()
    {
        Context.Dispose();
    }
}

Twój konsument zrobiłby wtedy coś takiego:

using ( var repository = new Repository() )
{
    // work
}

Ta sama klasa zaimplementowana z wzorcem wstrzykiwania zależności wyglądałaby następująco:

public class RepositoryWithDI
{
    protected DbContext Context { get; }

    public RepositoryWithDI(DbContext context)
    {
        Context = context;
    }
}

Teraz to osoba dzwoniąca jest odpowiedzialna za utworzenie wystąpienia DbContexti przekazanie (errm, wstrzyknięcie ) swojej klasie:

using ( var context = new DbContext("name=MyEntities") )
{
    var repository = new RepositoryWithDI(context);

    // work
}
Marc.2377
źródło
3
Należy to dodać do Wikipedii.
Evorlor,
2
Teraz to osoba dzwoniąca jest odpowiedzialna za utworzenie wystąpienia DbContext - myślę, że to byłby obowiązek punktu wejścia aplikacji do utworzenia wszystkich wymaganych zależności. Tak więc konsument musi jedynie wprowadzić wymagany typ jako zależność we własnej umowie.
Fabio
@Fabio To może być. (W takim przypadku odpowiedzialnością wywołującego byłoby dostarczenie zasobu, który został utworzony przy uruchomieniu aplikacji do wywoływanej metody / klasy). W moim przykładzie tak nie jest, ponieważ nie jest to wymóg wyjaśnienia pojęcia wstrzykiwania zależności .
Marc.2377,
5

Pojęcia abstrakcyjne są często lepiej wyjaśniane za pomocą analogii z prawdziwego świata. Oto moja analogia:

Prowadzisz sklep z kanapkami. Robisz niesamowite kanapki, ale niewiele wiesz o samym chlebie. Masz tylko łagodny biały chleb. Twoja praca w całości koncentruje się na dodatkach, których używasz do zamiany chleba na kanapkę.

Jednak niektórzy klienci wolą brązowy chleb. Niektórzy wolą pełnoziarnisty. Tak naprawdę to nie obchodzi, możesz zrobić dowolną niesamowitą kanapkę, pod warunkiem, że jest to chleb o podobnej wielkości. Naprawdę nie chcesz też brać na siebie dodatkowej odpowiedzialności za zaopatrywanie się w kilka rodzajów chleba i utrzymywanie zapasów. Nawet jeśli zaopatrzysz się w kilka rodzajów chleba, zawsze znajdzie się jakiś klient o egzotycznym smaku chleba, którego nie można racjonalnie przewidzieć.

Ustanawiasz więc nową zasadę: klienci przynoszą własny chleb. Sam już nie zapewniasz chleba. Jest to sytuacja korzystna dla obu stron: klienci mają dokładnie taki chleb, jaki chcą, a Ty nie musisz już męczyć się o chleb, na którym ci nie zależy. W końcu jesteś opiekaczem kanapek, a nie piekarzem.

Aha, aby zadowolić klientów, którzy nie chcą kupować własnego chleba, otworzysz drugi sklep obok, w którym sprzedajesz swoje oryginalne, łagodne białe pieczywo. Klienci, którzy nie przynieśli własnego chleba, muszą po prostu dostać domyślny, a następnie przyjść do ciebie, aby zrobić z nim kanapkę.

Nie jest idealny, ale podkreśla kluczową funkcję: kontrolę konsumenta . Nieodłączną korzyścią dla obu stron jest to, że nie musisz już nabywać własnych zależności, a konsumentowi nie przeszkadza wybór zależności.

Flater
źródło
1
Podoba mi się to, ale OP szuka wyjaśnienia dla programistów . Początkowa abstrakcja jest dobra, ale wcześniej czy później będzie potrzebować przykładu z prawdziwego życia.
Robbie Dee
1
@RobbieDee: Kiedy cel wzorca jest jasny, jego implementacja również staje się jasna. Na przykład odpowiedź Marca jest absolutnie poprawna, ale wydaje mi się, że wyjaśnienie to zagmatwa złożona natura przykładowej sytuacji, której używa. Sprowadza się to do: „Jeśli chcesz zbudować statek, nie zbieraj ludzi do zbierania drewna, nie przydzielaj im zadań i prac, ale raczej naucz ich tęsknić za nieskończonym bezkresem morza”. . Zamiast wyjaśniać, co robić, wolę wyjaśniać, dlaczego to robić.
Flater
2
Oczywiście masz rację, ale nie mogę przestać myśleć, że potrzebowałbym konkretnego przykładu - takiego jak brak prawdziwego systemu plików lub bazy danych, aby pobudzić mój apetyt, ale może to tylko mój wąski pogląd programisty :)
Robbie Dee
1

Prosta odpowiedź na to:

Przede wszystkim klasa powinna mieć ściśle określoną odpowiedzialność, a wszystko poza tym zakresem powinno znajdować się poza tą klasą. Powiedziawszy to, Dependency Injection ma miejsce wtedy, gdy wstrzykujesz funkcjonalność z innej klasy B do klasy A, korzystając z pomocy „strony trzeciej”, aby osiągnąć to rozdzielenie obaw, pomagając klasie A wykonać pewne operacje, które są poza jej zakresem.

.Net Core jest całkiem dobrym przykładem, który możesz podać, ponieważ ten framework używa dużo wstrzykiwania zależności. Zasadniczo usługi, które chcesz wstrzyknąć, znajdują się w startup.cspliku.

Jasne, uczeń powinien być świadomy niektórych pojęć, takich jak polimorfizm, interfejsy i zasady projektowania OOP.

Fernando Calazans
źródło
0

Wokół tego, co jest w zasadzie prostą koncepcją, jest dużo puchu i bunkum.

Bardzo łatwo jest też zagłębić się w „ jakiego frameworku powinienem użyć ”, kiedy można to zrobić po prostu w kodzie.

Oto definicja, której osobiście używam:

Biorąc pod uwagę zachowanie X z zależnością Y. Wstrzykiwanie zależności polega na możliwości dostarczenia dowolnego Y, które spełnia kryteria bycia instancją Y, nawet jeśli nie masz.

Niektóre przykłady mogą być takie, gdzie Y jest połączeniem systemu plików lub bazy danych.

Frameworki, takie jak moq, umożliwiają definiowanie dubletów (udawanie wersji Y) za pomocą interfejsu, dzięki czemu możliwe jest wstrzyknięcie w instancję Y, gdzie Y jest na przykład połączeniem z bazą danych.

Łatwo wpaść w pułapkę przekonania, że ​​jest to wyłącznie kwestia testowania jednostkowego, ale jest to bardzo użyteczny wzorzec dla każdego fragmentu kodu, w którym oczekuje się zmiany i prawdopodobnie dobrej praktyki.

Robbie Dee
źródło
0

Zapewniamy zachowanie funkcji w czasie wykonywania poprzez metodę wstawiania tego zachowania do funkcji za pomocą parametru.

Wzorzec strategii jest doskonałym przykładem zastrzyku zależności.

ShinEmperor
źródło
0

Aby zrobić to dobrze, musimy najpierw zdefiniować zależności i zastrzyk.

  • Zależność: każdy zasób potrzebny operacji.
  • Zastrzyk: przekazanie tego zasobu do operacji, zwykle jako argument metody.

Podstawowym przykładem byłaby metoda, która dodaje dwie wartości. Oczywiście metody te wymagają dodania wartości. Jeśli zostaną dostarczone przez przekazanie ich jako argumentów, byłby to już przypadek wstrzyknięcia zależności. Alternatywą byłoby zaimplementowanie argumentów jako właściwości lub zmiennych globalnych. W ten sposób nie zostałyby wstrzyknięte żadne zależności, zależności byłyby dostępne z zewnątrz z góry.

Załóżmy, że zamiast tego używasz właściwości i nadajesz im nazwy A i B. Jeśli zmienisz nazwy na Op1 i Op2, przerwiesz metodę Add. Lub twoje IDE zaktualizuje dla ciebie wszystkie nazwy, chodzi o to, że metoda również musiałaby zostać zaktualizowana, ponieważ ma zależności od zasobów zewnętrznych.

Ten przykład jest prosty, ale możesz sobie wyobrazić bardziej złożone przykłady, w których metoda wykonuje operację na obiekcie, takim jak obraz lub gdzie odczytuje ze strumienia pliku. Czy chcesz, aby metoda sięgnęła po obraz, wymagając, aby wiedział, gdzie on jest? Nie. Czy chcesz, aby metoda otworzyła sam plik, wymagając od niego wiedzy, gdzie szukać pliku, a nawet wiedząc, że będzie on czytał z pliku? Nie.

Chodzi o to, aby zredukować funkcjonalność metody do jej podstawowego zachowania i oddzielić metodę od środowiska. Otrzymujesz pierwszy, robiąc drugi, możesz uznać to za definicję wstrzykiwania zależności.

Korzyści: ponieważ wyeliminowano zależności dla środowiska metody, zmiany w metodzie nie będą miały wpływu na środowisko i odwrotnie. => Aplikacja staje się łatwiejsza w utrzymaniu (modyfikacji).

Martin Maat
źródło