Jak skonfigurować MVP dla rozwiązania Winforms?

14

W przeszłości korzystałem z MVP i MVC i wolę MVP, ponieważ moim zdaniem znacznie lepiej kontroluje przebieg wykonywania.

Stworzyłem swoją infrastrukturę (klasy magazynu danych / repozytorium) i używam ich bez problemu podczas twardego kodowania przykładowych danych, więc teraz przechodzę do GUI i przygotowuję MVP.

Sekcja A

  1. Widziałem MVP używającego widoku jako punktu wejścia, to znaczy w metodzie konstruktora widoków tworzy on prezentera, który z kolei tworzy model, łącząc zdarzenia w razie potrzeby.

  2. Widziałem również prezentera jako punkt wejściowy, w którym tworzony jest widok, model i prezenter. Następnie ten prezenter otrzymuje widok i model obiektu w swoim konstruktorze w celu połączenia zdarzeń.

  3. Jak w 2, ale model nie jest przekazywany do prezentera. Zamiast tego model jest statyczną klasą, w której wywoływane są metody, a odpowiedzi zwracane bezpośrednio.

Sekcja B

Widziałem, jeśli chodzi o synchronizację widoku i modelu.

  1. Ilekroć zmienia się wartość w widoku, tj. TextChangedZdarzenie w .Net / C #. To uruchamia model, DataChangedEventktóry jest przekazywany do modelu, aby cały czas był zsynchronizowany. A gdy model się zmienia, tj. Zdarzenie w tle, którego słucha, widok jest aktualizowany za pomocą tego samego pomysłu wywołania a DataChangedEvent. Gdy użytkownik chce zatwierdzić zmiany SaveEvent, uruchamia się, przechodząc do modelu, aby zapisać. W takim przypadku model naśladuje dane widoku i przetwarza działania.

  2. Podobne do # b1, jednak widok nie jest zsynchronizowany z modelem przez cały czas. Zamiast tego, gdy użytkownik chce zatwierdzić zmiany, SaveEventzostaje zwolniony, a prezenter pobiera najnowsze szczegóły i przekazuje je do modelu. w tym przypadku model nie wie o danych widoków, dopóki nie będzie musiał na nim działać, w którym to przypadku zostaną przekazane wszystkie potrzebne szczegóły.

Sekcja C

Wyświetlanie obiektów biznesowych w widoku, tj. Obiektu (MyClass), a nie pierwotnych danych (int, double)

  1. Widok ma pola właściwości dla wszystkich swoich danych, które będą wyświetlane jako obiekty domeny / biznesu. Na przykład view.Animalsujawnia IEnumerable<IAnimal>właściwość, nawet jeśli widok przetwarza je na węzły w TreeView. Następnie dla wybranego zwierzęcia odsłoniłby się SelectedAnimaljako IAnimalwłasność.

  2. Widok nie ma wiedzy o obiektach domeny, udostępnia właściwości tylko dla typów obiektów zawartych w elementach pierwotnych / ramowych (.Net / Java). W tym przypadku prezenter przekazuje obiekt adaptera obiekt domeny, adapter następnie przetłumaczy dany obiekt biznesowy na formanty widoczne w widoku. W tym przypadku adapter musi mieć dostęp do rzeczywistych elementów sterujących w widoku, a nie tylko do dowolnego widoku, dlatego zostaje ściślej sprzężony.

Sekcja D

Wiele widoków użytych do utworzenia jednego elementu sterującego. tzn. masz złożony widok z prostym modelem, takim jak zapisywanie obiektów różnych typów. Możesz mieć system menu z boku przy każdym kliknięciu elementu, wyświetlane są odpowiednie elementy sterujące.

  1. Tworzysz jeden ogromny widok, który zawiera wszystkie poszczególne elementy sterujące, które są widoczne przez interfejs widoków.

  2. Masz kilka widoków. Masz jeden widok na menu i pusty panel. Ten widok tworzy inne wymagane widoki, ale ich nie wyświetla (widoczny = fałsz), ten widok implementuje również interfejs dla każdego widoku, który zawiera (tj. Widoki potomne), dzięki czemu może być udostępniany jednemu prezenterowi. Pusty panel jest wypełniony innymi widokami ( Controls.Add(myview)) i ( (myview.visible = true). Zdarzenia wywołane w tych „podrzędnych” widokach są obsługiwane przez widok nadrzędny, który z kolei przekazuje zdarzenie prezenterowi i odwrotnie w celu dostarczenia zdarzeń z powrotem do elementów potomnych.

  3. Każdy widok, niezależnie od tego, czy jest to główny widok rodzica, czy mniejszy widok dziecka, jest połączony z własnym prezenterem i modelem. Możesz dosłownie po prostu upuścić kontrolkę widoku do istniejącej postaci, a ona będzie mieć gotową funkcjonalność, wystarczy podłączyć do prezentera za kulisami.

Sekcja E

Jeśli wszystko ma interfejs, teraz oparte na sposobie wykonania MVP w powyższych przykładach wpłynie na tę odpowiedź, ponieważ mogą one nie być kompatybilne krzyżowo.

  1. Wszystko ma interfejs, widok, prezentera i model. Każdy z nich ma oczywiście konkretne wdrożenie. Nawet jeśli masz tylko jeden konkretny widok, model i prezentera.

  2. Widok i model mają interfejs. Dzięki temu widoki i modele mogą się różnić. Prezenter tworzy / otrzymuje widok i modeluje obiekty i służy tylko do przekazywania komunikatów między nimi.

  3. Tylko widok ma interfejs. Model ma metody statyczne i nie jest tworzony, dlatego nie ma potrzeby używania interfejsu. Jeśli chcesz mieć inny model, prezenter wywołuje inny zestaw metod klasy statycznej. Będąc statycznym Model nie ma linku do prezentera.

Osobiste myśli

Ze wszystkich przedstawionych przeze mnie wariantów (większość, których prawdopodobnie użyłem w jakiejś formie), których jestem pewien, że jest ich więcej. Wolę A3 jako logikę biznesową wielokrotnego użytku poza MVP, B2 dla mniejszego powielania danych i mniejszej liczby zdarzeń. C1 za brak dodawania w innej klasie, na pewno umieszcza w widoku niewielką ilość logiki, która nie może być testowana jednostkowo (sposób wizualizacji obiektu domeny), ale można to sprawdzić w kodzie lub po prostu wyświetlić w aplikacji. Gdyby logika była złożona, zgodziłbym się na klasę adaptera, ale nie we wszystkich przypadkach. W przypadku sekcji D wydaje mi się, że D1 tworzy widok, który jest zbyt duży przynajmniej na przykład w menu. Używałem wcześniej D2 i D3. Problem z D2 polega na tym, że w końcu musisz napisać dużo kodu, aby przekierować zdarzenia do i z prezentera do właściwego widoku potomnego i nie jest on zgodny z funkcją przeciągnij / upuść, każdy nowy element sterujący wymaga dodatkowego okablowania, aby obsługiwać jednego prezentera. D3 jest moim preferowanym wyborem, ale dodaje jeszcze więcej klas jako prezenterów i modeli do obsługi widoku, nawet jeśli widok jest bardzo prosty lub nie trzeba go ponownie wykorzystywać. myślę, że mieszanka D2 i D3 najlepiej opiera się na okolicznościach. Jeśli chodzi o sekcję E, myślę, że wszystko, co ma interfejs, może być przesadzone. Już to robię dla obiektów domenowych / biznesowych i często nie widzę w tym „przewagi”, ale pomaga to w wyśmiewaniu obiektów w testach. Osobiście widziałbym E2 jako klasyczne rozwiązanie, chociaż widziałem E3 zastosowane w 2 projektach, nad którymi wcześniej pracowałem. myślę, że mieszanka D2 i D3 najlepiej opiera się na okolicznościach. Jeśli chodzi o sekcję E, myślę, że wszystko, co ma interfejs, może być przesadą. Już to robię dla obiektów domenowych / biznesowych i często nie widzę w tym „przewagi”, ale pomaga to w wyśmiewaniu obiektów w testach. Osobiście widziałbym E2 jako klasyczne rozwiązanie, chociaż widziałem E3 zastosowane w 2 projektach, nad którymi wcześniej pracowałem. myślę, że mieszanka D2 i D3 najlepiej opiera się na okolicznościach. Jeśli chodzi o sekcję E, myślę, że wszystko, co ma interfejs, może być przesadzone. Już to robię dla obiektów domenowych / biznesowych i często nie widzę żadnej korzyści w „projektowaniu”, ale pomaga to w wyśmiewaniu obiektów w testach. Osobiście widziałbym E2 jako klasyczne rozwiązanie, chociaż widziałem E3 zastosowane w 2 projektach, nad którymi wcześniej pracowałem.

Pytanie

Czy poprawnie wdrażam MVP? Czy istnieje odpowiedni sposób, aby to zrobić?

Przeczytałem pracę Martina Fowlera, która ma różne wersje, i pamiętam, kiedy po raz pierwszy zacząłem tworzyć MVC, zrozumiałem koncepcję, ale początkowo nie mogłem ustalić, gdzie jest punkt wejścia, wszystko ma swoją funkcję, ale to, co kontroluje i tworzy oryginał zestaw obiektów MVC.

JonWillis
źródło
2
Powodem, dla którego zadaję to pytanie, jest to, że staram się to zrobić przy prawie pierwszej próbie. Wolę mieć standardowy MVP do naśladowania, niż tworzyć 6 aplikacji przy użyciu różnych odmian dla tego samego wzorca.
JonWillis
Idealnie ... Chciałem o to zapytać przez długi czas
Król

Odpowiedzi:

4

Wiele z tego, co tu prezentujesz, jest bardzo rozsądne i solidne. Niektóre wybory będą zależeć od specyfiki aplikacji i tego, który „wydaje się” odpowiedni. Jak zwykle przez większość czasu, nie będzie jednej właściwej odpowiedzi. Niektóre z tych wyborów będą miały sens tutaj i mogą być całkowicie niewłaściwe dla następnej aplikacji i okoliczności. Nie znając niektórych szczegółów aplikacji, myślę, że jesteś na dobrej drodze i podjąłeś rozsądne, przemyślane decyzje.

Uważam, że Prezenter prawie zawsze powinien być punktem wyjścia. Posiadanie interfejsu użytkownika jako punktu wejścia powoduje, że interfejs użytkownika jest zbyt logiczny i odbiera możliwość zastąpienia nowego interfejsu użytkownika bez dużych zmian w kodowaniu. I tak naprawdę jest to praca prezentera.

Walter
źródło
Być może pytanie, które powinienem zadać, to po prostu niewłaściwy sposób użycia MVP. Zgadzam się z wariantami odpowiadającymi różnym scenariuszom, ale uważam, że powinno być ogólnie przyjęte podejście. Przykłady MVP są prostymi przykładami i prawie wszystkie z tą samą potrzebą, aby edytować obiekt domeny i zapisać zmiany. Jednak z tych przykładów mających ten sam cel powstały powyższe warianty. Właśnie zakodowałem część aplikacji przy użyciu zdarzeń uruchomionych w widoku, które mają być obsługiwane w prezencie, ale mogłem mieć widok zawierający odniesienie do prezentera i metody bezpośredniego wywoływania.
JonWillis
Zgadzam się z całym wysiłkiem na rzecz uproszczenia widoku, aby uzasadnić, że nie jest on testowany jednostkowo, wtedy prezenter powinien mieć kontrolę. Moją jedyną troską jest to (myślenie w mojej głowie, więc może się mylić), że powiedzmy, że winforms / usercontrols może wymagać połączenia z elementami nadrzędnymi i zastanawiałem się, czy potrzebna jest dodatkowa logika w prezenterie, aby to napisać.
JonWillis
@Jon - sposoby MVP są nieprawidłowe? W mojej książce byłoby tak, gdy widok wie o prezencie. To może nie być „złe” w tym sensie, że działa, ale po prostu nie byłoby MVP, w tym momencie przekształciło się w coś innego. Problem z wzorami projektowymi polega na tym, że przykłady są zawsze tak czyste i proste, jak to możliwe. Potem, kiedy wdrażasz je po raz pierwszy, a rzeczywisty świat podskakuje i mówi „hej, prawdziwe aplikacje są znacznie bardziej skomplikowane niż ten chudy przykład”. Tam zaczyna się twój wzrost. Znajdź, co działa dla Ciebie i okoliczności aplikacji.
Walter
dzięki za radę. Pamiętam, że uczyłem się MVC na uniwersytecie. Brzmiało świetnie, ale z pustym płótnem zastanawiasz się, od czego zacząć i jak to naprawdę działa. Pod względem błędności MVP mam na myśli, że którekolwiek z pomysłów / wariantów MVC opublikowałem powyżej niewłaściwego sposobu, tj. Kiedy widok wie, jak działa prezenter. A może widok powinien mieć odniesienie do interfejsu prezentera lub konkretnego typu itp. Tylko wiele różnych odmian, które mogą działać w tym samym celu.
JonWillis
1
@Jon - Twoje odmiany są w zasadzie zgodne z duchem MVP. Jeśli chodzi o pracę z interfejsami lub konkretnymi typami, będzie to zależeć od okoliczności aplikacji. Jeśli aplikacja jest dość mała, niezbyt skomplikowana, być może dodanie interfejsów nie jest konieczne. Lubię, aby wszystko było tak proste, jak to możliwe, dopóki nie stanie się jasne, że aplikacja absolutnie musi implementować architekturę X. Zobacz tę odpowiedź, aby uzyskać więcej szczegółów: programmers.stackexchange.com/questions/34547/…
Walter
4

W naszej aplikacji WinForm .NET 2.0 używamy zmodyfikowanej formy MVP. Dwa elementy, których nam brakowało, to zmodyfikowany adapter WPF ViewModel i dodanie powiązania danych. Naszym specyficznym wzorem jest MVPVM.

Niemal w każdym przypadku podłączamy się jako prezenter, z wyjątkiem niestandardowych elementów sterowania użytkownika, które są okablowane jako pierwsze dla wygody projektanta. Używamy wstrzykiwania zależności, generowanych kodów ViewModels, BDD dla prezenterów i TDD / TED dla modelu.

Maszyny wirtualne są po prostu masywnym, płaskim zwitkiem właściwości, które podnoszą PropertyChanged po ich zmianie. Bardzo łatwo było je wygenerować za pomocą kodu (i powiązanych testów jednostkowych ćwiczeń). Używamy ich do odczytywania i zapisywania w kontrolach interaktywnych przez użytkownika oraz do kontrolowania statusów włączonych. ViewModel jest połączony z View, ponieważ używamy wiązania danych do cholernie blisko wszystkiego innego.

Widok będzie czasem zawierał metody umożliwiające osiągnięcie rzeczy, których maszyna wirtualna nie może. Zwykle kontroluje to widoczność przedmiotów (WinForm może być wybredny) i rzeczy, które nie chcą być danymi. Widok zawsze ujawnia zdarzenia takie jak „Logowanie” lub „Uruchom ponownie”, z odpowiednimi EventArgs do działania na zachowaniach użytkowników. O ile nie musieliśmy używać hacka typu „View.ShowLoginBox”, widok jest całkowicie wymienny, o ile spełnia ogólne wymagania projektowe.

Wzięcie tego wzoru zajęło nam około 6-8 miesięcy. Ma wiele elementów, ale jest bardzo elastyczny i niezwykle wydajny. Nasza konkretna implementacja jest bardzo asynchroniczna i sterowana zdarzeniami, co może być jedynie artefaktem innych wymagań, a nie efektem ubocznym zachowania projektowego. Na przykład dodałem synchronizację wątków do klasy podstawowej, z której dziedziczy nasza maszyna wirtualna (która po prostu ujawniła metodę OnPropertyChanged w celu wywołania zdarzenia) - i poof, teraz możemy mieć wielowątkowe prezentery i modele.

Bryan Boettcher
źródło
Wiem, że Twój MVPVM może być interesem komercyjnym, ale jeśli jest w porządku, czy możesz podać kod przykładowy? Na marginesie, gdzie narysujesz linię tego, co robi model, a co prezentera. Widziałem, że prezenter jest tak prosty, że obsługuje zdarzenia przeglądania i po prostu wywołuje model, do prezentera uzyskującego dostęp do warstw danych w celu przekazania obiektów biznesowych do modelu, aż do zastąpienia modelu przez prezentera.
JonWillis
Tak, pozwól mi dokończyć przykład. Zostanie on nieco dostosowany do naszej działalności, ponieważ używam go jako wewnętrznego narzędzia szkoleniowego.
Bryan Boettcher
Dziękuję,
przejrzę
@insta - link nie działa, czy mógłbyś go gdzieś ponownie załadować?
Yves Schelpe,
1
Bzdura Właśnie usunąłem go tydzień temu. Liczby :(
Bryan Boettcher
2

Korzystam z wersji PureMvc, która została zmodyfikowana dla .Net, a następnie rozszerzona przeze mnie.

Przyzwyczaiłem się do PureMvc z używania go w aplikacjach Flex. Jest to szkielet typu gołe kości, więc można go łatwo dostosować, jeśli chcesz go dostosować.

Zabrałem ze sobą następujące swobody:

  • Dzięki refleksji udało mi się uzyskać prywatne właściwości w mediatorze widoku, aby przejść do klasy form i pobrać (prywatne) odwołanie do każdej kontrolki, w której pośredniczyłem.
  • Za pomocą odbicia mogę automatycznie połączyć elementy sterujące z podpisami zdarzeń w moim mediatorze, które są ogólne, więc po prostu włączam parametr nadawcy.
  • Zwykle PureMvc chce, aby pochodne mediatory zdefiniowały swoje zainteresowania powiadomieniami w funkcji, która zwróci tablicę ciągów. Ponieważ moje zainteresowania są w większości statyczne i chciałem mieć łatwiejszy sposób, aby zobaczyć zainteresowania mediatorów, zrobiłem modyfikację, aby mediator mógł również zadeklarować swoje zainteresowania poprzez zestaw zmiennych składowych o określonej sygnaturze: _ _ _ <nazwa klasy> _ <nazwa-wiadomienia>. W ten sposób mogę zobaczyć, co się dzieje przy użyciu drzewa członków IDE zamiast zaglądania do funkcji.

W PureMvc można użyć polecenia jako punktu wejścia, typowe polecenie Start skonfiguruje Model w takim zakresie, w jakim będzie to możliwe, a następnie utworzy formularz główny, utworzy i zarejestruje mediator dla tego formularza, a następnie wykona aplikację. Na formularzu.

Mediator formularza byłby odpowiedzialny za ustanowienie wszystkich sub-mediatorów, niektóre z nich można zautomatyzować ponownie za pomocą sztuczek refleksyjnych.

System, którego używam, jest zgodny z metodą przeciągnij / upuść, jeśli rozumiem twoje znaczenie. Rzeczywista forma jest tworzona w VS, ale moje doświadczenie dotyczy tylko formularzy, które mają statycznie utworzone formanty. Rzeczy takie jak dynamicznie tworzone pozycje menu wydają się wykonalne przy odrobinie ulepszenia mediatora dla tego menu lub podmenu. Owłosienie byłoby wtedy, gdy mediator nie miał statycznego elementu głównego, który mógłby zostać zaczepiony, a ty zacząłeś tworzyć dynamiczne mediatory „instancji”.

znak
źródło