2013-05-02 11 views
28

Po zrobieniu kilka projektów z wykorzystaniem wzorca MVVM, Im nadal zmaga się z rolą ViewModel:MVVM: ViewModel i Business Logic Connection

co zrobiłem w przeszłości: Korzystanie Modelu tylko jako pojemnik Danych . Wprowadzanie logiki do manipulowania danymi w ViewModel. (To prawda w logice biznesowej?) Con: Logika nie nadaje się do ponownego użycia.

Co próbuję teraz: Utrzymywanie modelu ViewModel tak cienkiego, jak to możliwe. Przenoszenie całej logiki do warstwy modelu. Tylko zachowanie logiki prezentacji w ViewModel. Con: Sprawia, że ​​powiadomienia UI są bolesne, jeśli dane zostaną zmienione w warstwie modelu.

Więc dam wam przykład, aby uczynić go bardziej przejrzyste:

Scenariusz: Narzędzie do zmiany nazwy plików. Klasy: Plik: Reprezentowanie każdego pliku; Reguła: Zawiera logikę jak zmienić nazwę pliku;

Jeśli jestem po podejście 1: Tworzenie ViewModel dla pliku, reguły i widoku -> RenamerViewModel. Umieszczenie całej logiki w RenamerViewModel: Zawiera listę FileViewModel i RuleViewModel oraz logikę postępowania. Łatwy i szybki, ale nie nadaje się do ponownego użycia.

Jeśli będę postępować zgodnie z podejściem 2: Tworzenie nowej Klasy Modelu -> Zmień nazwę, która zawiera Listę Plików, Regułę i Kontynuującą Logikę, aby interweniować nad każdym plikiem i zastosować każdą Regułę. Tworzenie widoku dla pliku, reguły i zmiany nazwy. Teraz tylko RenamerViewModel zawiera instancję Modelu Renamer oraz dwie ObservableCollections do zawijania Listy Plików i Reguł Renamer. Ale cała Logika jest w Modelu Renamer. Tak więc, jeśli Model Renamer jest uruchamiany w celu manipulowania niektórymi wywołaniami danych przez metody, ViewModel nie ma pojęcia, które dane są manipulowane. Ponieważ model nie zawiera żadnych powiadomień PropertyChange i uniknę tego. Logika biznesowa i prezentacji jest oddzielona, ​​ale to utrudnia powiadomienie interfejsu użytkownika.

Odpowiedz

34

Umieszczenie logiki biznesowej wewnątrz viewmodel jest bardzo złym sposobem robienia rzeczy, więc zamierzam szybko powiedzieć nigdy nie robić tego i przejść do omawiania drugiej opcji.

Umieszczenie logiki wewnątrz modelu jest znacznie rozsądniejsze i jest to dobre podejście początkowe. Jakie są wady?Twoje pytanie mówi

Więc jeśli Renamer model jest wyzwalany manipulować niektóre dane o Metodzie połączeń, ViewModel nie ma pojęcia, która jest manipulowana danych. Ponieważ Model nie zawiera żadnych powiadomień PropertyChange i będę go unikać.

Cóż, sprawienie, że model Twojego urządzenia INotifyPropertyChanged z pewnością pozwoliłby ci przejść do lepszych rzeczy. Jednak prawdą jest, że czasami nie jest to możliwe - na przykład model może być klasą częściową, w której właściwości są generowane automatycznie przez narzędzie i nie wywołują powiadomień o zmianach. To niefortunne, ale nie koniec świata.

Jeśli chcesz coś kupić, to ktoś musi zapłacić za nie;; jeśli nie jest to model, który daje takich powiadomień potem pozostaje ci tylko dwie możliwości:

  1. ViewModel wie, jakie operacje na modelu (ewentualnie) zmiany powodują i aktualizuje swój stan po każdej takiej operacji.
  2. Ktoś wie, które operacje powodują zmiany, i powiadamia o tym model viewmodel, aby zaktualizował swój stan po zmianie modelu owijania.

Pierwsza opcja jest znowu złym pomysłem, ponieważ w efekcie wraca do "logiki biznesowej" wewnątrz modelu widoku. Nie tak źle jak umieszczenie logiki biznesowej w viewmodelu, ale wciąż.

Druga opcja jest bardziej obiecujący (i niestety więcej pracy do wykonania): część

  • Put od logiki biznesowej w osobnej klasy (a „usługa”). Usługa wdroży wszystkie operacje biznesowe, które chcesz wykonać, odpowiednio do pracy z instancjami modelu.
  • Oznacza to, że usługa wie, kiedy właściwości modelu mogą ulec zmianie (jest to OK: model + usługa == logika biznesowa).
  • Usługa dostarczy powiadomień o zmienionych modelach wszystkim zainteresowanym stronom; Twoje viewmodels będą uzależnione od usługi i otrzymają powiadomienia (dzięki temu będą wiedzieć, kiedy "ich" model został zaktualizowany).
  • Ponieważ operacje biznesowe są również realizowane przez usługę, jest to bardzo naturalne (np. Gdy polecenie jest wywoływane w viewmodel, reakcja wywołuje odpowiednią metodę w usłudze, pamiętaj, że sam viewmodel nie wie o firmie logika).

Aby uzyskać więcej informacji na temat wdrożenia, zobacz również moje odpowiedzi here i here.

+1

Powiadomienie jest zawsze opisywane jako część ViewModel, dlatego chciałbym tego uniknąć w Modelu. Wydaje się, że robisz to samo dwa razy. – JDeuker

+1

@JD .: Oczywiście, ale to albo to, albo wdrażanie usług. Twoja decyzja. – Jon

+0

@Jon: +1 dla "nigdy tego nie rób". Deweloperzy przyzwyczajeni do modelu N-tieru zapominają, że można dodawać odniesienia do bibliotek WPF do VM, aby przekazać złożone obiekty wymagające kompozycji, takie jak FlowDocument. –

10

Oba podejścia są prawidłowe, ale istnieje trzecie podejście: wdrożenie usługi między modelem a warstwami maszyny wirtualnej. Jeśli chcesz, aby twoje modele były głupie, usługa może dostarczyć agnostycznego pośrednika, który może egzekwować reguły biznesowe w sposób wielokrotnego użytku.

ponieważ model robi Zawierają Każde zawiadomienie PropertyChange i będę uniknąć

Czemu unikając tym? Nie zrozumcie mnie źle, staram się, aby moje modele były tak głupie, jak to tylko możliwe, ale wdrażanie powiadomienia o zmianach w modelu może czasami być przydatne, a w zależności od tego, kiedy to robisz, bierzesz pod uwagę tylko System.ComponentModel. Jest całkowicie agnostykiem UI.

+0

Czy usługa korzysta z modeli lub instancji ViewModel? – JDeuker

+0

@ JD .: model - nie ma zależności od warstwy widoku od warstwy usługi. Możesz myśleć o warstwie usługi jako rozszerzającej i chroniącej integralność warstwy modelu. –

0

Można także zaimplementować IDataErrorInfo zarówno na Model i ViewModel, ale wykonując sprawdzanie poprawności tylko w modelu, ułatwi to wdrażanie reguł biznesowych tylko w modelu ...

Ex:

ViewModel:

... 

private Person person; 

... 

string IDataErrorInfo.this[string propertyName] 
{ 
    get 
    { 
     string error = (person as IDataErrorInfo)[propertyName]; 
     return error; 
    } 
} 

Model:

public class Person:INotifyPropertyChanged,IDataErrorInfo 
{ 

... 

    string IDataErrorInfo.this[string propertyName] 
    { 
     get { return this.GetValidationError(propertyName); } 
    } 

... 

    string GetValidationError(string propertyName) 
    { 
     if(propertyName == "PersonName") 
      //do the validation here returning the string error 
    } 
} 

Plus spojrzeć na wzór MVCVM, im właściwie go używać i to jest całkiem w porządku do abstrakt biznes logika do klasy kontrolera zamiast modelu lub modelu widoku

1

Wykonuję następujące czynności

  1. Widok z XAML widzenia logiki tylko

  2. ViewModel który obsługuje kliknięć ładowarki i tworzenia nowych modeli widoku. Obsługuje routowane zdarzenia itp.

  3. Model będący moim kontenerem danych i logiką biznesową związaną z weryfikacją danych modelu.

  4. Usługi, które zapełniają model danymi. Na przykład wywołaj serwer WWW, wczytaj go z dysku, zapisz na dysku itp. W zależności od tego przykładu często mój model i usługa implementują IPropertyChanged. Lub mogą zamiast tego korzystać z programów obsługi zdarzeń.

Dowolna złożona aplikacja imo potrzebuje kolejnej warstwy. Nazywam to model + usługa, widok, viewmodel. Usługa abstrakcyjna logika biznesowa i pobiera instancję modelu jako zależność lub tworzy model.