Ostatnio przeczytałem artykuł Marka Seemanna na temat anty-wzorca Service Locator.
Autor wskazuje dwa główne powody, dla których ServiceLocator jest anty-wzorcem:
Problem z używaniem API (z którym jestem w porządku)
Gdy klasa korzysta z lokalizatora usług, bardzo trudno jest zobaczyć jego zależności, ponieważ w większości przypadków klasa ma tylko jeden konstruktor BEZ PARAMETRÓW. W przeciwieństwie do ServiceLocator, podejście DI jawnie uwidacznia zależności za pośrednictwem parametrów konstruktora, dzięki czemu zależności są łatwo widoczne w IntelliSense.Problem konserwacji (który mnie
zastanawia ) Rozważ następujący przykład
Mamy klasę „MyType”, która wykorzystuje podejście do lokalizatora usług:
public class MyType
{
public void MyMethod()
{
var dep1 = Locator.Resolve<IDep1>();
dep1.DoSomething();
}
}
Teraz chcemy dodać kolejną zależność do klasy „MyType”
public class MyType
{
public void MyMethod()
{
var dep1 = Locator.Resolve<IDep1>();
dep1.DoSomething();
// new dependency
var dep2 = Locator.Resolve<IDep2>();
dep2.DoSomething();
}
}
I tu zaczyna się moje nieporozumienie. Autor mówi:
O wiele trudniej jest stwierdzić, czy wprowadzasz przełomową zmianę, czy nie. Musisz zrozumieć całą aplikację, w której używany jest Lokalizator usług, a kompilator ci nie pomoże.
Ale poczekaj chwilę, gdybyśmy używali podejścia DI, wprowadzilibyśmy w konstruktorze zależność z innym parametrem (w przypadku iniekcji konstruktora). A problem nadal będzie istniał. Jeśli możemy zapomnieć o skonfigurowaniu ServiceLocator, możemy zapomnieć o dodaniu nowego mapowania w naszym kontenerze IoC, a podejście DI spowodowałoby ten sam problem w czasie wykonywania.
Autor wspomniał również o trudnościach z testami jednostkowymi. Ale czy nie będziemy mieli problemów z podejściem DI? Czy nie będziemy musieli aktualizować wszystkich testów, które tworzyły instancję tej klasy? Zaktualizujemy je tak, aby przekazywały nową, mockowaną zależność, aby nasz test był kompilowalny. I nie widzę żadnych korzyści z tej aktualizacji i spędzania czasu.
Nie próbuję bronić podejścia Service Locator. Ale to nieporozumienie sprawia, że myślę, że tracę coś bardzo ważnego. Czy ktoś mógłby rozwiać moje wątpliwości?
AKTUALIZACJA (PODSUMOWANIE):
Odpowiedź na moje pytanie „Czy Service Locator jest anty-wzorcem” naprawdę zależy od okoliczności. I zdecydowanie nie sugerowałbym wykreślenia go z listy narzędzi. Może się to okazać bardzo przydatne, gdy zaczniesz zajmować się starszym kodem. Jeśli masz szczęście być na samym początku projektu, podejście DI może być lepszym wyborem, ponieważ ma pewne zalety w porównaniu z lokalizatorem usług.
A oto główne różnice, które przekonały mnie do rezygnacji z usługi Service Locator w nowych projektach:
- Najbardziej oczywiste i najważniejsze: Service Locator ukrywa zależności klas
- Jeśli korzystasz z jakiegoś kontenera IoC, prawdopodobnie przeskanuje on wszystkie konstruktory podczas uruchamiania, aby sprawdzić wszystkie zależności i natychmiast przekaże Ci informacje o brakujących mapowaniach (lub nieprawidłowej konfiguracji); nie jest to możliwe, jeśli używasz kontenera IoC jako lokalizatora usług
Po szczegóły przeczytaj doskonałe odpowiedzi, które podano poniżej.
Odpowiedzi:
Jeśli zdefiniujesz wzorce jako anty-wzorce tylko dlatego, że są sytuacje, w których nie pasuje, to TAK jest to anty-wzorce. Ale przy takim rozumowaniu wszystkie wzorce byłyby również anty wzorcami.
Zamiast tego musimy sprawdzić, czy istnieją prawidłowe zastosowania wzorców, a dla Lokalizatora usług istnieje kilka przypadków użycia. Ale zacznijmy od przyjrzenia się przykładom, które podałeś.
Koszmar konserwacji tej klasy polega na tym, że zależności są ukryte. Jeśli tworzysz i używasz tej klasy:
Nie rozumiesz, że ma zależności, jeśli są one ukryte za pomocą lokalizacji usługi. Jeśli zamiast tego użyjemy zastrzyku zależności:
Możesz bezpośrednio dostrzec zależności i nie możesz użyć klas przed ich spełnieniem.
Z tego właśnie powodu w typowych zastosowaniach biznesowych należy unikać korzystania z lokalizacji usług. Powinien to być wzorzec do użycia, gdy nie ma innych opcji.
Czy wzór jest anty-wzorem?
Nie.
Na przykład odwrócenie kontenerów kontrolnych nie zadziałałoby bez lokalizacji usługi. W ten sposób wewnętrznie rozwiązują usługi.
Ale znacznie lepszym przykładem są ASP.NET MVC i WebApi. Jak myślisz, co umożliwia wstrzykiwanie zależności w kontrolerach? Zgadza się - lokalizacja serwisu.
Twoje pytania
Istnieją dwa poważniejsze problemy:
Dzięki iniekcji konstruktora przy użyciu kontenera otrzymujesz to za darmo.
To prawda. Ale dzięki iniekcji konstruktora nie musisz skanować całej klasy, aby dowiedzieć się, których zależności brakuje.
Niektóre lepsze kontenery również sprawdzają wszystkie zależności podczas uruchamiania (skanując wszystkie konstruktory). Tak więc w przypadku tych kontenerów błąd wykonania pojawia się bezpośrednio, a nie w jakimś późniejszym momencie.
Nie. Ponieważ nie masz zależności od statycznego lokalizatora usług. Czy próbowałeś uzyskać testy równoległe działające z zależnościami statycznymi? To nie jest zabawne.
źródło
Chciałbym również zwrócić uwagę, że JEŚLI refaktoryzujesz stary kod, to wzorzec Service Locator nie tylko nie jest anty-wzorcem, ale jest również praktyczną koniecznością. Nikt nigdy nie machnie magiczną różdżką nad milionami linii kodu i nagle cały ten kod będzie gotowy do DI. Więc jeśli chcesz rozpocząć wprowadzanie DI do istniejącej bazy kodu, często jest tak, że będziesz powoli zmieniać rzeczy, aby stały się usługami DI, a kod, który odwołuje się do tych usług, często NIE będzie usługami DI. Dlatego TE usługi będą musiały korzystać z Lokalizatora usług, aby uzyskać wystąpienia tych usług, które ZOSTAŁY przekonwertowane do korzystania z DI.
Dlatego podczas refaktoryzacji dużych starszych aplikacji, aby zaczęły używać koncepcji DI, powiedziałbym, że nie tylko Service Locator NIE jest anty-wzorcem, ale jest to jedyny sposób na stopniowe stosowanie koncepcji DI w bazie kodu.
źródło
Z punktu widzenia testów, Service Locator jest zły. Zobacz ładne wyjaśnienie Misko Hevery w Google Tech Talk z przykładami kodu http://youtu.be/RlfLCWKxHJ0 od minuty 8:45. Podobała mi się jego analogia: jeśli potrzebujesz 25 dolarów, poproś bezpośrednio o pieniądze, zamiast oddawać portfel, skąd zostaną pobrane pieniądze. Porównuje również Service Locator ze stogiem siana, który ma potrzebną igłę i wie, jak ją odzyskać. Z tego powodu zajęcia korzystające z Lokalizatora usług są trudne do ponownego wykorzystania.
źródło
Istnieją 2 różne powody, dla których korzystanie z lokalizatora usług jest pod tym względem złe.
Jasne i proste: klasa z lokalizatorem usług jest trudniejsza do ponownego użycia niż klasa, która akceptuje jej zależności za pośrednictwem swojego konstruktora.
źródło
Moja wiedza nie jest wystarczająco dobra, aby to ocenić, ale generalnie myślę, że jeśli coś ma zastosowanie w określonej sytuacji, niekoniecznie oznacza to, że nie może być anty-wzorcem. Szczególnie, gdy masz do czynienia z bibliotekami innych firm, nie masz pełnej kontroli nad wszystkimi aspektami i możesz skończyć z użyciem niezbyt najlepszego rozwiązania.
Oto akapit z Adaptive Code via C # :
źródło
Autor argumentuje, że „kompilator ci nie pomoże” - i to prawda. Kiedy raczysz klasą, będziesz chciał starannie wybrać jej interfejs - między innymi, aby uczynić ją tak niezależną, jak ... na ile ma to sens.
Gdy klient akceptuje odwołanie do usługi (do zależności) za pośrednictwem jawnego interfejsu, Ty
Masz rację, że DI ma swoje problemy / wady, ale wymienione zalety zdecydowanie je przeważają ... IMO. Masz rację, że z DI istnieje zależność wprowadzona w interfejsie (konstruktorze) - ale mam nadzieję, że jest to właśnie ta zależność, której potrzebujesz i którą chcesz, aby była widoczna i możliwa do sprawdzenia.
źródło
Tak, lokalizator usług jest anty-wzorcem, narusza hermetyzację i jest solidny .
źródło