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 Project
był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 ViewState
w ViewModels
pakiecie 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 ViewModel
klasa.
ViewModel
Klasa 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 Controllers
i Services
dzielą 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.