Jak mogę zastosować wzorzec MVC do aplikacji C # WinForms?


11

Jestem programistą C ++, który do tej pory używa wzorca MVC do projektowania GUI.

Ostatnio chciałem wrócić do C # i skonfigurowałem aplikację Windows Forms, ale teraz trochę się zagubiłem, jak przenieść ją do struktury zgodnej z MVC.

Obecnie próbuję „zadeklarować” klasę, którą otrzymałem dla WinForm jako Widok, i dodać klasę dla Modelu i Kontrolera w tle. Nie jestem jednak pewien, jak wchodzić w interakcje ze zdarzeniami, takimi jak kliknięcie przycisku. Zwykle przekierowuję te zdarzenia do kontrolera i wykonuję akcję w widoku po zakończeniu.

W tej konstelacji wydaje się to jednak niezadowalające. Na przykład, jeśli chcę zaimplementować przycisk „Wyjdź”, musiałbym przekierować zdarzenie z widoku do kontrolera, a także zaimplementować dodatkową metodę publiczną w moim widoku, którą można wywołać z kontrolera, podczas gdy ja może po prostu wywołać Close () z widoku w pierwszej instancji.

Czy masz dla mnie jakąś radę? Czy moje rozumienie formularzy Windows Forms w języku C # nie jest jeszcze wystarczająco dobre, aby wypróbować implementację MVC? Czy źle przypisuję klasie formularzy? Czy MVC jest po prostu nieodpowiednią architekturą dla tego przypadku użycia?


1
Pytanie OT. Ale dlaczego WInforms? WPF miał zastąpić Winforms i obsługuje MVC (Cóż technicznie MVVM). Chociaż powiem, że krzywa uczenia się WPF może być stroma. (ale jeśli zrobisz to źle, możesz sprawić, że kod WPF będzie wyglądał jak Winforms)
Peter M

1
@PeterM: ponieważ nawet po 10 latach WPF jest do bani i nadal jest wolny.
whatsisname

3
Aby sparafrazować komentarz @ whatsisname, WPF jest nastawiony na większe aplikacje. Mniejsze aplikacje mogą być lepiej obsługiwane w Winforms, ponieważ Winforms jest prawdopodobnie prostszy. Jednak jeśli chcesz przedstawić ten argument, możesz również argumentować, że twoja aplikacja Winforms jest wystarczająco mała, gdzie prawdopodobnie i tak nie potrzebuje MVP. MVVM jest wypiekany do WPF. WPF ma grafikę wektorową, więc nie ma takich samych problemów z rozmiarem jak Winforms. WPF jest bardzo łatwy do skomponowania (możesz łatwo umieścić kontrolki w innych kontrolkach) i ma powiązanie z zabójczymi danymi. Czego nie lubić?
Robert Harvey

3
@whatsisname WPF może ssać, ale wysysa znacznie mniej niż Winforms za coś ponad program zabawkowy
Peter M

2
Powiedziałbym tak w przypadku wewnętrznych programów komputerowych. Ale wiele firm woli aplikacje oparte na przeglądarce do prowadzenia działalności biznesowej, po prostu dlatego, że nie jest wymagana instalacja.
Robert Harvey

Odpowiedzi:


3

Przypadkowo pracuję nad projektem WinForms wzorowanym na MVC. Nie nazwałbym tego idealną odpowiedzią, ale wyjaśnię mój ogólny projekt i mam nadzieję, że pomoże ci to wymyślić własny.

Na podstawie lektury, którą zrobiłem przed rozpoczęciem tego projektu, wydaje się, że nie ma „właściwego” sposobu na wdrożenie tego. Postępowałem zgodnie z prostymi zasadami projektowania OOP i MVC, a reszta była próbą i błędem podczas opracowywania przepływu pracy.

Czy MVC jest po prostu nieodpowiednią architekturą dla tego przypadku użycia?

Nie .. W twoim pytaniu nie ma wystarczającego kontekstu, aby udzielić bezpośredniej odpowiedzi na to pytanie. Dlaczego w ogóle korzystasz z MVC? Jakie są niefunkcjonalne wymagania twojego projektu? Czy twój projekt będzie bardzo obciążony interfejsem użytkownika? Czy zależy Ci bardziej na bezpieczeństwie i wolisz architekturę warstwową? Jakie są główne elementy twojego projektu? Być może każdy element wymaga innego wzorca projektowego. Dowiedz się, dlaczego chcesz skorzystać z tego wzorca projektowego, a możesz odpowiedzieć na własne pytanie;)

Moim powodem użycia MVC: jest to dość prosty wzorzec projektowy, który moim zdaniem należy zrozumieć, a mój projekt opiera się w dużej mierze na interakcji z użytkownikiem. Sposób, w jaki MVC pozwala programistom na rozwiązywanie problemów, jest wystarczający również dla mojej aplikacji. To sprawia, że mój kod dużo bardziej linkujących i sprawdzalne.

Przypuszczam również, że używam bardziej hybrydowego projektu. Zwykle idealna koncepcja przedstawiona w inżynierii oprogramowania nie sprawdza się w praktyce. Możesz zmodyfikować projekt, aby dostosować go do potrzeb twojego projektu. Nie musisz dać się wciągnąć w to, co jest dobre, a co złe. Istnieją ogólne praktyki, ale zasady mogą być zawsze zgięte lub złamane, o ile nie postrzelisz się w stopę.

Moje wdrożenie rozpoczęło się od projektu na wysokim poziomie, który dał mi pojęcie o tym, jakich komponentów będę potrzebować. Najlepiej zacząć w ten sposób i posuwać się w dół po architekturze. Oto schemat pakietu dla projektu (utworzonego w StarUML): wprowadź opis zdjęcia tutaj

Zwróć uwagę, że każda warstwa oprócz warstwy prezentacji zależy od systemu przesyłania wiadomości. Jest to wspólny „język” wykorzystywany przez dolne warstwy i podsystemy tych warstw do komunikowania się ze sobą. W moim przypadku było to proste wyliczenie oparte na operacjach, które można wykonać. Co prowadzi mnie do następnego punktu ...

Pomyśl o operacjach lub poleceniach jako podstawie do wdrożenia. Co chcesz zrobić z aplikacją? Podziel go na najbardziej podstawowe operacje. Na przykład: CreateProject, WriteNotes, SaveProject, LoadProject itp. W GUI (lub klasach Form) wystąpi pewne zdarzenie (np. Naciśnięcie przycisku). Każda operacja ma powiązaną z nią metodę kontrolera. W tym przypadku coś takiego jak Wyjście jest zbyt proste. Aplikację można po prostu zamknąć z klasy Form. Ale załóżmy, że najpierw chciałem zachować niektóre dane aplikacji w pliku? Wywołam metodę „Zapisz” z odpowiedniej klasy kontrolera w ramach mojej metody naciskania przycisków.

Stamtąd kontroler wywoła prawidłowy zestaw operacji z klas usług. Klasy usług w mojej aplikacji działają jako interfejs do warstwy domeny. Sprawdzą poprawność danych wejściowych otrzymanych z wywołania metody kontrolera (a tym samym z GUI) i manipulują modelem danych.

Po zakończeniu sprawdzania poprawności i odpowiedniej manipulacji obiektem metoda usługi zwróci kod komunikatu do kontrolera. Na przykład MessageCodes.SaveSuccess. Zarówno kontroler, jak i klasy usług były oparte na obiektach domeny i / lub ogólnym zestawie operacji, które można grupować razem.

Na przykład: FileMenuController(operacje: NewProject, SaveProject, LoadProject) -> ProjectServices(CreateProject, PersistProjectToFile, LoadProjectFromFile). Gdzie Projectbyłaby klasa domeny w twoim modelu danych. Klasy kontrolera i usługi w moim przypadku były klasami, które nie nadają się do utworzenia z metodami statycznymi.

Następnie sterownik rozpoznaje operację jako zakończoną niepowodzeniem / pomyślnie. Teraz kontroler ma własny system przesyłania wiadomości, którego używa do interakcji z warstwą prezentacji, stąd podwójna zależność między warstwą usługi a warstwą prezentacji. W takim przypadku klasa wywoływana ViewStatew ViewModelspakiecie jest zawsze zwracana do GUI przez kontroler. Ten stan zawiera takie informacje, jak: „ czy stan, w którym próbujesz ustawić poprawną aplikację? ”, „ Czytelny dla człowieka komunikat o operacji, którą próbujesz wykonać i dlaczego zakończyła się ona niepowodzeniem (komunikaty o błędach) ” oraz ViewModelklasa.

ViewModelKlasa zawiera odpowiednie dane z warstwy domeny GUI będzie użyć do aktualizacji widoku. Te modele widoków wyglądają jak klasy domenowe, ale w moim przypadku użyłem bardzo chudych obiektów. Zasadniczo nie zachowują się właściwie, po prostu przekazują informacje o stanie niższego poziomu aplikacji. Innymi słowy, NIGDY nie zamierzam oddawać moich klas domen do warstwy prezentacji. Z tego też powodu pakiety Controllersi Servicesdzielą warstwę usługową na dwie części. Kontrolery nigdy nie będą obsługiwać klas domen ani sprawdzać ich stanu. Działają one po prostu jako granica przekształcająca dane istotne z GUI w dane istotne dla domeny, z których mogą korzystać usługi i odwrotnie. Włączenie logiki serwisowej do sterownika doprowadziłoby do bardzo grubego kontrolery, które są trudniejsze w utrzymaniu.

Mam nadzieję, że daje to punkt wyjścia.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.