W swojej książce „Czysta architektura” wujek Bob mówi, że prezenter powinien umieścić otrzymane dane w czymś, co nazywa „modelem widoku”.
Czy to to samo, co „ViewModel” z wzorca projektowego Model-View-ViewModel (MVVM), czy jest to prosty obiekt do przesyłania danych (DTO)?
Jeśli to nie prosta DTO, jak to się odnosi do widoku? Czy widok pobiera z niego aktualizacje za pośrednictwem relacji Obserwator?
Domyślam się, że bardziej przypomina ViewModel z MVVM, ponieważ w rozdziale 23 swojej książki Robert Martin mówi:
Zadaniem [Prezentera] jest przyjęcie danych z aplikacji i sformatowanie ich do prezentacji, aby widok mógł po prostu przenieść je na ekran. Na przykład, jeśli aplikacja chce, aby data była wyświetlana w polu, przekaże Prezenterowi obiekt Data. Następnie Prezenter sformatuje te dane w odpowiedni ciąg i umieści je w prostej strukturze danych o nazwie Model widoku, w której widok może je znaleźć.
Oznacza to, że widok jest w jakiś sposób połączony z ViewModel, w przeciwieństwie do zwykłego odbierania go jako argumentu funkcji (na przykład w przypadku DTO).
Innym powodem, dla którego tak myślę, jest to, że jeśli spojrzysz na obraz, Prezenter używa Modelu widoku, ale nie widoku. Podczas gdy Prezenter używa zarówno granicy wyjściowej, jak i danych wyjściowych DTO.
Jeśli nie jest to ani DTO, ani ViewModel firmy MVVM, wyjaśnij, co to jest.
źródło
ViewModel
jest nakładką naController
,Presenter
iViewModel
w czystej architektury wuja Boba.Controller
->ICommand
iPresenter
->data-binding mechanism
.Odpowiedzi:
Nie.
To byłoby tak :
To ma cykle. Wujek Bob starannie unikał cykli .
Zamiast tego masz to:
Który z pewnością nie ma cykli. Ale zastanawiasz się, skąd widok wie o aktualizacji. Za chwilę do tego dojdziemy.
Aby zacytować Boba z poprzedniej strony:
Tak więc, jeśli chcesz.
Ale mocno podejrzewam, że to, co cię naprawdę denerwuje, to :
To urocze małe nadużycie UML kontrastuje kierunek zależności kodu źródłowego z kierunkiem przepływu kontroli. Tutaj można znaleźć odpowiedź na twoje pytanie.
W relacji za pomocą:
przepływ kontroli idzie w tym samym kierunku co zależność kodu źródłowego.
W relacji wykonawczej:
przepływ kontroli zwykle idzie w przeciwnym kierunku niż w zależności od kodu źródłowego.
Co oznacza, że naprawdę na to patrzysz:
Powinieneś być w stanie zobaczyć, że przepływ kontroli nigdy nie dostanie się od Prezentera do Widoku.
Jak to możliwe? Co to znaczy?
Oznacza to, że widok ma własny wątek (co nie jest takie niezwykłe) lub (jak wskazuje @Euphoric) przepływ kontroli pojawia się w widoku z czegoś, co nie zostało tutaj przedstawione.
Jeśli jest to ten sam wątek, widok będzie wiedział, kiedy model widoku jest gotowy do odczytu. Ale jeśli tak jest, a widok jest graficznym interfejsem użytkownika, trudno będzie mu odmalować ekran, gdy użytkownik przesunie go, czekając na DB.
Jeśli widok ma własny wątek, wówczas ma własny przepływ kontroli. Oznacza to, że aby to zaimplementować, widok będzie musiał sondować model widoku, aby zauważyć zmiany.
Ponieważ Prezenter nie wie, że Widok istnieje, a Widok nie wie, że Prezenter istnieje, nie mogą do siebie dzwonić. Nie mogą rzucać na siebie wydarzeniami. Wszystko, co może się zdarzyć, to Prezenter zapisze w View-Model, a View przeczyta View-Model. Kiedy tylko masz na to ochotę.
Zgodnie z tym diagramem jedyną rzeczą, którą widok i prezenter udostępniają, jest znajomość modelu widoku. I to tylko struktura danych. Więc nie oczekuj, że będzie się zachowywał.
Może się to wydawać niemożliwe, ale można sprawić, że zadziała, nawet jeśli model widoku jest złożony. Jedno małe zaktualizowane pole to wszystko, co widok musiałby sondować, aby wykryć zmianę.
Teraz możesz oczywiście nalegać na użycie wzorca obserwatora lub sprawić, by jakieś ramowe rzeczy ukryły przed tobą ten problem, ale proszę zrozum, że nie musisz.
Oto trochę zabawy, którą miałem ilustrując przepływ kontroli:
Zauważ, że ilekroć widzisz przepływ idący wbrew wskazanym wcześniej kierunkom, to, co widzisz, powraca. Ta sztuczka nie pomoże nam dostać się do Widoku. No chyba, że najpierw wrócimy do czegoś, co nazywamy Kontrolerem. Lub możesz po prostu zmienić projekt , aby uzyskać dostęp do widoku. To także naprawia coś, co wygląda jak początek problemu jo-jo z Data Access i jego interfejsem.
Jedyną inną rzeczą do nauczenia się tutaj jest to, że Interoperator Przypadków Użytkowych może właściwie wywoływać rzeczy w dowolnej kolejności, o ile chce wywołać prezentera jako ostatni.
źródło
Uważam ten problem za zbyt mylący i zajęłoby dużo tekstu i czasu, aby poprawnie wyjaśnić problem, ponieważ uważam, że źle rozumiesz zarówno czystą architekturę Martina, jak i MVVM.
Pierwszą rzeczą, na którą należy zwrócić uwagę, jest to, że zamieszczony schemat jest niekompletny. Pokazuje tylko „logikę biznesową”, ale brakuje jakiegoś „orkiestratora”, który faktycznie sprawia, że części poruszają się we właściwej kolejności.
Kod orkiestratora byłby tak prosty jak
Wydaje mi się, że słyszałem, jak Martin mówił o tym w jednym ze swoich wystąpień na temat czystej architektury.
Inną rzeczą do podkreślenia jest to, że uwaga candied_orange na temat braku cykli jest błędna. Tak, cyklicznie nie ma (i nie powinno) w architekturze kodu. Ale cykle między instancjami wykonawczymi są powszechne i często prowadzą do prostszego projektu.
Tak jest w przypadku MVVM. W MVVM View zależy od ViewModel, a ViewModel używa zdarzeń do powiadamiania View o swoich zmianach. Oznacza to, że przy projektowaniu klas istnieje tylko zależność od klas View do Model, ale podczas działania występuje cykliczna zależność między instancjami View i ViewModel. Z tego powodu program Orchestrator nie jest potrzebny, ponieważ ViewModel zapewni View sposób, aby dowiedzieć się, kiedy sam się zaktualizować. Dlatego „powiadomienia” na tym schemacie używają linii „squigly”, a nie linii bezpośredniej. Oznacza to, że View obserwuje zmiany w ViewModel, nie że ViewModel zależy od View.
Najważniejszą rzeczą, którą powinieneś wziąć z czystej architektury Martina, nie jest sam projekt, ale sposób radzenia sobie z zależnościami. Jednym z krytycznych punktów, które porusza w swoich przemówieniach, jest to, że gdy istnieje granica, wówczas wszystkie zależności kodu przekraczające tę granicę przekraczają ją w jednym kierunku. Na schemacie granica ta jest reprezentowana przez podwójną linię. I jest wiele zależności inwersji poprzez interfejsy (
InputBoundary
,OutputBoundary
iDataAccessInterface
), które ustala kierunek zależność kod.Natomiast
ViewModel
w Czystej architekturze jest po prostu DTO bez logiki. Widać to po<DS>
tagach. I to jest powód, dla któregoorchestrator
jest to konieczne, ponieważView
nie będzie wiedział, kiedy uruchomić jego logikę.Gdybym miał „spłaszczyć” diagram tak, jak będzie on wyglądał w czasie wykonywania, będzie wyglądać tak:
Tak więc w czasie wykonywania zależności są w „złym” kierunku, ale to jest w porządku.
Polecam obejrzeć jego wykład o czystej architekturze, aby lepiej zrozumieć jego rozumowanie.
źródło