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):

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.