Jaka jest różnica między wzorcami wstrzykiwania zależności a lokalizatorem usług?


304

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?


13
Odwrócenie kontroli oznacza, że ​​„obiekt nie powinien wiedzieć, jak skonstruować swoje zależności”?!? Ten jest dla mnie nowy. Nie, naprawdę nie to oznacza „odwrócenie kontroli”. Zobacz martinfowler.com/bliki/InversionOfControl.html Ten artykuł zawiera nawet odniesienia do etymologii tego terminu, sięgającego lat osiemdziesiątych.
Rogério


1
Mark Seemann uważa Service Locator za anty-wzorzec ( blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern ). Uważam też, że schemat (znajdujący się tutaj, stackoverflow.com/a/9503612/1977871 ) jest pomocny w zrozumieniu kłopotliwego położenia DI i SL. Mam nadzieję że to pomoże.
VivekDev,

Odpowiedzi:


181

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.


20
Dodatkowo możesz użyć obu tych elementów podczas tworzenia klasy. Domyślny konstruktor może użyć SL do pobrania zależności i przekazania ich do „prawdziwego” konstruktora, który odbiera te zależności. Dostajesz to, co najlepsze z obu światów.
Grant Palin

6
Nie, ServiceLocator jest odpowiedzialny za utworzenie prawidłowej implementacji dla danej zależności (wtyczki). W przypadku DI odpowiedzialny jest za to „pojemnik” DI.
Rogério

5
@Rogerio tak, ale klasa wciąż musi wiedzieć o Lokalizatorze usług ... to dwie zależności. Częściej też nie widziałem Lokalizatora usług delegowanego do kontenera DI w celu wyszukiwania, szczególnie w przypadku obiektów przejściowych, które wymagają obsługi serwisowej.
Adam Gent

2
@Adam Nie powiedziałem, że Lokalizator usług deleguje do kontenera DI. Są to dwa wzajemnie wykluczające się wzorce, jak opisano w „oficjalnym” artykule . Dla mnie Lokalizator usług ma ogromną przewagę nad DI w praktyce: użycie kontenera DI zachęca do nadużyć (które wielokrotnie widziałem), podczas gdy korzystanie z Lokalizatora usług nie.
Rogério,

3
„Jednym z ważnych rezultatów jest to, że przykład DI jest o wiele łatwiejszy do testowania jednostkowego - ponieważ można mu przekazać fałszywe implementacje jego zależnych obiektów”. Nie prawda. W testach jednostkowych można łatwo wywołać funkcję rejestru w rejestrze w wywołaniu funkcji rejestru w kontenerze lokalizatora usług.
Drumbeg

93

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.


17
Ale jak sobie poradzić z przypadkiem, w którym musisz tworzyć obiekty w czasie wykonywania? Jeśli utworzysz je ręcznie za pomocą „nowego”, nie będziesz mógł użyć DI. Jeśli zadzwonisz do frameworka DI w celu uzyskania pomocy, przełamiesz wzorzec. Więc jakie opcje pozostały?
Boris

9
@ Boris Miałem ten sam problem i postanowiłem wstrzyknąć fabryki specyficzne dla klasy. Nie ładna, ale wykonała pracę. Bardzo chciałbym zobaczyć ładniejsze rozwiązanie.
Charlie Rudenstål

Bezpośredni link do porównania: martinfowler.com/articles/…
Teoman shipahi

2
@ Boris Gdybym musiał konstruować nowe obiekty w locie, wstrzyknąłbym Fabrykę Abstrakcyjną dla tych obiektów. Który byłby podobny do wstrzykiwania lokalizatora usług w tym przypadku, ale zapewnia konkretny, jednolity, czas kompilacji interfejs do budowania odpowiednich obiektów i wyraźnie określa zależności.
LivePastTheEnd

51

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 :

określenie warunków wstępnych i końcowych interfejsu obiektu klienta staje się trudne, ponieważ działanie jego implementacji może być wtrącane z zewnątrz.

W przypadku wstrzykiwania zależności po określeniu zależności obiektu są one kontrolowane przez sam obiekt.


3
Wolę „Singletons Consugs
Charles Graham

2
Uwielbiam starego Steve'a Yegge, a tytuł tego artykułu jest świetny, ale myślę, że cytowany artykuł i „Singletony są patologicznymi kłamcami” Miško Hevery'ego ( misko.hevery.com/2008/08/08/17/singletons-are-pathological- kłamcy ) lepiej argumentują przeciwko konkretnemu diabłu lokalizatorowi służby.
Jeff Sternal

Ta odpowiedź jest najbardziej poprawna, ponieważ najlepiej definiuje lokalizator usług: „Klasa, która ukrywa swoje zależności”. Zauważ, że wewnętrzne tworzenie zależności, choć często nie jest dobrą rzeczą, nie czyni klasy lokalizatorem usług. Ponadto uzależnienie od kontenera stanowi problem, ale nie „problem”, który najdokładniej definiuje lokalizator usług.
Sam

1
With dependency injection (at least constructor injection) the dependencies are explicit.. Proszę wytłumacz.
FindOutIslamNow

Jak wyżej, nie widzę, jak SL sprawia, że ​​zależności są mniej wyraźne niż DI ...
Michał Powłoka

38

Martin Fowler stwierdza :

W przypadku lokalizatora usług klasa aplikacji wyraźnie o to prosi w komunikacie do lokalizatora. Po wstrzyknięciu nie ma wyraźnego żądania, usługa pojawia się w klasie aplikacji - stąd odwrócenie kontroli.

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


Fantastyczne podsumowanie: „Lokalizator usług i wstrzykiwanie zależności są tylko implementacjami zasady odwrócenia zależności”.
Hans

I stwierdza także: Kluczowa różnica polega na tym, że w przypadku Lokalizatora usług każdy użytkownik usługi jest zależny od lokalizatora. Lokalizator może ukryć zależności od innych implementacji, ale musisz go zobaczyć. Zatem decyzja między lokalizatorem a wtryskiwaczem zależy od tego, czy ta zależność stanowi problem.
programaths

1
ServiceLocator i DI nie mają nic wspólnego z „Zasadą inwersji zależności” (DIP). DIP jest sposobem na zwiększenie możliwości ponownego użycia komponentu wysokiego poziomu poprzez zastąpienie zależności czasu kompilacji komponentu niskiego poziomu zależnością od typu abstrakcyjnego zdefiniowanego wraz ze składnikiem wysokiego poziomu, który jest implementowany przez komponent niskiego poziomu składnik poziomu; w ten sposób zależność czasu kompilacji zostaje odwrócona, ponieważ teraz jest to poziom niskiego poziomu, który zależy od poziomu wysokiego. Zauważ też, że artykuł Martina Fowlera wyjaśnia, że ​​DI i IoC to nie to samo.
Rogério,

23

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.


Ciekawy! Używam zarówno DI, jak i SL, ale nie z dwoma konstruktorami. Trzy lub cztery najbardziej nudne, często potrzebne zależności (ustawienia itp.) Są uzyskiwane z SL w locie. Wszystko inne zostaje wstrzyknięte przez konstruktora. To trochę brzydkie, ale pragmatyczne.
maaartinus,

10

Obie są technikami wdrażania IoC. Istnieją również inne wzorce implementujące Inversion of Control:

  • Wzór fabryczny
  • Lokalizator usług
  • Pojemnik DI (IoC)
  • Wstrzykiwanie zależności (wstrzykiwanie konstruktora, wstrzykiwanie parametrów (jeśli nie jest wymagane), wstrzykiwanie interfejsu przez wstrzykiwanie interfejsu) ...

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


7

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/


Link do artykułu johandekoning jest zepsuty.
JakeJ

6

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.


6

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 Fooutworzoną instancję ” rozumiem, że w tym przykładzie opartym na komponencie instancja IBari będzie dostępna, a dostarczający ją komponent:

  • mieć wymagane zależności,
  • nie brać udziału w żadnych nieprawidłowych cyklach zależności, oraz
  • w przypadku MEF należy dostarczyć tylko jedną instancję.

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 Foobę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 CompositionAssertprzykład w dokumentacji wiki. Używając go, możesz napisać kod taki jak:

 // Whatever you use at runtime to configure the container
var container = CreateContainer();

CompositionAssert.CanExportSingle<Foo>(container);

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


5

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.


4

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.


4
Hmm Nie zgadzam się: lokalizator usług ex. wyraźnie pokazuje, że nadal istnieje tam zależność ... lokalizator usług. Jeśli barsama klasa ma zależność, barbędzie miała również lokalizator usług, o to właśnie chodzi w korzystaniu z DI / IoC.
GFoley83

2

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.


1

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.


0

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 .


-2

Dla przypomnienia

//Foo Needs an IBar
public class Foo
{
  private IBar bar;

  public Foo(IBar bar)
  {
    this.bar = bar;
  }

  //...
}

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.

//Foo Needs an IBar
public class Foo
{
  private BarService bar;

  public Foo(IBar bar)
  {
    this.bar = bar;
  }

  //...
}

Jeśli „bar” zawiedzie, powinienem sprawdzić i uruchomić klasę BarService.


1
Klasa to plan budowy określonego obiektu. Z drugiej strony interfejs to contracti 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ść.
Gunhan
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.