Buduję aplikację WPF przy użyciu wzorca MVVM. W tej chwili moje viewmodels wywołuje warstwę usługi, aby pobrać modele (co nie ma znaczenia dla viewmodel) i przekonwertować je na viewmodels. Używam iniekcji konstruktora, aby przekazać wymaganą usługę do modelu viewmodel.
Jest łatwy do przetestowania i działa dobrze w modelach view z kilkoma zależnościami, ale gdy tylko próbuję utworzyć viewModels dla złożonych modeli, mam konstruktora z dużą ilością usług wstrzykniętych do niego (jedna do pobierania każdej zależności i listy wszystkich dostępnych wartości powiązanie na przykład z itemSource). Zastanawiam się, jak obsługiwać wiele takich usług i nadal mam model widoku, który mogę z łatwością testować jednostkowo.
Mam na myśli kilka rozwiązań:
Tworzenie singletonu usług (IServices) zawierającego wszystkie dostępne usługi jako interfejsy. Przykład: Services.Current.XXXService.Retrieve (), Services.Current.YYYService.Retrieve (). W ten sposób nie mam wielkiego konstruktora z mnóstwem parametrów usług.
Tworzenie fasady dla usług używanych przez viewModel i przekazywanie tego obiektu w ctor mojego viewmodela. Ale potem będę musiał stworzyć fasadę dla każdego z moich złożonych modeli widoków i może to być trochę dużo ...
Jak myślisz, jaki jest „właściwy” sposób wdrożenia tego rodzaju architektury?
źródło
new
do tworzenia innych modeli widoków, ale myślę o czymś tak prostym jak aplikacja MDI, w której kliknięcie przycisku lub menu „nowego dokumentu” doda nową kartę lub otworzy nowe okno. Powłoka / przewodnik musi być w stanie tworzyć nowe wystąpienia czegoś , nawet jeśli jest ukryty za jedną lub kilkoma warstwami pośredniczącymi.Odpowiedzi:
W rzeczywistości oba te rozwiązania są złe.
Zasadniczo jest to wzorzec lokalizatora usług , który jest anty-wzorcem. Jeśli to zrobisz, nie będziesz już w stanie zrozumieć, na czym tak naprawdę zależy model widoku, nie patrząc na jego prywatną implementację, co bardzo utrudni testowanie lub refaktoryzację.
To nie jest tak bardzo anty-wzór, ale zapach kodu. Zasadniczo tworzysz obiekt parametru , ale celem wzorca refaktoryzacji PO jest zajmowanie się zestawami parametrów, które są używane często i w wielu różnych miejscach , podczas gdy ten parametr byłby użyty tylko raz. Jak wspomniałeś, spowodowałoby to wiele rozdętych kodów bez żadnej realnej korzyści i nie grałoby dobrze z wieloma kontenerami IoC.
W rzeczywistości obie powyższe strategie pomijają ogólny problem, którym jest zbyt duże sprzężenie między modelami widoków a usługami . Po prostu ukrycie tych zależności w lokalizatorze usług lub obiekcie parametru w rzeczywistości nie zmienia liczby innych obiektów, od których zależy model widoku.
Pomyśl, jak przetestowałbyś jeden z tych modeli widoków. Jak duży będzie twój kod instalacyjny? Ile rzeczy trzeba zainicjować, aby działało?
Wiele osób zaczynających od MVVM próbuje stworzyć modele widoków dla całego ekranu , co jest zasadniczo niewłaściwym podejściem. MVVM polega na kompozycji , a ekran z wieloma funkcjami powinien składać się z kilku różnych modeli widoków, z których każdy zależy tylko od jednego lub kilku modeli / usług wewnętrznych. Jeśli muszą się ze sobą komunikować, robisz to za pośrednictwem pub / sub (broker komunikatów, szyna zdarzeń itp.)
To, co naprawdę musisz zrobić, to przefakturować modele widoków, aby miały mniej zależności . Następnie, jeśli potrzebujesz zagregowanego „ekranu”, tworzysz inny model widoku, aby agregować mniejsze modele widoku. Ten zagregowany model widoku sam w sobie nie musi wiele robić, więc z kolei jest dość łatwy do zrozumienia i przetestowania.
Jeśli zrobiłeś to poprawnie, powinno być oczywiste po prostu patrząc na kod, ponieważ będziesz miał krótkie, zwięzłe, specyficzne i testowalne modele widoku.
źródło
Mógłbym napisać o tym książkę ... tak naprawdę jestem;)
Po pierwsze, nie ma uniwersalnego „właściwego” sposobu robienia rzeczy. Musisz wziąć pod uwagę inne czynniki.
Możliwe, że Twoje usługi są zbyt szczegółowe. Lepszym rozwiązaniem może być owijanie Usług fasadami zapewniającymi interfejs, z którego korzystałby konkretny model Viewmodel lub nawet klaster pokrewnych modeli ViewModels.
Jeszcze prostsze byłoby zawinięcie usług w jedną fasadę, z której korzystają wszystkie modele widoków. Oczywiście może to być bardzo duży interfejs z wieloma niepotrzebnymi funkcjami dla przeciętnego scenariusza. Ale powiedziałbym, że nie różni się niczym od routera wiadomości, który obsługuje każdą wiadomość w twoim systemie.
W rzeczywistości to, co widziałem, że wiele architektur ostatecznie ewoluowało, to szyna komunikatów zbudowana wokół czegoś takiego jak wzorzec Event Aggregator. Testowanie jest tam łatwe, ponieważ klasa testowa rejestruje tylko słuchacz w EA i odpala odpowiednie zdarzenie w odpowiedzi. Ale jest to zaawansowany scenariusz, do którego rozwoju potrzeba czasu. Mówię: zacznij od jednoczącej fasady i stamtąd.
źródło
Dlaczego nie połączyć obu?
Stwórz fasadę i umieść wszystkie usługi, z których korzystają twoje modele. Wtedy możesz mieć jedną fasadę dla wszystkich swoich modeli bez złego słowa S.
Lub możesz użyć zastrzyku właściwości zamiast zastrzyku konstruktora. Ale musisz upewnić się, że są one odpowiednio wstrzykiwane.
źródło