Hej, jeśli chodzi o kontroler z wzorca MVC, no to powinien być on jak najgłupszy i wszystkie zadania delegować do serwisów. A co z ViewModelem z MVVM? Czy tutaj sytuacja jest analogiczna? Czy może wręcz przeciwnie i ViewModel może robić sporo więcej?
W MVVM Viewmodel to taki "wirtualny' widok więc sam widok powinien być jeszcze głupszy.
Teoretycznie ViewModel powinien wypluwać coś z czym bindujesz tylko faktyczne kontrolki/komponenty - taki trochę plik .dfm z Delphi albo projekty formatek z Cpp.
Oczywiście wszystko zależy do jakiego poziomu abstrakcji wyciągasz to pojęcie.
A co z komendami? Przecież one są częścią właśnie ViewModelu.
Juhas napisał(a):
Hej, jeśli chodzi o kontroler z wzorca MVC, no to powinien być on jak najgłupszy i wszystkie zadania delegować do serwisów.
Kontroler raczej powinien delegować zadania do modelu, w takim klasycznym MVC.
Juhas napisał(a):
Hej, jeśli chodzi o kontroler z wzorca MVC, no to powinien być on jak najgłupszy i wszystkie zadania delegować do serwisów. A co z ViewModelem z MVVM? Czy tutaj sytuacja jest analogiczna? Czy może wręcz przeciwnie i ViewModel może robić sporo więcej?
ViewModel jest mniej więcej odpowiednikiem kontrolera, i odpowiada za logikę prezentacji. Od kontrolera różni się generalnie obsługą eventów i trzymaniem stanu.
https://docs.microsoft.com/en-us/previous-versions/msp-n-p/gg405484(v=pandp.40)
@somekind: Czyli obsługa wszystkich komend powinna odbywać się w innym miejscu?
Dokładnie o to chcę zapytać. Czy komenda może być wykonywana w ViewModelu, czy powinna iść przez inną warstwę, np. zapis dokumentu. Załóżmy, że ta metoda jest uruchamiana przez komendę:
bool CanSaveDocument()
{
return ActiveDocument != null;
}
async Task SaveDocument()
{
if(ActiveDocument == null)
return;
UIAction(() => ShowProgress());
await db.Save(ActiveDocument);
UIAction(() => HideProgress());
}
ShowProgress/HideProgress to po prostu metody, które w jakiś sposób mówią widokowi, że ma pokazać progress.
Dla ułatwienia dałem uproszczony kod, ale widać, że ViewModel ma zależność od jakiegoś repozytorium i może tutaj wykonywać inne operacje. Czy może lepszym jest:
async Task SaveDocument()
{
UIAction(() => ShowProgress());
await documentService.Save(ActiveDocument);
UIAction(() => HideProgress());
}
Tutaj ViewModel nie ma nic wspólnego z warstwą bazodanową. Odwołuje się tylko do jakiegoś serwisu (ewentualnie polecenia - w sensie wzorca behawioralnego).
A jeśli mamy zapisać wszystkie otwarte dokumenty? To też pytanie jak powinno to być wg sztuki. Tak:
async Task SaveAllDocuments()
{
UIAction(() => ShowProgress());
await documentService.SaveAll(OpenedDocuments);
UIAction(() => HideProgress());
}
czy tak:
async Task SaveDocument()
{
UIAction(() => ShowProgress());
foreach(var doc in OpenedDocuments)
await documentService.Save(doc); //czy może też zależne od repozytorium
UIAction(() => HideProgress());
}
Z MVVM nie pracowałem za wiele, ale im mniej zależności i im głupszy tym lepiej - łatwiej też testować. Testowalność ma kluczowe znaczenie przy wyborze struktury kodu. A przynajmniej powinna :-).
Zapis do bazy to nie logika prezentacji i nie należy do ViewModelu. W Twoim przykładzie serwis to jest taka przelotka do która woła repozytorium. Przy takim przykładzie nie widać sensu stosowania osobnej warstwy. Jeśli jednak zamiast wołania samego "await db.Save(ActiveDocument);" umieścisz tam kilka(naście) linii kodu zaczyna to nabierać sensu i wyraźnie widać że repozytorium powinno NIE być tutaj dostępne, a być częścią czegoś "większego" np SaveService (robienie klas typu DocumentService gdzie jest 5 metod często "brzydko pachnie").
Dodam, że moim zdaniem samo " if(ActiveDocument == null) return;" może tam sobie zostać jako część logiki prezentacji, zwłąszcza że masz CanSave metodę która np może decydować czy w ogóle przycisk jest aktywny/widoczny.
Co do pytania drugiego SaveAll vs Foreach - ja bym powiedział zrób tak, żeby było wygodnie i sensownie. To też może być wygodniej umieścić w osobnej warstwie jeśli np chcesz tam jakoś obsłużyć scenariusz, gdy tylko część z dokumentów się uda zapisać. I co wtedy? No to łatwiej to testować też poza ViewModelem.
@Juhas: no jak dla mnie Twoje oba kody się specjalnie nie różnią. W obu ViewModel woła Model do wykonania jakiejś tam operacji. A jak wygląda architektura modelu, to jest inna sprawa. Mogą być serwisy, mogą być handlery poleceń.
Jeszcze jedno pytanie. Co sądzicie o umieszczaniu ViewModelu wewnątrz innego ViewModelu?
Jest MainViewModel, który posiada DocumentListViewModel. MainViewModel to po prostu ViewModel dla głównego okna aplikacji, w którym mamy podstawowe operacje, jak również widzimy listę dokumentów. Możemy utworzyć nowy dokument itd.
Moim zdaniem to złe podejście i ViewModel nie powinien używać innych ViewModeli w sobie. Z drugiej strony może to być "wygodne". Jak uważacie?
OK, z tego co czytam, to wszędzie właściwie piszą, że to normalne, że w jednym ViewModelu znajduje się inny.
VM nie jest głupi, to on skleja interaktory i daje komendy do V, odbiera akcje z V.
Zarejestruj się i dołącz do największej społeczności programistów w Polsce.
Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.