Zrozumienie wstrzykiwania zależności

109

Czytam o iniekcji zależności (DI). Dla mnie jest to bardzo skomplikowana rzecz, ponieważ czytałem, że odnosi się to również do inwersji kontroli (IoC) i czułem, że będę w podróży.

Rozumiem to: zamiast tworzyć model w klasie, która również go zużywa, przekazujesz (wstrzykujesz) model (już wypełniony interesującymi właściwościami) tam, gdzie jest potrzebny (do nowej klasy, która może wziąć go jako parametr w konstruktor).

Dla mnie to tylko argument. Musiałem nie rozumieć o co chodzi? Może staje się to bardziej oczywiste przy większych projektach?

Moje rozumienie jest inne niż DI (przy użyciu pseudo kodu):

public void Start()
{
    MyClass class = new MyClass();
}

...

public MyClass()
{
    this.MyInterface = new MyInterface(); 
}

I DI byłoby

public void Start()
{
    MyInterface myInterface = new MyInterface();
    MyClass class = new MyClass(myInterface);
}

...

public MyClass(MyInterface myInterface)
{
    this.MyInterface = myInterface; 
}

Czy ktoś mógłby rzucić trochę światła, bo jestem pewien, że jestem w błocie.

MyDaftQuestions
źródło
5
Spróbuj mieć 5 MyInterface i MyClass ORAZ mieć wiele klas tworzących łańcuch zależności. Stawianie wszystkiego od samego początku staje się uciążliwe.
Euforyczny
159
Dla mnie to tylko argument. Musiałem źle zrozumieć sedno? ” Nie. Masz to. To jest „zastrzyk zależności”. Teraz zobacz, jaki inny szalony żargon możesz wymyślić dla prostych pomysłów, jest fajnie!
Eric Lippert,
8
Nie mogę głosować wystarczająco za komentarzem pana Lipperta. Ja też uznałem, że to mylący żargon w odniesieniu do czegoś, co i tak robiłem naturalnie.
Alex Humphrey
16
@EricLippert: Chociaż poprawne, myślę, że to nieco redukujące. Parameters-as-DI nie jest od razu prostą koncepcją dla przeciętnego programisty imperative / OO, który jest przyzwyczajony do przekazywania danych. DI tutaj pozwala ci przekazywać zachowania , które wymagają bardziej elastycznego spojrzenia na świat niż średni mierny programista.
Phoshi
14
@ Phoshi: Masz rację, ale wskazałeś także, dlaczego jestem sceptyczny, że „zastrzyk uzależnienia” to dobry pomysł. Jeśli napiszę klasę, która zależy od jej poprawności i wydajności od zachowania innej klasy, ostatnią rzeczą, którą chcę zrobić, jest uczynienie użytkownika klasy odpowiedzialnym za prawidłowe konstruowanie i konfigurowanie zależności! To moja praca. Za każdym razem, gdy pozwalasz klientowi na wstrzyknięcie zależności, tworzysz punkt, w którym konsument może zrobić to źle.
Eric Lippert

Odpowiedzi:

106

Tak, wstrzykujesz swoje zależności, albo przez konstruktor, albo przez właściwości.
Jednym z powodów tego jest nie obciążanie MyClass szczegółowymi informacjami na temat tego, jak należy utworzyć instancję MyInterface. MyInterface może być czymś, co samo ma całą listę zależności, a kod MyClass stałby się brzydki bardzo szybko, gdybyś utworzył instancję wszystkich zależności MyInterface w MyClass.

Innym powodem są testy.
Jeśli masz zależność od interfejsu czytnika plików i wstrzykujesz tę zależność za pomocą konstruktora, powiedzmy, ConsumerClass, co oznacza, że ​​podczas testowania możesz przekazać implementację czytnika plików w pamięci do ConsumerClass, unikając konieczności zrobić I / O podczas testowania.

Stefan Billiet
źródło
13
To jest świetne, dobrze wyjaśnione. Proszę przyjąć wyimaginowaną +1, ponieważ mój przedstawiciel jeszcze na to nie zezwala!
MyDaftQuestions
11
Skomplikowany scenariusz budowy jest dobrym kandydatem do zastosowania wzoru fabrycznego. W ten sposób fabryka bierze na siebie odpowiedzialność za konstruowanie obiektu, aby sama klasa nie musiała i trzymała się zasady pojedynczej odpowiedzialności.
Matt Gibson
1
@MattGibson dobry punkt.
Stefan Billiet
2
+1 za testowanie, dobrze skonstruowana DI pomoże zarówno przyspieszyć testowanie, jak i wykonać testy jednostkowe zawarte w testowanej klasie.
Umur Kontacı
52

Sposób implementacji DI zależy bardzo od używanego języka.

Oto prosty przykład inny niż DI:

class Foo {
    private Bar bar;
    private Qux qux;

    public Foo() {
        bar = new Bar();
        qux = new Qux();
    }
}

To jest do bani, np. Gdy do testu chcę użyć próbnego obiektu bar. Możemy więc uczynić to bardziej elastycznym i pozwolić na przekazywanie instancji przez konstruktor:

class Foo {
    private Bar bar;
    private Qux qux;

    public Foo(Bar bar, Qux qux) {
        this.bar = bar;
        this.qux = qux;
    }
}

// in production:
new Foo(new Bar(), new Qux());
// in test:
new Foo(new BarMock(), new Qux());

Jest to już najprostsza forma wstrzykiwania zależności. Ale nadal jest to do bani, ponieważ wszystko musi być wykonane ręcznie (również dlatego, że osoba dzwoniąca może przechowywać odwołanie do naszych wewnętrznych obiektów i tym samym unieważniać nasz stan).

Możemy wprowadzić więcej abstrakcji, korzystając z fabryk:

  • Jedną z opcji jest Foowygenerowanie przez abstrakcyjną fabrykę

    interface FooFactory {
        public Foo makeFoo();
    }
    
    class ProductionFooFactory implements FooFactory {
        public Foo makeFoo() { return new Foo(new Bar(), new Baz()) }
    }
    
    class TestFooFactory implements FooFactory {
        public Foo makeFoo() { return new Foo(new BarMock(), new Baz()) }
    }
    
    FooFactory fac = ...; // depends on test or production
    Foo foo = fac.makeFoo();
    
  • Inną opcją jest przekazanie fabryki do konstruktora:

    interface DependencyManager {
        public Bar makeBar();
        public Qux makeQux();
    }
    class ProductionDM implements DependencyManager {
        public Bar makeBar() { return new Bar() }
        public Qux makeQux() { return new Qux() }
    }
    class TestDM implements DependencyManager {
        public Bar makeBar() { return new BarMock() }
        public Qux makeQux() { return new Qux() }
    }
    
    class Foo {
        private Bar bar;
        private Qux qux;
    
        public Foo(DependencyManager dm) {
            bar = dm.makeBar();
            qux = dm.makeQux();
        }
    }
    

Pozostałe problemy to to, że musimy napisać nową DependencyManagerpodklasę dla każdej konfiguracji i że liczba zależności, którymi można zarządzać, jest dość ograniczona (każda nowa zależność wymaga nowej metody w interfejsie).

Dzięki funkcjom takim jak odbicie i dynamiczne ładowanie klas możemy to obejść. Ale zależy to bardzo od użytego języka. W Perlu do klas można odwoływać się według ich nazw, a ja po prostu mogę

package Foo {
    use signatures;

    sub new($class, $dm) {
        return bless {
            bar => $dm->{bar}->new,
            qux => $dm->{qux}->new,
        } => $class;
    }
}

my $prod = { bar => 'My::Bar', qux => 'My::Qux' };
my $test = { bar => 'BarMock', qux => 'QuxMock' };
$test->{bar} = 'OtherBarMock';  # change conf at runtime

my $foo = Foo->new(rand > 0.5 ? $prod : $test);

W językach takich jak Java mogę mieć menedżera zależności działającego podobnie do Map<Class, Object>:

Bar bar = dm.make(Bar.class);

Do której faktycznej klasy Bar.classrozwiązuje się problem można skonfigurować w czasie wykonywania, np. Utrzymując Map<Class, Class>interfejs odwzorowujący na implementacje.

Map<Class, Class> dependencies = ...;

public <T> T make(Class<T> c) throws ... {
    // plus a lot more error checking...
    return dependencies.get(c).newInstance();
}

W pisaniu konstruktora nadal jest element manualny. Ale możemy sprawić, że konstruktor stanie się całkowicie niepotrzebny, np. Poprzez sterowanie DI za pomocą adnotacji:

class Foo {
    @Inject(Bar.class)
    private Bar bar;

    @Inject(Qux.class)
    private Qux qux;

    ...
}

dm.make(Foo.class);  // takes care of initializing "bar" and "qux"

Oto przykładowa implementacja małego (i bardzo ograniczonego) frameworka DI: http://ideone.com/b2ubuF , chociaż ta implementacja jest całkowicie bezużyteczna dla niezmiennych obiektów (ta naiwna implementacja nie może przyjmować żadnych parametrów dla konstruktora).

amon
źródło
7
To super z świetnymi przykładami, chociaż zajmie mi to kilka razy, aby to wszystko przetrawić, ale dzięki za poświęcenie tyle czasu.
MyDaftQuestions
33
To zabawne, że każde kolejne uproszczenie jest bardziej złożone
Kromster
48

Nasi przyjaciele na Stack Overflow mają na to fajną odpowiedź . Moim ulubionym jest druga odpowiedź, w tym cytat:

„Wstrzykiwanie zależności” to termin 25 dolarów za koncepcję 5 centów. (...) Wstrzykiwanie zależności oznacza nadanie obiektowi zmiennych instancji. (...).

z bloga Jamesa Shore'a . Oczywiście istnieją bardziej złożone wersje / wzory warstwowe, ale wystarczy po prostu zrozumieć, co się dzieje. Zamiast obiektu tworzącego własne zmienne instancji, są one przekazywane z zewnątrz.

użytkownik967543
źródło
10
Przeczytałem to ... i do tej pory nie miało to sensu ... Teraz ma sens. Zależność nie jest tak wielkim pojęciem do zrozumienia (bez względu na to, jak potężny jest), ale ma dużą nazwę i związany z tym hałas w Internecie!
MyDaftQuestions
18

Załóżmy, że zajmujesz się projektowaniem wnętrz w swoim salonie: instalujesz na suficie fantazyjny żyrandol i podłączasz klasyczną, dopasowaną lampę podłogową. Rok później twoja urocza żona postanawia, że ​​wolałaby pokój nieco mniej formalny. Którą oprawę będzie łatwiej zmienić?

Ideą DI jest stosowanie podejścia „plug-in” wszędzie. Może to nie wydawać się wielkim problemem, jeśli mieszkasz w zwykłej chacie bez suchej zabudowy i odsłoniętych wszystkich przewodów elektrycznych. (Jesteś złotą rączką - możesz zmienić wszystko!) I podobnie, w małej i prostej aplikacji, DI może dodać więcej komplikacji, niż jest to warte.

Jednak w przypadku dużych i złożonych aplikacji DI jest niezbędny do długotrwałej konserwacji. Umożliwia „odłączenie” całych modułów od kodu (na przykład zaplecza bazy danych) i wymianę ich z różnymi modułami, które osiągają to samo, ale robią to w zupełnie inny sposób (na przykład system pamięci masowej w chmurze).

BTW, dyskusja na temat DI byłaby niekompletna bez rekomendacji Dependency Injection w .NET , autorstwa Marka Seemana. Jeśli znasz platformę .NET i kiedykolwiek zamierzasz uczestniczyć w tworzeniu oprogramowania SW na dużą skalę, jest to niezbędna lektura. Wyjaśnia tę koncepcję znacznie lepiej niż ja.

Pozwólcie, że zostawię wam ostatnią istotną cechę DI, którą przeoczy wasz przykład kodu. DI umożliwia wykorzystanie ogromnego potencjału elastyczności zawartego w powiedzeniu „ Programuj interfejsy ”. Aby nieznacznie zmodyfikować przykład:

public void Main()
{
    ILightFixture fixture = new ClassyChandelier();
    MyRoom room = new MyRoom (fixture);
}
...
public MyRoom(ILightFixture fixture)
{
    this.MyLightFixture = fixture ; 
}

Ale TERAZ, ponieważ MyRoomjest przeznaczony do interfejsu ILightFixture , mogę łatwo przejść w przyszłym roku i zmienić jedną linię w funkcji głównej („wstrzyknąć” inną „zależność”) i natychmiast uzyskać nową funkcjonalność w MyRoom (bez konieczności przebuduj lub ponownie wdróż MyRoom, jeśli zdarzy się, że znajduje się w osobnej bibliotece. Zakłada to oczywiście, że wszystkie implementacje ILightFixture zostały zaprojektowane z myślą o podstawieniu Liskova). Wszystko za pomocą prostej zmiany:

public void Main()
{
    ILightFixture fixture = new MuchLessFormalLightFixture();
    MyRoom room = new MyRoom (fixture);
}

Plus, nie mogę teraz Jednostka testowa MyRoom(ty jesteś testowanie jednostkowe, prawda?) I użyć „mock” lampę, która pozwala mi testować MyRoomcałkowicie niezależna odClassyChandelier.

Oczywiście jest wiele innych zalet, ale to one sprzedały mi ten pomysł.

kmote
źródło
Chciałem podziękować, było to dla mnie pomocne, ale nie chcą, abyś to zrobił, ale raczej użyj komentarzy, aby zasugerować ulepszenia, więc jeśli masz na to ochotę, możesz dodać jedną z innych zalet, o których wspomniałeś. Ale poza tym, dzięki, to mi pomogło.
Steve
2
Interesuje mnie również książka, do której się odwoływałeś, ale niepokoi mnie to, że na ten temat jest 584 stronicowa książka.
Steve
4

Wstrzykiwanie zależności tylko przekazuje parametr, ale nadal prowadzi to do pewnych problemów, a nazwa ma na celu skupienie się na tym, dlaczego różnica między tworzeniem obiektu a przekazywaniem jest ważna.

Jest to ważne, ponieważ (oczywiście), gdy obiekt A wywoła typ B, A zależy od tego, jak działa B. Co oznacza, że ​​jeśli A podejmie decyzję o konkretnym typie B, którego użyje, wówczas utracona zostanie duża elastyczność w tym, w jaki sposób A może być używane przez klasy zewnętrzne, bez modyfikacji A.

Wspominam o tym, ponieważ mówisz o „pomijaniu sedna” zastrzyku uzależnienia, i właśnie dlatego ludzie lubią to robić. Może to po prostu oznaczać przekazanie parametru, ale to, czy to zrobisz, może być ważne.

Ponadto niektóre trudności z faktycznym wdrażaniem technologii DI lepiej pokazują się w dużych projektach. Na ogół przeniesiesz faktyczną decyzję, które konkretne klasy zastosować do samego końca, aby Twoja metoda początkowa mogła wyglądać następująco:

public void Start()
{
    MySubSubInterfaceA mySubSubInterfaceA = new mySubSubInterfaceA();
    MySubSubInterfaceB mySubSubInterfaceB = new mySubSubInterfaceB();     
    MySubInterface mySubInterface = new MySubInterface(mySubSubInterfaceA,mySubSubInterfaceB);
    MyInterface myInterface = new MyInterface(MySubInterface);
    MyClass class = new MyClass(myInterface);
}

ale z kolejnymi 400 liniami tego rodzaju nitującego kodu. Jeśli wyobrażasz sobie utrzymanie tego z czasem, atrakcyjność kontenerów DI staje się bardziej widoczna.

Wyobraź sobie także klasę, która, powiedzmy, implementuje IDisposable. Jeśli klasy, które go używają, pobierają go poprzez wstrzyknięcie, a nie tworzą go same, to skąd wiedzą, kiedy wywołać Dispose ()? (Który to kolejny problem, którym zajmuje się kontener DI).

Tak, oczywiście, wstrzykiwanie zależności jest po prostu przekazywaniem parametru, ale tak naprawdę, gdy ludzie mówią, że wstrzykiwanie zależności oznacza „przekazywanie parametru do obiektu w porównaniu z alternatywą polegającą na tym, że obiekt sam tworzy instancję czegoś samego”, i radzenie sobie ze wszystkimi zaletami wad takie podejście, być może przy pomocy kontenera DI.

psr
źródło
To dużo subwooferów i interfejsów! Czy chodzi o nowy interfejs zamiast klasy, która implementuje interfejs? Wydaje się źle.
JBRWilkinson
@JBRWilkinson Mam na myśli klasę, która implementuje interfejs. Powinienem to wyjaśnić. Chodzi jednak o to, że duża aplikacja będzie miała klasy z zależnościami, które z kolei mają zależności, które z kolei mają zależności ... Ustawienie tego wszystkiego ręcznie w metodzie Main jest wynikiem wykonywania wielu iniekcji konstruktora.
psr
4

Na poziomie klasy jest to łatwe.

„Dependency Injection” po prostu odpowiada na pytanie „jak znajdę moich współpracowników” za pomocą „oni są na ciebie naciskani - nie musisz iść i sam ich zdobyć”. (Jest podobny - ale nie taki sam jak - „Inwersja kontroli”, gdzie na pytanie „w jaki sposób mam zamówić operacje na podstawie moich danych wejściowych?” Ma podobną odpowiedź).

Tylko korzyść, że posiadanie współpracownicy popchnął na ciebie jest to, że umożliwia kodu klienckiego do korzystania z klasy do komponowania wykres obiektu, który odpowiada swoją obecną potrzebę ... nie ma arbitralnie z góry określony kształt i zmienność na wykresie przez prywatnie decydując konkretne typy i cykl życia współpracowników.

(Wszystkie te inne korzyści, testowalność, luźne sprzężenie itp. Wynikają w dużej mierze z zastosowania interfejsów, a nie tyle z zależności wstrzykiwania zależności, chociaż DI naturalnie promuje stosowanie interfejsów).

Warto zauważyć, że jeśli unikniesz tworzenia własnych współpracowników, twoja klasa musi zatem uzyskać współpracowników od konstruktora, właściwości lub argumentu metody (ta druga opcja jest często pomijana, nawiasem mówiąc ... robi to nie zawsze ma sens, aby „kolaboranci klasy” byli częścią swojego „stanu”).

I to dobrze.

Na poziomie aplikacji ...

Tyle o widoku rzeczy dla poszczególnych klas. Załóżmy, że masz wiele klas, które działają zgodnie z zasadą „nie twórz własnych współpracowników” i chcesz złożyć z nich aplikację. Najprostszą rzeczą do zrobienia jest użycie starego dobrego kodu (bardzo przydatnego narzędzia do wywoływania konstruktorów, właściwości i metod!) Do skomponowania pożądanego wykresu obiektowego i dołożenia do niego pewnych danych wejściowych. (Tak, niektóre z tych obiektów na wykresie same w sobie będą obiektami-fabrykami, które zostały przekazane jako współpracownicy innym długowiecznym obiektom na wykresie, gotowym do obsługi ... nie można wstępnie zbudować każdego obiektu! ).

... potrzeba „elastycznego” skonfigurowania wykresu obiektowego aplikacji ...

W zależności od innych (niezwiązanych z kodem) celów możesz chcieć dać użytkownikowi końcowemu kontrolę nad wdrożonym w ten sposób wykresem obiektowym. To prowadzi cię w kierunku schematu konfiguracji, niezależnie od tego, czy jest to plik tekstowy własnego projektu z pewnymi parami nazwa / wartość, plik XML, niestandardowy DSL, deklaratywny język opisu graficznego, taki jak YAML, imperatywny język skryptowy, taki jak JavaScript, lub coś innego odpowiedniego do wykonywanego zadania. Wszystko, czego potrzeba, aby skomponować prawidłowy wykres obiektowy, w sposób odpowiadający potrzebom użytkowników.

... może być znaczącą siłą projektową.

W najbardziej skrajnej sytuacji tego typu, można zdecydować się trwać bardzo ogólne podejście i dać użytkownikom końcowym ogólny mechanizm „okablowanie up” z obiektu-wykres ich wyboru, a nawet pozwolić im , aby zapewnić konkretne realizacje interfejsów do środowisko uruchomieniowe! (Twoja dokumentacja jest lśniącym klejnotem, użytkownicy są bardzo inteligentni, znają co najmniej gruboziarnisty zarys grafu obiektowego aplikacji, ale nie mają przydatnego kompilatora). Ten scenariusz może teoretycznie wystąpić w niektórych sytuacjach „korporacyjnych”.

W takim przypadku prawdopodobnie masz deklaratywny język, który pozwala użytkownikom wyrażać dowolny typ, kompozycję grafu obiektowego takich typów oraz paletę interfejsów, które mityczny użytkownik końcowy może mieszać i dopasowywać. Aby zmniejszyć obciążenie poznawcze użytkowników, wolisz podejście „konfiguracja zgodnie z konwencją”, tak aby musieli tylko wkroczyć i przejechać interesujący fragment wykresu obiektowego, zamiast zmagać się z całością.

Ty biedny darni!

Ponieważ nie masz ochoty pisać tego wszystkiego samemu (ale poważnie, sprawdź powiązanie YAML dla swojego języka), używasz pewnego rodzaju frameworku DI.

W zależności od dojrzałości tego frameworka, możesz nie mieć opcji użycia wstrzykiwania przez konstruktora, nawet jeśli ma to sens (kolaboranci nie zmieniają się przez cały czas istnienia obiektu), zmuszając w ten sposób do użycia Settera Injection (nawet gdy współpracownicy nie zmieniają się przez cały czas istnienia obiektu, a nawet jeśli nie ma tak naprawdę logicznego powodu, dla którego wszystkie konkretne implementacje interfejsu muszą mieć współpracowników określonego typu). Jeśli tak, to obecnie znajdujesz się w piekle silnie sprzężonym, pomimo starannego „wykorzystywania interfejsów” w całej bazie kodu - horror!

Mamy jednak nadzieję, że użyłeś frameworka DI, który daje ci możliwość wstrzyknięcia konstruktora, a twoi użytkownicy są trochę zrzędliwi, że nie spędzasz więcej czasu na zastanawianiu się nad konkretnymi rzeczami, których potrzebowali do skonfigurowania i nadaniu im interfejsu bardziej odpowiedniego do zadania na dłoni. (Chociaż, żeby być uczciwym, prawdopodobnie próbowałeś wymyślić jakiś sposób, ale JavaEE Cię zawiodło i musiałeś uciekać się do tego okropnego włamania).

Bootnote

Nigdy nie używasz Google Guice, co daje koderowi sposób na zrezygnowanie z tworzenia wykresu obiektowego z kodem ... przez pisanie kodu. Argh!

David Bullock
źródło
0

Wiele osób mówi tutaj, że DI jest mechanizmem outsourcingu inicjalizacji zmiennych instancji.

Dla oczywistych i prostych przykładów tak naprawdę nie potrzebujesz „wstrzykiwania zależności”. Możesz to zrobić za pomocą prostego parametru przekazanego do konstruktora, jak zauważyłeś w pytaniu.

Myślę jednak, że dedykowany mechanizm „wstrzykiwania zależności” jest przydatny, gdy mówimy o zasobach na poziomie aplikacji, w których zarówno osoba dzwoniąca, jak i osoba odbierająca nic nie wie o tych zasobach (i nie chce wiedzieć!).

Na przykład: połączenie z bazą danych, kontekst bezpieczeństwa, funkcja rejestrowania, plik konfiguracyjny itp.

Ponadto, ogólnie rzecz biorąc, każdy singleton jest dobrym kandydatem na DI. Im więcej masz i używasz, tym większe szanse na DI.

W przypadku tego rodzaju zasobów jest całkiem jasne, że nie ma sensu przenosić ich dookoła za pomocą list parametrów, a zatem wynaleziono DI.

Ostatnia uwaga, potrzebujesz również pojemnika DI, który wykona rzeczywisty zastrzyk ... (ponownie, aby zwolnić zarówno dzwoniącego, jak i odbierającego ze szczegółów implementacji).

gamliela
źródło
-2

Jeśli przekażesz abstrakcyjny typ odwołania, kod wywołujący może przekazać dowolny obiekt, który rozszerza / implementuje typ abstrakcyjny. Tak więc w przyszłości klasa, która korzysta z tego obiektu, mogłaby użyć nowego obiektu, który został utworzony bez modyfikowania kodu.

DavidB
źródło
-4

To kolejna opcja oprócz odpowiedzi Amona

Użyj konstruktorów:

klasa Foo {
    prywatny bar;
    prywatny Qux qux;

    prywatny Foo () {
    }

    public Foo (Builder builder) {
        this.bar = builder.bar;
        this.qux = builder.qux;
    }

    konstruktor publicznej klasy statycznej {
        prywatny bar;
        prywatny Qux qux;
        public Buider setBar (Bar Bar) {
            this.bar = bar;
            zwróć to;
        }
        public Builder setQux (Qux qux) {
            this.qux = qux;
            zwróć to;
        }
        public Foo build () {
            zwraca nowy Foo (ten);
        }
    }
}

// w produkcji:
Foo foo = nowy Foo.Builder ()
    .setBar (nowy pasek ())
    .setQux (new Qux ())
    .budować();

// w teście:
Foo testFoo = nowy Foo.Builder ()
    .setBar (nowy BarMock ())
    .setQux (new Qux ())
    .budować();

Tego używam do zależności. Nie używam żadnego środowiska DI. Oni przeszkadzają.

użytkownik3155341
źródło
4
Nie dodaje to wiele do innych odpowiedzi sprzed roku i nie wyjaśnia, dlaczego budowniczy może pomóc pytającemu zrozumieć zastrzyk zależności.
@Snowman Jest to INNA opcja zamiast DI. Przepraszam, że nie widzisz tego w ten sposób. Dziękuję za głosowanie w dół.
user3155341
1
pytający chciał zrozumieć, czy DI naprawdę jest tak proste jak przekazanie parametru, nie prosił o alternatywy dla DI.
1
Przykłady PO są bardzo dobre. Twoje przykłady zapewniają większą złożoność czegoś, co jest prostym pomysłem. Nie ilustrują problemu pytania.
Adam Zuckerman
Konstruktor DI przekazuje parametr. Tyle że przekazujesz obiekt interfejsu zamiast konkretnego obiektu klasy. To ta „zależność” od konkretnej implementacji jest rozwiązywana za pośrednictwem DI. Konstruktor nie wie, jaki typ jest przekazywany, nie obchodzi go to, po prostu wie, że otrzymuje typ, który implementuje interfejs. Sugeruję PluralSight, ma świetny kurs dla początkujących / DI.
Hardgraf