Oba wzorce wydają się implementacją zasady inwersji kontroli. Oznacza to, że obiekt nie powinien wiedzieć, jak skonstruować swoje zależności.
Dependency Injection (DI) wydaje się używać konstruktora lub setera do „wstrzykiwania” swoich zależności.
Przykład użycia wtrysku konstruktora:
//Foo Needs an IBar
public class Foo
{
private IBar bar;
public Foo(IBar bar)
{
this.bar = bar;
}
//...
}
Lokalizator usług wydaje się używać „kontenera”, który łączy jego zależności i daje mu pasek.
Przykład użycia lokalizatora usług:
//Foo Needs an IBar
public class Foo
{
private IBar bar;
public Foo()
{
this.bar = Container.Get<IBar>();
}
//...
}
Ponieważ nasze zależności są same obiektami, zależności te mają zależności, które mają jeszcze więcej zależności, i tak dalej. Tak narodziła się inwersja kontenera kontrolnego (lub kontenera DI). Przykłady: Zamek Windsor, Ninject, Mapa struktury, Wiosna itp.)
Ale kontener IOC / DI wygląda dokładnie jak lokalizator usług. Czy nazywanie go kontenerem DI to zła nazwa? Czy kontener IOC / DI to kolejny rodzaj lokalizatora usług? Czy niuans polega na tym, że używamy kontenerów DI głównie wtedy, gdy mamy wiele zależności?
źródło
Odpowiedzi:
Różnica może wydawać się niewielka, ale nawet w przypadku ServiceLocator klasa nadal jest odpowiedzialna za tworzenie swoich zależności. Po prostu używa do tego lokalizatora usług. Dzięki DI klasa ma swoje zależności. Nie wie ani nie dba o to, skąd pochodzą. Jednym z ważnych rezultatów jest to, że przykład DI jest znacznie łatwiejszy do testowania jednostkowego - ponieważ można mu przekazać fałszywe implementacje jego zależnych obiektów. Możesz połączyć oba - i wstrzyknąć lokalizator usług (lub fabrykę), jeśli chcesz.
źródło
Podczas korzystania z lokalizatora usług każda klasa będzie zależna od tego lokalizatora usług. Nie jest tak w przypadku wstrzykiwania zależności. Wtryskiwacz zależności będzie zwykle wywoływany tylko raz podczas uruchamiania, aby wstrzyknąć zależności do niektórych głównych klas. Klasy, od których zależy ta główna klasa, będą rekurencyjnie wprowadzane do swoich zależności, aż do uzyskania pełnego wykresu obiektowego.
Dobre porównanie: http://martinfowler.com/articles/injection.html
Jeśli twój aplikator zależności wygląda jak lokalizator usług, gdzie klasy wywołują go bezpośrednio, prawdopodobnie nie jest to aplikator zależności, ale raczej lokalizator usług.
źródło
Lokalizatory usług ukrywają zależności - nie można stwierdzić, patrząc na obiekt, czy trafi on do bazy danych, czy nie (na przykład), gdy uzyskuje połączenia z lokalizatora. W przypadku wstrzykiwania zależności (przynajmniej wstrzykiwania przez konstruktora) zależności są jawne.
Ponadto lokalizatory usług przerywają enkapsulację, ponieważ zapewniają globalny punkt dostępu do zależności innych obiektów. Z lokalizatorem usług, jak w przypadku każdego singletona :
W przypadku wstrzykiwania zależności po określeniu zależności obiektu są one kontrolowane przez sam obiekt.
źródło
With dependency injection (at least constructor injection) the dependencies are explicit.
. Proszę wytłumacz.Martin Fowler stwierdza :
W skrócie: Service Locator i Dependency Injection to tylko implementacje zasady Dependency Inversion.
Ważną zasadą jest „Zależnie od abstrakcji, a nie od konkrecji”. Dzięki temu Twój projekt oprogramowania będzie „luźno sprzężony”, „rozszerzalny”, „elastyczny”.
Możesz użyć tego, który najlepiej odpowiada Twoim potrzebom. W przypadku dużej aplikacji, mającej dużą bazę kodów, lepiej użyć Lokalizatora usług, ponieważ Wstrzyknięcie zależności wymagałoby więcej zmian w bazie kodu.
Możesz sprawdzić ten post: Odwrócenie zależności: Lokalizator usług lub Wstrzyknięcie zależności
Również klasyczny: Inwersja kontenerów kontrolnych i wzór wtrysku zależności według Martina Fowlera
Projektowanie klas wielokrotnego użytku przez Ralpha E. Johnsona i Briana Foote'a
Jednak ten, który otworzył mi oczy to: ASP.NET MVC: Resolve czy Inject? To jest problem… autorstwa Dino Esposito
źródło
Klasa używająca konstruktora DI wskazuje na konsumpcję kodu, że istnieją zależności, które należy spełnić. Jeśli klasa korzysta z SL wewnętrznie do pobierania takich zależności, konsumujący kod nie jest świadomy zależności. Na pozór może się to wydawać lepsze, ale tak naprawdę pomocne jest poznanie wyraźnych zależności. Lepiej jest z architektonicznego punktu widzenia. Podczas przeprowadzania testów musisz wiedzieć, czy klasa potrzebuje pewnych zależności, i skonfiguruj SL, aby zapewnić odpowiednie fałszywe wersje tych zależności. Z DI, po prostu podaj podróbki. Nie jest to wielka różnica, ale ona istnieje.
DI i SL mogą jednak współpracować. Przydatne jest posiadanie centralnej lokalizacji dla typowych zależności (np. Ustawienia, rejestrator itp.). Biorąc pod uwagę klasę używającą takich deps, możesz stworzyć „prawdziwy” konstruktor, który odbiera deps, oraz domyślny konstruktor (bez parametru), który pobiera z SL i przekazuje do „prawdziwego” konstruktora.
EDYCJA: i oczywiście, kiedy używasz SL, wprowadzasz pewne sprzężenie z tym komponentem. Co jest ironiczne, ponieważ ideą takiej funkcjonalności jest pobudzanie abstrakcji i ograniczanie sprzężenia. Obawy można zrównoważyć i zależy to od tego, ile miejsc trzeba by użyć SL. Jeśli zrobiono to jak sugerowano powyżej, tylko w domyślnym konstruktorze klas.
źródło
Obie są technikami wdrażania IoC. Istnieją również inne wzorce implementujące Inversion of Control:
Lokalizator usług i kontener DI wydają się bardziej podobne, oba używają kontenera do definiowania zależności, które odwzorowują abstrakcję na konkretną implementację.
Główną różnicą jest sposób lokalizowania zależności, w Lokalizatorze usług kod klienta żąda zależności, w kontenerze DI używamy kontenera do tworzenia wszystkich obiektów i wstrzykuje on zależność jako parametry konstruktora (lub właściwości).
źródło
W moim ostatnim projekcie korzystam z obu. Używam wstrzykiwania zależności do testowania jednostkowego. Używam lokalizatora usług do ukrywania implementacji i zależności od mojego kontenera IoC. i tak! Gdy użyjesz jednego z kontenerów IoC (Unity, Ninject, Windsor Castle), będziesz na nim polegać. A kiedy będzie przestarzały lub z jakiegoś powodu, jeśli będziesz chciał go zamienić, będziesz musiał / powinna zmienić swoją implementację - przynajmniej root root. Ale lokalizator usług streszcza tę fazę.
Jak nie byłbyś zależny od swojego kontenera IoC? Albo będziesz musiał go owinąć (co jest złym pomysłem), albo użyjesz Service Locator, skonfiguruj swój kontener IoC. Powiesz więc lokalizatorowi usług, aby uzyskał potrzebny interfejs, i zadzwoni on do kontenera IoC skonfigurowanego do pobierania tego interfejsu.
W moim przypadku używam ServiceLocator, który jest składnikiem frameworka. I użyj Unity dla kontenera IoC. Jeśli w przyszłości będę musiał zamienić mój kontener IoC na Ninject muszę jedynie skonfigurować lokalizator usług, aby używał Ninject zamiast Unity. Łatwa migracja.
Oto świetny artykuł wyjaśniający ten scenariusz; http://www.johandekoning.nl/index.php/2013/03/03/dont-wrap-your-ioc-container/
źródło
Myślę, że oboje pracują razem.
Wstrzykiwanie zależności oznacza, że wpychasz jakąś zależną klasę / interfejs do klasy konsumującej (zwykle do jej konstruktora). To oddziela dwie klasy za pomocą interfejsu i oznacza, że klasa konsumująca może pracować z wieloma typami implementacji „wstrzykiwanej zależności”.
Rolą lokalizatora usług jest połączenie implementacji. Lokalizator usług konfiguruje się przez wiązanie rozruchowe na początku programu. Bootstrapping to proces kojarzenia typu implementacji z określonym abstraktem / interfejsem. Który zostanie stworzony dla Ciebie w czasie wykonywania. (na podstawie konfiguracji lub bootstrapu). Jeśli nie wdrożono wstrzykiwania zależności, bardzo trudno byłoby użyć lokalizatora usług lub kontenera IOC.
źródło
Jeden powód do dodania, zainspirowany aktualizacją dokumentacji, którą napisaliśmy dla projektu MEF w zeszłym tygodniu (pomagam budować MEF).
Gdy aplikacja składa się z potencjalnie tysięcy komponentów, ustalenie, czy jakikolwiek konkretny komponent może być poprawnie utworzony, może być trudne. Przez „poprawnie
Foo
utworzoną instancję ” rozumiem, że w tym przykładzie opartym na komponencie instancjaIBar
i będzie dostępna, a dostarczający ją komponent:W drugim podanym przez ciebie przykładzie, w którym konstruktor idzie do kontenera IoC w celu odzyskania swoich zależności, jedynym sposobem na sprawdzenie, czy wystąpienie
Foo
będzie mogło zostać poprawnie utworzone z rzeczywistą konfiguracją środowiska wykonawczego aplikacji, to faktyczne skonstruowanie to .Ma to różnego rodzaju niezręczne skutki uboczne w czasie testu, ponieważ kod, który będzie działał w czasie wykonywania, niekoniecznie będzie działał pod wiązką testową. Próbki się nie sprawdzą, ponieważ musimy przetestować prawdziwą konfigurację, a nie konfigurację w czasie testowym.
Źródłem tego problemu jest różnica już przywołana przez @Jon: wstrzykiwanie zależności przez konstruktor jest deklaratywne, podczas gdy druga wersja używa imperatywnego wzorca Service Locator.
Kontener IoC, jeśli jest używany ostrożnie, może statycznie analizować konfigurację środowiska wykonawczego aplikacji bez faktycznego tworzenia żadnych instancji zaangażowanych komponentów. Wiele popularnych kontenerów zapewnia pewne odmiany tego; Microsoft.Composition , która jest wersją MEF skierowaną do aplikacji internetowych i Metro w stylu .NET 4.5, zapewnia
CompositionAssert
przykład w dokumentacji wiki. Używając go, możesz napisać kod taki jak:(Zobacz ten przykład ).
Weryfikując korzenie kompozycji aplikacji w czasie testu, możesz potencjalnie wykryć niektóre błędy, które w przeciwnym razie mogą prześlizgnąć się przez testy w późniejszym etapie procesu.
Mam nadzieję, że jest to interesujący dodatek do tego obszernego zestawu odpowiedzi na ten temat!
źródło
Uwaga: nie odpowiadam dokładnie na pytanie. Ale wydaje mi się, że może to być przydatne dla nowych osób uczących się wzoru wstrzykiwania zależności, które są mylone z tym wzorcem lokalizatora usług (anty-), które przypadkiem natkną się na tę stronę.
Znam różnicę między lokalizatorem usług (wydaje się, że jest to teraz uważane za anty-wzorzec) a wzorcami wstrzykiwania zależności i potrafię zrozumieć konkretne przykłady każdego wzorca, ale myliłem się z przykładami pokazującymi lokalizator usług wewnątrz konstruktora (zakładamy, że „ ponownie wykonuje iniekcję konstruktora).
„Lokalizator usług” jest często używany zarówno jako nazwa wzorca, jak i jako nazwa odnosząca się do obiektu (również załóżmy) użytego w tym wzorcu w celu uzyskania obiektów bez użycia nowego operatora. Teraz ten sam typ obiektu może być również używany w katalogu głównym kompozycji do wykonania wstrzyknięcia zależności, i właśnie tam pojawia się zamieszanie.
Chodzi o to, że możesz używać obiektu lokalizatora usług wewnątrz konstruktora DI, ale nie używasz „wzorca lokalizatora usług”. To mniej mylące, jeśli ktoś odniesie go jako obiekt kontenerowy IoC, jak można się domyślać, że zasadniczo robią to samo (popraw mnie, jeśli się mylę).
Niezależnie od tego, czy jest to określane jako lokalizator usług (lub po prostu lokalizator), czy jako kontener IoC (lub tylko kontener), jak się domyślacie, ponieważ prawdopodobnie odnoszą się do tej samej abstrakcji (poprawcie mnie, jeśli się mylę) ). Po prostu nazwanie go lokalizatorem usług sugeruje, że używa się anty-wzorca Service Locator wraz ze wzorem Dependency Injection.
IMHO, nazywając go „lokalizatorem” zamiast „lokalizacji” lub „lokalizacji”, może czasem powodować, że ktoś myśli, że lokalizator usług w artykule odnosi się do kontenera Lokalizatora usług, a nie wzorca usługi lokalizatora (anty) , szczególnie gdy istnieje podobny wzorzec o nazwie Dependency Injection, a nie Dependency Injector.
źródło
W tym uproszczonym przypadku nie ma różnicy i można ich używać zamiennie. Jednak rzeczywiste problemy nie są tak proste. Załóżmy, że sama klasa Bar miała inną zależność o nazwie D. W takim przypadku lokalizator usług nie byłby w stanie rozwiązać tej zależności i musiałbyś utworzyć ją w klasie D; ponieważ obowiązkiem twoich klas jest tworzenie ich zależności. Byłoby jeszcze gorzej, gdyby sama klasa D miała inne zależności, aw rzeczywistych sytuacjach zwykle staje się jeszcze bardziej skomplikowana. W takich scenariuszach DI jest lepszym rozwiązaniem niż ServiceLocator.
źródło
bar
sama klasa ma zależność,bar
będzie miała również lokalizator usług, o to właśnie chodzi w korzystaniu z DI / IoC.Jaka jest różnica (jeśli występuje) między wstrzykiwaniem zależności a lokalizatorem usług? Oba wzorce są dobre we wdrażaniu zasady odwrócenia zależności. Wzorzec Lokalizatora usług jest łatwiejszy w użyciu w istniejącej bazie kodu, ponieważ sprawia, że ogólny projekt jest luźniejszy bez wymuszania zmian w interfejsie publicznym. Z tego samego powodu kod oparty na wzorcu lokalizatora usług jest mniej czytelny niż kod równoważny oparty na wstrzykiwaniu zależności.
Wzorzec wstrzykiwania zależności wyjaśnia, ponieważ sygnatura, jakie zależności będzie miała klasa (lub metoda). Z tego powodu wynikowy kod jest czystszy i bardziej czytelny.
źródło
Poniższa prosta koncepcja pozwoliła mi lepiej zrozumieć różnicę między lokalizatorem usług a kontenerem DI:
Lokalizator usług jest używany przez konsumenta i pobiera usługi według identyfikatora z niektórych pamięci na bezpośrednie żądanie konsumenta
DI Container znajduje się gdzieś na zewnątrz i pobiera usługi z niektórych magazynów i przekazuje je konsumentowi (bez względu na konstruktora lub metodę)
Możemy jednak mówić o różnicy między nimi tylko w kontekście konkretnego użycia przez konsumenta. Gdy Service Locator i DI Container są używane w katalogu głównym kompozycji, są one prawie podobne.
źródło
Kontener DI jest nadzbiorem lokalizatora usług. Można go użyć do zlokalizowania usługi , z dodatkową możliwością montażu (okablowania) zastrzyków zależności .
źródło
Dla przypomnienia
O ile naprawdę nie potrzebujesz interfejsu (interfejs jest używany przez więcej niż jedną klasę), NIE WOLNO Z niego korzystać . W takim przypadku IBar pozwala na użycie dowolnej klasy usługi, która ją implementuje. Jednak zwykle ten interfejs będzie używany przez jedną klasę.
Dlaczego używanie interfejsu jest złym pomysłem ?. Ponieważ naprawdę trudno jest debugować.
Na przykład, powiedzmy, że instancja „bar” nie powiodła się, pytanie: która klasa zawiodła ?. Który kod powinienem naprawić? Prosty widok, prowadzi do interfejsu i tu kończy się moja droga.
Zamiast tego, jeśli kod wykorzystuje twardą zależność, łatwo jest debugować błąd.
Jeśli „bar” zawiedzie, powinienem sprawdzić i uruchomić klasę BarService.
źródło
contract
i po prostu definiuje zachowanie, a nie akcję. Zamiast przekazywać rzeczywisty obiekt, tylko interfejs jest współdzielony, dzięki czemu konsument nie ma dostępu do reszty twojego obiektu. Również w przypadku testów jednostkowych pomaga przetestować tylko część, która wymaga przetestowania. Myślę, że z czasem zrozumiesz jego przydatność.