Zastanawiałem się, jak zrównoważyć projekt możliwy do przetestowania za pomocą wstrzykiwania zależności, zapewniając prosty stały publiczny interfejs API. Mój dylemat brzmi: ludzie chcieliby zrobić coś podobnego var server = new Server(){ ... }
i nie musieliby się martwić tworzeniem wielu zależności i wykresu zależności, które Server(,,,,,,)
mogą mieć. Podczas programowania nie martwię się zbytnio, ponieważ używam frameworka IoC / DI do obsługi tego wszystkiego (nie używam aspektów zarządzania cyklem życia żadnego kontenera, co jeszcze bardziej skomplikowałoby sprawę).
Teraz jest mało prawdopodobne, aby zależności zostały ponownie zaimplementowane. Składanie w tym przypadku jest prawie wyłącznie w celu przetestowania (i przyzwoitego projektu!), A nie tworzenia szwów dla rozszerzenia itp. Ludzie będą chcieli przez 99,999% czasu korzystać z domyślnej konfiguracji. Więc. Mógłbym zakodować zależności na stałe. Nie chcę tego robić, tracimy nasze testy! Mógłbym zapewnić domyślny konstruktor z zakodowanymi na stałe zależnościami i taki, który przyjmuje zależności. To ... niechlujny i prawdopodobnie wprowadzający w błąd, ale opłacalny. Mógłbym sprawić, by konstruktor odbierający zależność był wewnętrzny, a moja jednostka przetestowałaby zestaw znajomych (zakładając C #), który porządkuje publiczny interfejs API, ale pozostawia nieprzyjemną ukrytą pułapkę czającą się w celu konserwacji. Posiadanie dwóch konstruktorów, które są domyślnie połączone, a nie jawne, byłoby ogólnie złym projektem w mojej książce.
W tej chwili jest to najmniejsze zło, jakie mogę wymyślić. Opinie? Mądrość?
HttpListener
konstruktor się zmieni,Server
klasa również musi się zmienić. Są sprzężone. Lepszym rozwiązaniem jest to, co mówi poniżej @Winston Ewert, który ma zapewnić domyślną klasę z predefiniowanymi zależnościami poza interfejsem API biblioteki. Następnie programista nie jest zmuszony do konfigurowania wszystkich zależności, ponieważ podejmujesz za niego decyzję, ale nadal ma elastyczność, aby je zmienić później. To wielka sprawa, gdy masz zależności od stron trzecich.W Javie w takich przypadkach zwykle konfiguracja „domyślna” jest budowana przez fabrykę, utrzymywana niezależnie od rzeczywistego „prawidłowo” zachowującego się serwera. Twój użytkownik pisze:
podczas gdy
Server
konstruktor nadal akceptuje wszystkie swoje zależności i może być używany do głębokiego dostosowywania.źródło
Co powiesz na zapewnienie podklasy, która zapewnia „publiczny interfejs API”
Użytkownik może
new StandardServer()
i jest w drodze. Mogą również użyć podstawowej klasy Serwer, jeśli chcą mieć większą kontrolę nad tym, jak działa serwer. To mniej więcej takie podejście stosuję. (Nie używam frameworka, ponieważ nie widziałem jeszcze sensu.)To wciąż ujawnia wewnętrzny interfejs API, ale myślę, że powinieneś. Podzieliłeś swoje obiekty na różne przydatne komponenty, które powinny działać niezależnie. Nie wiadomo, kiedy strona trzecia może chcieć użyć jednego z komponentów w izolacji.
źródło
Nie wydaje mi się to „paskudną ukrytą pułapką”. Konstruktor publiczny powinien po prostu wywoływać konstruktor wewnętrzny z zależnościami „domyślnymi”. Dopóki jest jasne, że konstruktor publiczny nie powinien być modyfikowany, wszystko powinno być w porządku.
źródło
Całkowicie zgadzam się z twoją opinią. Nie chcemy zanieczyszczać granicy użycia komponentów tylko w celu testowania jednostkowego. To jest cały problem rozwiązania testowego opartego na DI.
Sugerowałbym użycie wzorca InjectableFactory / InjectableInstance . InjectableInstance to proste klasy narzędzi wielokrotnego użytku, które są zmiennymi posiadaczami wartości . Interfejs komponentu zawiera odwołanie do tego uchwytu wartości, który został zainicjowany przy użyciu domyślnej implementacji. Interfejs dostarczy metodę singletonową get (), która deleguje do posiadacza wartości. Klient komponentu wywoła metodę get () zamiast nowej, aby uzyskać instancję implementacji w celu uniknięcia bezpośredniej zależności. Podczas testowania możemy opcjonalnie zastąpić domyślną implementację próbną. Poniżej wykorzystam jeden przykład TimeProviderklasa, która jest abstrakcją Date (), więc pozwala testom jednostkowym wstrzykiwać i wyśmiewać, aby symulować inny moment. Przepraszam, użyję java tutaj, ponieważ jestem bardziej zaznajomiony z java. C # powinien być podobny.
InjectableInstance jest dość łatwy do wdrożenia, mam implementację referencyjną w Javie. Aby uzyskać szczegółowe informacje, zapoznaj się z moim postem na blogu Pośrednictwo zależności od fabryki wstrzykiwanej
źródło