Jaka jest praktyczna różnica między stylami wstrzykiwania zależności?

12

Jestem nowy w zastrzykach zależności i mam kilka pytań na temat tego, jaki styl powinienem zastosować w swoich aplikacjach. Właśnie przeczytałem Inversion of Control Containers i wzorzec Dependency Injection autorstwa Martina Fowlera, ale nie mogę dostrzec praktycznej różnicy między konstruktorem, seterą i iniekcją interfejsu.

Wydaje mi się, że powody używania jednego nad drugim to tylko kwestia czyszczenia kodu i / lub przejrzystości. Jaka jest różnica? Czy są jakieś zalety lub wady korzystania z jednego nad drugim, czy jest to właśnie to, co powiedziałem wcześniej?

Moim zdaniem wstrzyknięcie konstruktora jest najbardziej intuicyjne ze wszystkich, podobnie jak wstrzyknięcie interfejsu . Z drugiej strony wstrzykiwanie setera jest terminem średnim, ale czy powinieneś być w stanie zmienić instancję obiektu zależności, który początkowo wstrzyknąłeś? Czy ten styl wstrzykiwania gwarantuje, że obiekt, który potrzebuje zależności, zawsze będzie ją wstrzykiwał? Nie wierzę, ale proszę, popraw mnie, jeśli się mylę.

ecampver
źródło
Nie jestem pewien, co jeszcze znalazłeś lub przeczytałeś po drodze. Znalazłem podobne wątki tu i tutaj . Sam jestem nowy i byłbym ciekawy odpowiedzi, jakie możesz uzyskać :)
@ user1766760 powinieneś mi pomóc, a tutaj moje głosowanie zostanie zamknięte, dwa kolejne głosy i gotowe !!! Głosuj na pytanie lub coś takiego, nie wiem, co można zrobić, aby uniknąć zamknięcia.
ecampver
erm ... sam jestem nowy w SO, więc nie jestem zbyt pewien, jak mogę pomóc / dlaczego głosowanie jest zamykane (nie widzę żadnych oznak?). Jeśli odważę się zgadnąć, być może nie jest to konkretne pytanie programistyczne, ale raczej dyskusja?
Możesz nieco rozszerzyć pytanie. Wstrzykiwanie zależności jest jedną z form „Inwersji kontroli”. Istnieją alternatywy. Przydatną, ale dojrzałą alternatywą do nadużyć jest na przykład lokalizacja usługi.
Ian

Odpowiedzi:

12

Constructor Injection ma tę zaletę, że wyraźnie określa zależność i zmusza klienta do zapewnienia wystąpienia. Może także zagwarantować, że klient nie będzie mógł później zmienić instancji. Jednym (możliwym) minusem jest to, że musisz dodać parametr do swojego konstruktora.

Zaletą Setter Injection jest to, że nie wymaga dodawania parametru do konstruktora. Nie wymaga również od klienta ustawienia instancji. Jest to przydatne w przypadku zależności opcjonalnych. Może to być również przydatne, jeśli chcesz, aby klasa utworzyła domyślnie np. Prawdziwe repozytorium danych, a następnie w teście możesz użyć setera do zastąpienia go instancją testową.

Interfejs Iniekcji , o ile wiem, nie różni się niczym od wtrysku setera. W obu przypadkach (opcjonalnie) ustawia się zależność, którą można zmienić później.

Ostatecznie jest to kwestia preferencji i tego, czy wymagana jest zależność, czy nie . Osobiście używam zastrzyku konstruktora prawie wyłącznie. Podoba mi się, że wyraźnie określa zależności klasy, zmuszając klienta do zapewnienia wystąpienia w konstruktorze. Podoba mi się również to, że klient nie może zmienić instancji po fakcie.

Często moim jedynym powodem przekazania dwóch osobnych implementacji jest testowanie. W produkcji mogę zdać DataRepository, ale w testach zdałbym FakeDataRepository. W takim przypadku zwykle dostarczam dwa konstruktory: jeden bez parametrów, a drugi akceptujący IDataRepository. Następnie w konstruktorze bez parametrów wywołam połączenie z drugim konstruktorem i przekażę komendę new DataRepository().

Oto przykład w języku C #:


public class Foo
{
  private readonly IDataRepository dataRepository;

  public Foo() : this(new DataRepository())
  {
  }

  public Foo(IDataRespository dataRepository)
  {
    this.dataRepository = dataRepository;
  }
}

Jest to znane jako zastrzyk uzależnienia od ubogich. Podoba mi się, ponieważ w kodzie klienta produkcyjnego nie muszę się powtarzać, mając kilka powtarzających się instrukcji, które wyglądają

var foo = new Foo(new DataRepository());
Nadal jednak mogę przekazać alternatywną implementację do testowania. Zdaję sobie sprawę, że w DI Poor Man zapisuję na stałe swoją zależność, ale jest to dla mnie do zaakceptowania, ponieważ do testów używam głównie DI.

jhewlett
źródło
dzięki, to dość blisko tego, co rozumiem, ale czy jest jakiś scenariusz, w którym nie można użyć wstrzykiwania konstruktora ?
ecampver
1
Prawdopodobnie nie chcesz używać wstrzykiwania konstruktora dla opcjonalnych zależności. Nie mogę wymyślić żadnych innych powodów, aby z niego nie korzystać.
jhewlett
Używam iniekcji ustawiacza właściwości specjalnie do wypełnienia niektórych klas konfiguracji, które zawierają dużą liczbę wartości. Nie używam go nigdzie indziej. Zawsze wątpię w uzasadnienie parametrów opcjonalnych, ponieważ istnieje duża szansa, że ​​jest to naruszenie zasady pojedynczej odpowiedzialności. Oczywiście zasady mają być łamane, więc ...
Ian
@jhewlett - to fantastyczne, szukałem dobrej prostej techniki stubowania do testów jednostkowych, która nie skomplikuje mojego rozwiązania - i myślę, że to jest to :)
Rocklan
2

Różnice między wtryskiem konstruktora i setera są już odpowiednio opisane powyżej, więc nie będę ich dalej omawiać.

Wstrzykiwanie interfejsu jest bardziej zaawansowaną formą wstrzykiwania, która jest przydatna, ponieważ pozwala określić zależność w momencie jej użycia, a nie podczas inicjalizacji obiektu, który będzie go używał. Umożliwia to szereg przydatnych opcji:

  • Zależność można określić inaczej niż obiekt, do którego jest wstrzykiwany; na przykład można użyć wstrzykiwania interfejsu w celu zapewnienia obiektu, dla którego istnieje jeden na sesję użytkownika lub na wątek do globalnego singletonu. Za każdym razem, gdy obiekt potrzebuje zależności, wywoła metodę gettera udostępnianą przez środowisko, co może zwrócić różne wyniki w zależności od sytuacji, w której jest wywołane.

  • Pozwala na leniwą inicjalizację - nie ma potrzeby inicjowania zależności, dopóki nie zostanie ona użyta

  • Umożliwia ładowanie zależności z kopii w pamięci podręcznej, gdy istnieją, lub ponowne inicjowanie, gdy nie istnieją (np. Za pomocą SoftReferenceJava).

Oczywiście takie zaawansowane techniki mają swoje wady; w tym przypadku głównym problemem jest to, że kod staje się mniej przejrzysty (klasy używane w kodzie stają się abstrakcyjne i nie ma oczywistej konkretnej implementacji, co może być mylące, jeśli się do niego nie przyzwyczaisz) i stajesz się bardziej zależny na platformie wstrzykiwania zależności ( oczywiście nadal możliwe jest tworzenie instancji obiektów ręcznie, ale jest to trudniejsze niż w przypadku innych stylów wstrzykiwania).

Jules
źródło